467 Commits

Author SHA1 Message Date
moneyl
493b80095d Chem dispenser attempts to eject into hands (#576)
Previously the chem dispenser eject button placed the container on top of the dispenser. Now it will attempt to place it in your active hand, then your secondary hand. If both hands are full then it'll place it on top of the dispenser like before.
2020-01-29 19:15:35 +01:00
ike709
73a9b2af89 Basic equipment for all jobs (#573) 2020-01-29 19:14:35 +01:00
AJCM-git
b167107c8b /TG/ ID sprites (#570)
*  This adds /TG/ ID sprites

* Applying suggestions

Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
2020-01-29 19:13:53 +01:00
moneyl
972d601664 Add solution pouring / click-transfer (#574)
* Add click-based solution transfer

For example, clicking on a beaker with a soda can to transfer the soda to the beaker. Works on plain solution containers like beakers and also on open drink containers like soda cans as long as they have the `PourIn` and `PourOut` solution  capabilities. If no `SolutionComponent` is added to a drink entity, the `DrinkComponent` will give the entity one. This PR extends that behavior slightly by also giving these default `SolutionComponent`'s the proper capabilities for pouring in/out.

* Improve fix for poured drinks not immediately disappearing

Instead of making `DrinkComponent.Use` public this 
 splits out the code important to both users and made that function public, leaving `Use` private.

* Shorten solution transfer popup

* Make code review changes

- Move pouring code from SolutionComponent to new PourableComponent. Added PourableComponent to client ignore list and added to existing container prototypes.
- Added EmptyVolume property to shared SolutionComponent for convenience.
- Removed DrinkComponent fix from pouring AttackBy code. Instead DrinkComponent subscribes to the SolutionChanged action and updates its self when necessary.
- Fixed pouring being able to add more than a containers max volume and sometimes deleting reagents.
- Added message for when a container is full.

* More code review changes

- Remove IAttackBy ComponentReference attribute in PourableComponent
- Remove _transferAmount from shared SolutionComponent. Left over var from previous commit not being used anymore.
2020-01-29 02:07:02 +01:00
Pieter-Jan Briers
7bc40ab13d Add test asserting reconnect works. 2020-01-26 23:53:48 +01:00
Pieter-Jan Briers
5243530d81 Fix crash on reconnect. 2020-01-26 23:53:33 +01:00
Pieter-Jan Briers
e3108261ab Update submodule. 2020-01-26 23:53:10 +01:00
Pieter-Jan Briers
883c465a14 Fix crash on human deletion.
Fixes #568
2020-01-26 19:52:09 +01:00
Pieter-Jan Briers
1d98152953 Fix grabbing things out of storage items doing an attack sometimes. 2020-01-26 16:17:24 +01:00
Tad Hardesty
a6f8ee317f Fix parallax on resolutions greater than 1920x1080 (#564) 2020-01-26 14:02:12 +01:00
ike709
4315618782 Adds some existing equipment to Janitors and Sec Officers (#566) 2020-01-26 14:01:48 +01:00
ShadowCommander
b71f39cfb4 Fix PickUp verb showing up for entities in containers (#567) 2020-01-26 14:00:59 +01:00
Pieter-Jan Briers
2ec493e2af Combat mode improvements.
You now need to hold the mouse for 0.15 seconds in combat mode to actually do an attack.

Otherwise it's regular item interaction (for now).
2020-01-26 03:38:51 +01:00
Pieter-Jan Briers
7e43d574d8 Fix opening context menus for world entities. 2020-01-25 23:35:03 +01:00
Pieter-Jan Briers
01a0a376e3 Update submodule 2020-01-25 23:17:19 +01:00
Acruid
59500e5278 Raycast API changes. 2020-01-25 13:55:29 -08:00
Pieter-Jan Briers
5390a9f375 Update submodule 2020-01-25 22:41:35 +01:00
Pieter-Jan Briers
1412cd5277 Combat mode is now on R. 2020-01-25 21:33:59 +01:00
Pieter-Jan Briers
ca57749a3b Remove bad use of GetAssemblyByName. 2020-01-25 20:39:39 +01:00
Pieter-Jan Briers
6706ff23ce Update submodule. 2020-01-25 20:39:24 +01:00
Pieter-Jan Briers
daf3c28929 Use PopupContainer for verbs & examine popups.
Fixes #501
2020-01-25 17:28:39 +01:00
Pieter-Jan Briers
821058740f Update submodule. 2020-01-25 17:27:38 +01:00
Pieter-Jan Briers
fbe7533d4b Run database migrations in parallel with the rest of game startup to improve load times. 2020-01-25 16:16:48 +01:00
DamianX
4a833e82cd Update character list when one of them is selected (#561) 2020-01-25 14:37:04 +01:00
Acruid
a692899f5b GridCoordinates API changes. 2020-01-25 01:39:14 -08:00
Acruid
a86363a6d2 API changes, renamed SpawnEntityAt to SpawnEntity. 2020-01-24 16:10:48 -08:00
Acruid
4ab7f1dcb3 Removed the StateType property from every component. This field was completely unused except for a debug assertion. 2020-01-24 14:10:36 -08:00
DamianX
0f1cee44a3 Fixed sqlite migrations (#558) 2020-01-24 22:03:42 +01:00
Pieter-Jan Briers
d16fe5376d Fix postgres on the public server. 2020-01-24 20:48:24 +01:00
DamianX
514d05b237 Added postgres support (#556) 2020-01-24 17:25:01 +01:00
Pieter-Jan Briers
f95c5b7921 Adds job icons to the job list. 2020-01-24 16:31:18 +01:00
Pieter-Jan Briers
1dd4a5b48b Update submodule 2020-01-24 13:46:46 +01:00
Tad Hardesty
7bf06a59d4 Expend cables when placing them (#547) 2020-01-24 02:52:44 +01:00
Pieter-Jan Briers
fd759e4a9d Jackboots and clown shoes produce different footsteps again. 2020-01-24 02:35:01 +01:00
Pieter-Jan Briers
11d47cc67a Remove an unused field. 2020-01-24 00:57:08 +01:00
DamianX
46ce6bf45e Implemented random character creation (#548)
* Implemented random character creation

* Pick from a list and apply a bit of randomness instead

* Rename SetInitialData, unselect list entries properly
2020-01-24 00:56:26 +01:00
Pieter-Jan Briers
5af5a02e31 Fixes ICharacterUI scene controls getting disposed when CharacterInterface is removed.
Fixes #550
2020-01-24 00:54:17 +01:00
DamianX
664acb140e Normalize wander AI's direction vector (#549) 2020-01-23 22:53:07 +01:00
Víctor Aguilera Puerto
09a27df6db Makes players spawn on job spawn markers on roundstart (#543)
* Adds job type to spawn points, makes players spawn on job spawn markers on roundstart

* Update Content.Server/GameObjects/Components/Markers/SpawnPointComponent.cs

Co-Authored-By: Pieter-Jan Briers <pieterjan.briers@gmail.com>

Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
2020-01-23 17:31:47 +01:00
moneyl
1996893a26 Fix UseDelayComponent (#546)
Currently if this component is present the delay timer is started but it doesn't actually prevent use interactions.

To test this bug, do the following:
1. Set the delay for [bike_horn](https://github.com/space-wizards/space-station-14/blob/master/Resources/Prototypes/Entities/items/bike_horn.yml#L25) to something large like 5.0 that'll make the effect obvious
2. Use the bike horn with the z key. The delay anim will play properly but you'll still be able to spam the honk.
2020-01-23 01:37:07 +01:00
moneyl
86d1f808af Fix more unknown component errors (#545)
Fixed UseDelayComponent not being on the ignore list of Content.Client and removed seemingly erroneous `Timing` component used in bike_horn.yml
2020-01-23 01:09:56 +01:00
Pieter-Jan Briers
aaa4329d8c Update submodule for windows build fixes. 2020-01-23 00:45:49 +01:00
Pieter-Jan Briers
5a5e8f0e31 Fixes notify messages causing an exception in UI update.
Fixes #539
2020-01-22 23:59:41 +01:00
Víctor Aguilera Puerto
83b2e59910 Fix bug where placing items in PlaceableSurfaces didn't cause a… (#544)
* Change variable name in some interactions

I definitely didn't copy-paste some stuff back then, absolutely not.

* Fix bug where dropping items in tables didn't cause the drop interaction
2020-01-22 23:09:36 +01:00
L.E.D
8f04ce894f Use Delay Component (#540)
* use delay timer

* remove accidental using

* and remove accidental newline because i don't proofread my code

* compatibility with HUD cooldown
suggested changes

* get out of here

* suggested changes

* change to seconds from milliseconds

* remove redundancy
2020-01-22 23:08:14 +01:00
Pieter-Jan Briers
2260d19364 Update submodule.
Way to inflate the commit count.
2020-01-22 20:24:59 +01:00
Pieter-Jan Briers
90409b0b3d Update submodule. 2020-01-22 20:23:39 +01:00
Pieter-Jan Briers
559367ee55 Content.{Client,Server} are now Exes that can be ran. 2020-01-22 20:17:32 +01:00
Pieter-Jan Briers
cab3688890 Update submodule. 2020-01-22 20:16:56 +01:00
Pieter-Jan Briers
3294634d24 Add MSBuild files to solution.
For convenience for me, mostly.
2020-01-22 20:15:04 +01:00
Pieter-Jan Briers
36cf1c3179 Use shutil.copy2 instead of shutil.copyfile for git_helper.py
Probably fixes #541
2020-01-22 17:09:39 +01:00
moneyl
930fb331db Fix crash / debug assertion failure from explosions (#535)
When explosions are spawned they grab all entities in range and interact with them. They don't check if entities are deleted before doing so which can cause a crash. 

To reproduce, place several grenades on the ground, pick one up and detonate it on top of the others so they detonate as well. The debug assertion in ContainerHelpers.IsInContainer will fail due to one of the accessed entities having been deleted.

This fixes that by ignoring deleted entities when making explosions.
2020-01-21 18:13:57 +01:00
moneyl
eb7f592154 Add more client/server only components to registerIgnore lists (#534)
Fixes a few warnings from components not added to the ignore list on client/server.
2020-01-21 18:12:50 +01:00
moneyl
aa77b017e8 Add keybind for line edit delete key usage (#533)
Separate commit on the engine repo has the code for this behavior.
2020-01-21 18:12:36 +01:00
Pieter-Jan Briers
09900a08e4 Wait uhhh don't punch a hole in that toolbox. 2020-01-21 18:12:04 +01:00
Pieter-Jan Briers
9beb7e48d4 Implement female uniform masking. 2020-01-21 18:11:15 +01:00
Pieter-Jan Briers
5481959018 Update submodule. 2020-01-21 18:10:52 +01:00
Pieter-Jan Briers
32fae60930 Make slot buttons in character setup less wide.
Most normal names will fit easily.
2020-01-21 03:37:27 +01:00
Pieter-Jan Briers
d564d3bc39 Fix a compiler warning. 2020-01-21 02:07:25 +01:00
Pieter-Jan Briers
6537aead64 Use MapCoordinates.Nullspace 2020-01-20 22:26:28 +01:00
Pieter-Jan Briers
2848da0b8e Update submodule. 2020-01-20 22:26:10 +01:00
Pieter-Jan Briers
923c5698b5 Use dummy game ticker for LoadSaveTicksSaveStationStation. 2020-01-20 22:19:24 +01:00
Pieter-Jan Briers
8d3bccbd56 Adds HumanInventoryUniformSlotsTest integration tests. 2020-01-20 22:19:24 +01:00
Pieter-Jan Briers
44b2b1b958 Update submodule. 2020-01-20 22:19:24 +01:00
Pieter-Jan Briers
eadb661515 Integration tests improvements:
1. Added dummy game ticker for future tests to reduce startup time of test. (no loading a map)
2. Re-organized tests a bit.
2020-01-20 22:14:44 +01:00
Pieter-Jan Briers
42066fc8a1 Actually use InventoryComponent.CanEquip from Equip. 2020-01-20 22:13:47 +01:00
Pieter-Jan Briers
ba88b2b1da Update NuGet dependencies.
EFCore excluded because I'm still using .NET Core Runtime 3.1.0 instead of 3.1.1
2020-01-20 20:44:36 +01:00
Pieter-Jan Briers
f550ba67aa Update submodule. 2020-01-20 20:41:07 +01:00
Pieter-Jan Briers
bdc637d3af Remove unused field to fix compiler warning. 2020-01-20 20:33:24 +01:00
Pieter-Jan Briers
73693b88f6 Don't accidentally commit your testing cvar change. 2020-01-20 18:48:54 +01:00
Pieter-Jan Briers
77fcc4a673 Use TG human sprites. 2020-01-20 15:13:03 +01:00
Pieter-Jan Briers
54f5f0ac08 Fill out captain & clown equipment. 2020-01-20 10:30:07 +01:00
Pieter-Jan Briers
425b85d5a7 Fix spawning of non-clothing items via starting gear. 2020-01-20 10:30:07 +01:00
Pieter-Jan Briers
f6fe9ce85c Show job title in character setup. 2020-01-20 10:30:06 +01:00
Pieter-Jan Briers
75aa9541e0 Equip clothing to preview dummies in the lobby. 2020-01-20 10:30:06 +01:00
Pieter-Jan Briers
03bfb22559 Fix profile editor not correctly enforcing single high-priority job. 2020-01-20 10:30:06 +01:00
Pieter-Jan Briers
05ff4e0956 Implement human pockets and ID card slot correctly.
Pockets & ID need uniform equipped. Pockets accept any sufficiently small item.
2020-01-20 10:30:06 +01:00
Pieter-Jan Briers
959bf7c477 Clean up EquipmentSlotDefinitions.cs a bit. 2020-01-20 10:30:06 +01:00
Acruid
7c562af0aa Nullspace map entity is created every time the client joins the server. This fixes #520. 2020-01-20 00:36:34 -08:00
ShadowCommander
57c3f63a26 Fix HandsGui StatusPanel capturing input (#525) 2020-01-20 01:31:46 +01:00
DamianX
f08455073a Unset ready status when opening character setup (#522)
* Close character setup when joining the game

* Unready when opening character setup instead
2020-01-20 00:15:39 +01:00
Pieter-Jan Briers
812654fe32 Add VSCode extension recommendations 2020-01-19 19:13:34 +01:00
Pieter-Jan Briers
aaf0b7a645 Game ticker now has a job assignment system. 2020-01-19 19:08:35 +01:00
Pieter-Jan Briers
e64d80d02e Update submodule. 2020-01-19 19:07:20 +01:00
Pieter-Jan Briers
ac9d236955 Acc 2020-01-19 18:33:30 +01:00
Pieter-Jan Briers
fc2d53eb4f Adds preference unavailable setting to profiles. 2020-01-19 18:33:22 +01:00
Pieter-Jan Briers
77367345b6 Declare overflow job (assistant) with constants. 2020-01-19 18:32:24 +01:00
Pieter-Jan Briers
02fbc5938b Adds more metadata to job prototypes:
Whether the job is a head.
The access levels the job has.
The total & spawn positions count.
2020-01-19 18:31:14 +01:00
Pieter-Jan Briers
ce794c4dac Move access levels to prototypes.
Also adds captain & ID computer access levels.
Fixes ID computer not saving access changes correctly.
2020-01-19 18:30:09 +01:00
Pieter-Jan Briers
edf280e2df Improve ID card names to be better.
And more inline with SS13.
2020-01-19 18:27:16 +01:00
DamianX
1bd17f73b1 Actually save when pressing "save and close" (#523) 2020-01-19 14:54:11 +01:00
Pieter-Jan Briers
511741d11a Update submodule. 2020-01-19 09:42:26 +01:00
Pieter-Jan Briers
f86ad6175e Basic implementation of jobs in the character profile. 2020-01-19 09:42:26 +01:00
Pieter-Jan Briers
afef34a648 Split jobs into separate files and remove less-important jobs. 2020-01-19 09:35:17 +01:00
DamianX
802fda3cfd Added option to generate random character name (#519)
* Added option to generate random character name

* awk '!seen[$0]++'
2020-01-18 23:09:14 +01:00
DamianX
b5feb0db2a Fixed character setup save button (#518) 2020-01-18 16:57:11 +01:00
Pieter-Jan Briers
4b60c03688 Fix Sex not showing in character preview panel. 2020-01-18 04:38:56 +01:00
ShadowCommander
e0aaab56e3 Implement StorageButton on HandsGUI and click empty hand to swap… (#517)
Also moved duplicate sprite view code to ItemSlotManager
2020-01-18 03:41:47 +01:00
Pieter-Jan Briers
9a76c70b37 Give TextCursorRight canRepeat: true 2020-01-18 03:17:52 +01:00
DamianX
e619b3026c HOME and END keys work in LineEdit episode 2 (#512) 2020-01-18 03:14:02 +01:00
Pieter-Jan Briers
54b7d3f229 Commit EF migrations so the game starts again. 2020-01-18 03:13:08 +01:00
Pieter-Jan Briers
a4b0c4e213 Update submodule. 2020-01-18 02:48:21 +01:00
Pieter-Jan Briers
47f33e002d Fix package_release_build.py to work with the new EFCore databases. 2020-01-18 02:48:08 +01:00
DamianX
a4e369e629 added Character Setup (#511)
* added Character Setup

* whoops

* reverted unrelated changes

* Made everything work post-rebase

* Removed unused PreferencesChanged event

* nope, don't need this

* HumanoidProfileEditorPanel -> HumanoidProfileEditor

* Set initial data for hair pickers

* Fixed nullable warning

* Renamed LooksComponent -> HumanoidAppearanceComponent

* Renamed LooksComponentState -> HumanoidAppearanceComponentState

* Final renaming maybe

* Use a human-like dummy instead of a real human

* Change preferences structs back to classes
2020-01-18 01:54:13 +01:00
ShadowCommander
d03da83fda Inventory Input (#503)
* Create ItemSlotButton

* Refactor inventory buttons

Refactor so that KeyBind handling of inventory and hands are the same.

* Refactor InventoryInterfaceController to call ItemSlotManager with entities

This change allows HandsGUI and InventoryInterfaceController to just call ItemSlotManager.OnButtonPressed with an entity instead of a slot.

* Add CooldownCircle to ItemSlotButton

This allows Hands and Inventory to have cooldown circles on their ItemSlots.

* Refactor HandsGUI to use ItemSlots

This allows functionality and GUI to be the same between Inventory and Hands.

Added clicking empty hand to switch ActiveHand to clicked hand.

* Implement CooldownCircle in ItemSlotManager

Reorganize files
2020-01-17 15:43:20 +01:00
DamianX
c20ba98a1e Research machinery requires power (#516)
* Fixed ResearchPointSourceComponent properties

* ResearchPointSourceComponent requires power

* ResearchServerComponent requires power
2020-01-17 15:34:40 +01:00
DamianX
c4ea6e53e8 Use EFCore to store preferences (#506)
* Use EFcore to store preferences

* Fixed nullabilty warnings
2020-01-15 15:10:18 +01:00
Pieter-Jan Briers
1856cb079c Fix a couple compiler warnings. 2020-01-15 14:51:01 +01:00
Pieter-Jan Briers
56f1233967 Hair style improvements:
1. made the magic mirror actually reflect your current hair state when you open it.
2. Made magic mirror one window.
3. Use color sliders for magic mirror.
2020-01-15 14:28:46 +01:00
Pieter-Jan Briers
da932c5caa Styling for Slider. 2020-01-15 14:27:47 +01:00
Pieter-Jan Briers
bf6e38703a Update submodule. 2020-01-15 14:27:24 +01:00
Pieter-Jan Briers
b5af7b1c3e Re-organize human layers more so that facial hair is below regular hair. 2020-01-15 14:12:35 +01:00
Pieter-Jan Briers
e0a4735fe2 Switch to TG hair sprites.
This removes the need for a separate shader because they're multiply-based instead of additive like /vg/ or Bay.
2020-01-12 02:04:13 +01:00
Pieter-Jan Briers
9c0a670525 Fix flashlights not updating status when battery inserted. 2020-01-12 02:02:58 +01:00
Pieter-Jan Briers
ca01e245cb Improve human sprite layering.
Matches TG, means hair goes over clothing now.
2020-01-12 02:02:38 +01:00
Pieter-Jan Briers
92da411ea5 Flashlight wattag is now a VV-able variable. 2020-01-12 02:01:00 +01:00
Pieter-Jan Briers
f60b0fce7d Fix hair not syncing style correctly.
Fixes #502
2020-01-12 01:58:05 +01:00
Pieter-Jan Briers
3cceb35445 Fix Stack item status not updating correctly. 2020-01-12 01:58:05 +01:00
Acruid
ca58afd81b Added a Collidable Component to the existing station grind in StationStation. 2020-01-11 15:15:11 -08:00
Acruid
32103979ed CollidableComponent and ICollideableComponent namespace was changed in the engine.
Minor code cleanup.
2020-01-11 14:12:20 -08:00
Acruid
51f7f14c08 Right now ShuttleControllers add the collision component to the Grid Entity.
Adds Grids to the collision group.
2020-01-09 18:29:09 -08:00
Pieter-Jan Briers
0db46da30e Update that submodule. 2020-01-09 00:34:06 +01:00
Pieter-Jan Briers
411c23c46e Item status! 2020-01-09 00:28:04 +01:00
Pieter-Jan Briers
e984fc24b6 Add addcomp command to groups.yml 2020-01-09 00:28:04 +01:00
Pieter-Jan Briers
33782ed7f3 Fix ranged weapon fire rates being stuttery.
Now we just send a fire message to the server every frame. Absolutely terrible!
2020-01-09 00:28:04 +01:00
Pieter-Jan Briers
e31078d8ef Make lmb_empty_alarm.ogg mono audio. 2020-01-09 00:28:04 +01:00
Pieter-Jan Briers
39d99485eb Fix laser weapons always hitting yourself. 2020-01-09 00:28:03 +01:00
Acruid
f73824adcb Prevents the MoverSystem from overwriting the MoverComponent on an entity.
Adds the new ShuttleControllerComponent, a custom IMoverComponent that moves the parent grid when controlled by a mind.
2020-01-08 15:17:00 -08:00
Acruid
8a49546add Added the NoDoor flag to storage components, so that the open/closed state is synced with the lock/unlock.
Added the glorious Pilot Seat.
2020-01-03 17:49:17 -08:00
Acruid
89745202f5 Actors inside storage containers are now centered on the container. 2020-01-03 17:28:16 -08:00
Acruid
d98ce413bb Added a flag to storage containers to allow the contents to be drawn when the container is closed. 2020-01-03 16:00:30 -08:00
Acruid
e2e5982d68 Adds a verb to toggle open/close a storage.
Adds a verb to toggle lock/unlocked a storage.
Adds the ability to lock a storage closed.
2020-01-03 13:38:58 -08:00
Acruid
4773aad40c Added the ID slot of the player inventory to the UI, next to the tool belt. 2019-12-30 11:42:31 -08:00
Acruid
b44ca8df8d Renamed GridCoordinates.Nullspace to GridCoordinates.InvalidGrid, because it has nothing to do with Nullspace anymore. 2019-12-29 18:12:56 -08:00
Pieter-Jan Briers
02a655d005 Update submodule, map file fixed. 2019-12-28 03:24:06 +01:00
Pieter-Jan Briers
1da8e66281 Update submodule. 2019-12-24 15:26:39 +01:00
Pieter-Jan Briers
301cebc254 Dim windows a bit, add rwindows. 2019-12-24 15:14:44 +01:00
Pieter-Jan Briers
26c8995dad Fix low wall layering.
This makes windows look great.
2019-12-24 13:40:13 +01:00
Pieter-Jan Briers
6b99946fd8 Update submodule 2019-12-24 02:52:55 +01:00
Pieter-Jan Briers
4ffaf3fbe6 Try to fix Dapper.dll not getting copied in server builds. 2019-12-24 01:55:52 +01:00
Pieter-Jan Briers
43cb54bb21 Override orientation of handsgui sprites.
Fixes #474
2019-12-24 01:22:20 +01:00
Pieter-Jan Briers
498c248c24 Update submodule 2019-12-24 01:16:28 +01:00
Pieter-Jan Briers
4f3f82f27f Fix cooldown circles not being centered. 2019-12-22 21:50:40 +01:00
Pieter-Jan Briers
0c9a95bc54 Update submodule.
Remove now-unecessary Initialize() calls on added components.
2019-12-22 21:43:34 +01:00
DamianX
f19795edaf Added preferences backend (#465)
* Added preferences backend

* Gender -> Sex

* ClientPreferencesManager properties

* Username validation

* Fixed client init

* WIP db

* Actually working sqlite db

* Dropped shitty sqlite libraries, dropped DbUp, added MigrationManager

* Added profile deletion, test

* Docs, sanity, tests, cleanup

* Cleaned up profile and appearance, fixed running on .net core

Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
2019-12-22 13:47:34 +01:00
Pieter-Jan Briers
c1b9bb348d Fix some Robust.Server namespaces in Robust.Shared. 2019-12-22 13:41:04 +01:00
Acruid
ece6e0a833 Map & Grid Entities (#493)
* Client & Server load with new scene hierarchy.

* Engine Update.
2019-12-22 13:23:38 +01:00
Acruid
c2c512a7e3 Adds base.Initialize() calls to components that were missing them. 2019-12-19 11:35:17 -08:00
Pieter-Jan Briers
690e9dbfe8 remove SS14.Launcher and Robust.Lite. 2019-12-17 16:10:21 +01:00
Pieter-Jan Briers
1e696edcff Use C# 8. 2019-12-17 16:09:10 +01:00
Acruid
1fe09c580c Entities now require a location when being spawned. 2019-12-16 21:44:24 -08:00
Pieter-Jan Briers
12261c5b56 Update submodule 2019-12-16 10:09:28 +01:00
Acruid
72cff220cf Wires are now broken in explosions, and drop cables.
Using the wire cutters on a wire drops a cable.
Fix bug where bullets raise an exception when the hit object deletes itself.
2019-12-15 19:58:24 -08:00
Pieter-Jan Briers
e0cf442041 Update submodule 2019-12-16 01:04:13 +01:00
Pieter-Jan Briers
a9f148c04e Let's pray it all works 2019-12-16 00:46:09 +01:00
Pieter-Jan Briers
a652e39b1c GLFW + .NET Core time, submodule update. 2019-12-15 16:01:46 +01:00
DamianX
79ee241bb7 Fixed client unknown component warnings (#491) 2019-12-15 14:12:33 +01:00
DamianX
63b98f26a6 Fixed a bunch of unused variables warnings (#492) 2019-12-15 14:12:23 +01:00
Pieter-Jan Briers
95fe7fdd25 Reduce sprint movement speed. 2019-12-11 07:23:11 +01:00
Acruid
9c9984a40a EventBus Refactor (#490)
* API changes for the new EventBus.

* Update Engine Module.
2019-12-08 19:52:29 -08:00
Pieter-Jan Briers
7f188b0f44 Disable sprinting.
Now you always sprint.
2019-12-07 17:16:56 +01:00
Pieter-Jan Briers
023c76db59 Make human hitbox square again. 2019-12-06 02:21:59 +01:00
Pieter-Jan Briers
672482194c Update submodule 2019-12-06 02:16:47 +01:00
Pieter-Jan Briers
689d16ee65 Outlines moved to InteractionOutlineComponent, now change color when in interaction range. 2019-12-06 02:08:17 +01:00
Pieter-Jan Briers
a912c999a9 GameScreen moved to content. 2019-12-06 00:41:30 +01:00
Pieter-Jan Briers
26da24c3c5 UI Layout v2. (#489)
* UI Layout v2.

* Lobby fixed.
2019-12-05 16:00:03 +01:00
moneyl
4cf8e18d1f Fix bug with chemical reactions which cause other reactions (#475)
SolutionComponent.CheckForReaction only checks for a reaction once, meaning that if a reaction generates reagents that create a solution that's valid for another reaction, that second reaction doesn't occur. This fixes that by repeatedly checking for a reaction until no more occur.
2019-12-04 13:51:05 +01:00
Pieter-Jan Briers
6df5028d7a Update submodule. 2019-12-04 13:50:06 +01:00
Pieter-Jan Briers
e179e89c03 Fix crash on shutting down client. 2019-12-04 01:23:14 +01:00
Pieter-Jan Briers
a7f86a4333 Clear out some unused sprites. 2019-12-04 01:10:24 +01:00
Pieter-Jan Briers
aea14074cc Make soviet vending machine use cyrillic. 2019-12-04 01:00:15 +01:00
L.E.D
fac91af34d new layers (#483) 2019-12-01 15:20:50 +01:00
Pieter-Jan Briers
7c54a3c923 Fixes window destruction causing a crash.
Fixes #473
2019-11-29 17:08:24 +01:00
moneyl
542428df32 Fixes the player being able to use the chem dispenser when they'… (#480)
* Fixes the player being able to use the chem dispenser when they're dead

Adds a check to `ReagentDispenserComponent.OnUiReceiveMessage()` that checks if the players `SpeciesComponent` has a damage state that prevents them from interacting with the chem dispenser.

* Switch to use ActionBlockSystem instead of checking SpeciesComponent directly
2019-11-29 17:04:36 +01:00
ShadowCommander
adaf0ade52 Fix crash on restart when a Airlock is opening/closing (#481)
* Fix crash when _appearance is null

Added a function to error check and set _appearance.SetData.

Added cancellation token which should stop the Timer, but doesn't seem to right now.

* Fix missing CancellationToken causing a crash

This delay would delay _appearance.SetData until after _apppearance was set to null when the component was removed causing a null reference exception.
2019-11-29 16:53:26 +01:00
Pieter-Jan Briers
4265fac7b8 Welders now play sounds when toggled. 2019-11-29 16:46:25 +01:00
L.E.D
8a90e5d186 Eliminate unnecessary whitespace in examine (#479)
* eliminate unnecessary whitespace in examine

* oops
2019-11-28 14:38:23 +01:00
L.E.D
c213fa8cdf object break sounds (#477) 2019-11-28 14:37:03 +01:00
Peter Wedder
45567c7acc Scrollbar min size. (#464) 2019-11-28 14:28:33 +01:00
Acruid
d549c44f95 Shared IMap/Map class removal (#467)
* Fully removed the Map and IMap class.

* Submodule update to sibling engine commit.
2019-11-26 23:16:36 +01:00
L.E.D
9cfa0d447a Stack popups (#470)
* stack popups

* Update Content.Server/GameObjects/Components/Stack/StackComponent.cs

Co-Authored-By: Pieter-Jan Briers <pieterjan.briers@gmail.com>

* change wording
2019-11-26 20:17:28 +01:00
ShadowCommander
590cb1e85c Fix crash on restart (#463)
Clears inventory slots before disposal when ClientInventoryComponent is removed.
2019-11-26 16:26:47 +01:00
Víctor Aguilera Puerto
fedc0ad71c Adds playable instruments, IDropped, IHandSelected and IHandDese… (#368)
* Instrument test.

* Midi stuff

* Some more work

* This actually works now!

* update

* Midi Audio works!

* Lots of stuff, and cool interfaces for items

* Update

* Fix a few things

* It just works

* Move textures to another folder, remove placeholder description from instruments

* Fix warning

* Use renderer enum

* Instruments now use DisposeRenderer method, and send MidiEvents as they receive them. Deletes InstrumentSystem whoo.

* Fix incorrect sprite paths

* Instruments take midi file size check into account when enabling/disabling midi playback buttons

* Fix crash when pressing drop on empty hand.

* Use new renderer return values for midi/input

* Xylophones are no longer handheld instruments, fix their sprites.

* Use new API

* Remove nfluidsynth from solution

* Timing information

* Use IGameTiming.CurTime for timestamps instead
2019-11-25 00:11:47 +01:00
moneyl
ce54c489eb Add null check before using prototype in ReagentDispenserWindow (#461)
Should fix #460. I'm unable to reproduce it myself, but the stack trace shows the crash occurring on the edited line, likely because `prop` is null. This fixes that by adding a null check before using `prop`.
2019-11-24 00:46:48 +01:00
Ephememory
d63c879404 Crowbar floor tiles and placement (#429)
* Adds tile removing behavior to CrowbarComponent.
Add FloorTileItemComponent.
Add genhit.ogg
Add tile.png for testing

* fixes

* Gives ContentTileDefinition a default value for tile item to drop.
Adds a few more tileitems.

* Changes per review request

* move stack.use and if statement
2019-11-23 22:10:05 +01:00
L.E.D
35f9de3366 Make examination of items in hand possible (#459) 2019-11-23 21:57:44 +01:00
dylanstrategie
17b31c417b Rejunevate now satiates hunger and thirst plus code quality (#456)
* Change Rejunevate to satiate hunger and thirst whenever applicable

* More progress

* One more before rebase

* Shit

* Bop obsolete using

* Verb will not show on non-applicable objects
2019-11-23 21:57:02 +01:00
dylanstrategie
421847e9d3 Add filled toolbelt variant for easy engineering (#458)
* Add filled toolbelt variant for easy engineering

* Petty newline
2019-11-23 21:56:32 +01:00
DamianX
3a7a3a89ba Added hair, facial hair, magic mirror (#452)
* Added hair, facial hair, magic mirror

* I forgot to commit the textures lmao

* Use shader to fix hair color blending
2019-11-23 21:55:46 +01:00
moneyl
b89615342e Fix exception in ReagentPrototype caused by IMetabolizable (#451)
* Fix exception in ReagentPrototype

Due to client trying to access concrete implementations of IMetabolizable that are in Content.Server. This fix checks to see if the prototype is being loaded by the client through reflection. I don't want to move the prototype completely into Content.Server since it's useful for the client to view descriptions and values of reagents without needing to send that data from the server.

* Make fix slightly more explicit

Now it will only load the metabolizable when in Robust.Server, instead of it being anything that's not Robust.Client.

* Add IModuleManager and ModuleManager

Provide simple way to check if shared code is being run by the server or the client

* Change ModuleManager implementations to not require assembly name comparison

Now just has ClientModuleManager registered to client, and ServerModuleManager registered to server.

* Change IModuleManager functions to properties

* Fix failing tests.

This was failing because the tests weren't initializing IoC. Simply using RobustUnitTest wasn't enough because that doesn't initialize content either. I did some cleaning up so now content IoC is registered via ContentUnitTest.
2019-11-23 21:55:31 +01:00
dylanstrategie
4198b6dc4e Medkits are now a container plus filled version (#457) 2019-11-23 21:39:12 +01:00
Remie Richards
0cf34d2d26 Server-side GlobalVerbs now work when used (#455) 2019-11-23 20:11:50 +01:00
Pieter-Jan Briers
d5a9747712 Update submodule 2019-11-23 01:08:39 +01:00
Pieter-Jan Briers
45767d881d Maybe run git pull before updating submodule eh 2019-11-22 19:56:05 +01:00
Pieter-Jan Briers
2d4f1780bf Fix outline shader 2019-11-22 19:55:34 +01:00
Pieter-Jan Briers
ca8609f42c Update submodule 2019-11-22 19:55:34 +01:00
ShadowCommander
1580750606 Implement Cargo Console (#413)
* Implement Cargo Console

Add to CargoConsoleComponent GalacticBank information for syncing Bank Account Balance.

Implement CargoOrderDatabase on the server side and a list of orders in the CargoOrderDatabaseComponent on the client side. This makes it easier to change data on the server side but also utilize the state syncing between components.

Implement GalacticMarketComponent.
Only productIds get sent. Both client and server create their lists from YAML.

Implement basic spawning of items from approved orders in CargoOrderDatabase.

* Finish Cargo Console

Add validation to make sure Order Amount is one or more.

Implement approve and cancel buttons to CargoConsoleMenu orders list row.

Add price to CargoConsoleMenu product list row.

Implement CargoOrderDataManager to consolidate CargoOrder lists.

Refactor CargoOrderDatabaseComponent to use CargoOrderDataManager instead of storing duplicate lists.

Implement canceling orders.
Implement approving orders.

Fix sprite links.

Implement Cargo Request Console.
2019-11-22 01:37:14 +01:00
metalgearsloth
58709d2d26 Add power cell and weapon chargers (#430)
* Add power cell and weapon chargers

Functionality's similar to SS13. The cell charger won't show the discrete charging levels because effort to not make it spam appearance updates over the network.
There's some stuff I wasn't sure on:
1. Updating power? Particularly around fixing rounding
2. Duplicating the full and empty sprites as I couldn't figure out a way to not have AppearanceVisualizer throw if the state isn't found
3. I made a BaseCharger abstract class under the assumption that when a mech / borg charger is added it would also inherit from it.
4. GetText currently isn't localized

* Address PJB's feedback

Updated the BaseCharger
* Change nullref exception to invalidoperation
* If the user doesn't have hands it will just return instead
2019-11-21 23:48:56 +01:00
Ephememory
94c00dda95 Fix various bugs with construction (#446) 2019-11-21 23:46:37 +01:00
Ephememory
0595088409 Fix crates having zero gravity all to themselves (#443) 2019-11-21 23:46:01 +01:00
moneyl
3ab8036363 De-hardcode chemical metabolism of StomachComponent (#437)
* Move chemical reaction effects into Chemistry/ReactionEffects/ subfolder

* Replace hardcoded StomachComponent metabolism with IMetabolizable

The benefits of this approach are that reagent metabolism effects are not hardcoded into StomachComponent, and metabolism effects can be more easily chained together in yaml prototypes, and reagents can have different metabolism rates. One problem with this approach is that getting metabolism rates slower than 1u / second is impossible. Implementing #377 should resolve that problem.

* Fix DefaultFood and DefaultDrink so they remove reagent regardless of Hunger/ThirstComponent presence

Previously if neither of those were present the reagents wouldn't be removed from the stomach. This fixes that.

* Additional comment on function

* Make metabolizer interface implementations explicit

Also removed some unused using statements

* Make StomachComponent._reagentDeltas readonly

* Fix misleading variable names and docs for metabolizables

Changes one of the arguments for `IMetabolizable.Metabolize()` to be called `tickTime` instead of `frameTime` to more accurately reflect it's purpose. It's not really the frametime, but the time since the last metabolism tick. Also updated and expanded the docs to reflect this and to be more clear.
2019-11-21 23:24:19 +01:00
Ephememory
1f177a044d Fix flashlight PointLight radius (#438)
* Add radius to `HandheldLightComponent.cs`

* Revert handheldlightcomponent and simply specify radius to flashlight in yaml.
2019-11-21 15:23:09 +01:00
Pieter-Jan Briers
96b8ded8af Make melee weapons use raycasts. 2019-11-17 21:46:39 +01:00
ancientpower
32bd23f85e fixes power provider range calculation (#442) 2019-11-17 19:56:54 +01:00
ZelteHonor
447db2e458 Basis for the job system (#434)
* Add basic yaml Jobs file

* Add Job Prototype

* Rename Jobs to Job

* Remove BaseJob

* Add the Job class child of Role

* Add code for spawning as an assistant. Not actually working, the job prototype can't be found.

* Fix role instead of job left in yaml

* Add starting gear support for job and the starting gear for assistant as an exemple

* Link job with starting gear in yaml

* Better naming and some error handling

* Tweak error handling
2019-11-17 17:18:39 +01:00
metalgearsloth
480d3b26c4 Make nutrition less harsh (#439)
* Make nutrition less harsh

Also fix the accumulator because why did I put that in the loop.
Decay rates decreased and made drink thirst levels same as hunger for now.

* Also fix stomach frametime accumulation
2019-11-17 02:26:31 +01:00
Acruid
8b1be6edee Fixes the client crashing when closed. When disconnecting from a server, _playerManager.LocalPlayer is set to null before the entity components are disposed. Previously this would throw a nullref exception in ClientStatusEffectsComponent, now it properly checks for the null value. This resolves #435. 2019-11-16 14:39:52 -08:00
ZelteHonor
b2e2aef78d Rider static analysis (#433)
* Non-accessed local variable

* Merge cast and type checks.

* StringComparison.Ordinal added for better culture support

* Supposed code improvement in launcher. Remove unused code.

* Update ExplosionHelper.cs

Unintentional change.

* Optimized Import

* Add Robust.Shared.Utility import where it was deleted

* Other random suggestion

* Improve my comment
2019-11-13 23:37:46 +01:00
ZelteHonor
62b31eee00 Change localize field to be readonly. (#432) 2019-11-12 22:39:18 +01:00
Pieter-Jan Briers
774f5f0db7 I'm bad at coding 2019-11-12 19:37:15 +01:00
Pieter-Jan Briers
f8ff829e27 Merge branches '19-11-12-outline-shader-submodule-update' and 'outline-shader-removal' 2019-11-12 18:52:03 +01:00
Pieter-Jan Briers
34083d3e8d Update submodule 2019-11-12 18:51:03 +01:00
scuffedjays
173329de8f move outline to content 2019-11-12 10:58:41 -06:00
Ephememory
ef2b665ff0 R&D Equipment now require power to operate. (#428)
* Fixes: #392

- Make Lathe require power to interact with or produce.
- Make R&D Console require power to interact with.
- Add PowerDevice component to R&D computer.
- Add PowerDevice component to R&D server.

* Update LatheComponent.cs

* Update LatheComponent.cs

* Update ResearchConsoleComponent.cs
2019-11-12 15:35:34 +01:00
Pieter-Jan Briers
841bb101c5 Visualize melee weapon cooldowns in HUD. 2019-11-12 01:43:11 +01:00
Ephememory
7198a7c78d Add cooldown to MeleeWeaponComponent (#426) 2019-11-11 22:21:29 +01:00
metalgearsloth
de148fc98f Add hunger and thirst (#363)
* Add hunger and thirst

Based on the SS13 systems.
Food (Nutriment) / Drink -> Stomach -> Hunger / Thirst

* Cleanup rebase

* Cleanup stuff that was prototyped

* Address feedback

Still need to add a statuseffects system in a separate branch

* More cleanup on nutrition

Fix Remie's feedback and also damage tick.

* Re-implement nutrition with master

* Updated to use the StatusEffectsUI update
* Removed all clientside components as they only receive the UI updates now
* Implemented PR feedback
* Had to make a slight adjustment to the chemistry SolutionComponent given it doesn't have an Owner, same with Solution

Still TODO:
* Metabolisation effects
* Change drink contents to alcohol / wine etc.
* Add items to the dispensers
* For transparent containers use RecalculateColor

Could probably genericise DrinkFoodContainer as well to be a temporary item dispenser

* Fix broken bottle parent
2019-11-11 22:20:03 +01:00
Pieter-Jan Briers
6de5c01afb Update submodule 2019-11-11 08:36:56 +01:00
Ephememory
0d30bc2676 Add Eris' keyboard sounds to R&D Console. (#427)
* Add Eris' keyboard sounds to R&D Console.

* woah buddy thats not c# standard

* fix weirdly capitalized keyboard method

* thats silly
2019-11-11 00:46:27 +01:00
rok-povsic
e5150d3714 Fix crowbar distance to work on adjacent tiles (#424)
* Fix crowbar distance to work on max 1.5 tiles

* Use existing distance methods

* Use the existing InteractionRange
2019-11-10 21:50:13 +01:00
Ephememory
7032c8a92e Checks if user is in combat mode before allowing firing. (#425) 2019-11-10 10:59:26 +01:00
Pieter-Jan Briers
51359cf77b Update submodule 2019-11-09 13:21:47 +01:00
metalgearsloth
6529542277 Fix overlay not clearing on respawn (#423)
I'm a derp.
2019-11-09 12:09:58 +01:00
metalgearsloth
63611cef80 Port Discordia shoes (#422)
Where no icon was found used the equipped sprites
Also updated the vending machine prototypes but more items will need adding in the future.
2019-11-09 12:09:42 +01:00
metalgearsloth
0b5759abe6 Port Discordia gloves (#419)
Still need descriptions on the items to be done but the groundwork is there.
Could also do with layers for stuff that should light up in the darkness but I wasn't entirely sure what should and shouldn't.
2019-11-06 17:23:35 +01:00
DamianX
1e425f3c28 Basic wrenchable component (#418) 2019-11-06 17:22:55 +01:00
DamianX
4720353fa2 Basic rotatable component (#416)
* Basic rotatable component

* Added counter-clockwise verb

* RegisterIgnore
2019-11-06 17:22:26 +01:00
moneyl
e8679d9308 Add chemistry sprites. Move existing ones into RSIs (#401)
* Add chemistry sprites and move existing ones into RSIs

There are already a few chemistry sprites that are sitting loose in folders. More of them will be needed soon such as pill and other chem machine sprites. This adds RSIs with chemistry sprites from CEV-Eris and moves existing ones into the relevant RSIs.

* Separate machine screens into their own sprites.

Separates screens and machines into their own sprites.
2019-11-03 15:33:23 +01:00
Pieter-Jan Briers
808f35adc6 Update submodule 2019-10-30 21:49:41 +01:00
Víctor Aguilera Puerto
7d307832a0 Adds /me command. (#414)
* Adds /me command.

* Update Content.Server/Chat/ChatManager.cs

Co-Authored-By: Pieter-Jan Briers <pieterjan.briers@gmail.com>
2019-10-30 21:49:07 +01:00
metalgearsloth
12cf5559c2 Refactor SpeciesUI into overlay and status effects (#381)
* Refactor SpeciesUI into overlay and status effects

All components that update the UI will need to use PlayerAttached for cases where the Mind transfers I think.

* Change overlay / status effects to use states

* Change TryRemoveStatus to RemoveStatus

Doesn't return a bool so not trying.
Addressing PJB's feedback.
2019-10-30 16:37:22 +01:00
moneyl
6497cdf8ff Add global verbs (#400)
* Add support for global verbs

These are verbs that are visible for all entities, regardless of their components. This works by adding a new class `GlobalVerb` and a new attribute `GlobalVerbAttribute`. It works in the same way as current verbs, except you can put the verbs class definition anywhere instead of inside a component. Also moved VerbUtility into it's own file since it now has functions for both verbs and global verbs.

* Add view variables verb as an example of global verbs

* Implement suggested changes

Implemented some suggested changes from code review:
- Remove unneeded attribute from `GlobalVerb`
- Added some useful attributes to `GlobalVerbAttribute`
- Moved constants used by both `Verb` and `GlobalVerb` into `VerbUtility`

* Reduce duplicate code in VerbSystem (client & server)

Greatly reduced the amount of duplicate code for handling component verbs and global verbs separately.

* Update engine submodule

Need this so client side permissions checks are available.
2019-10-30 16:31:35 +01:00
Pieter-Jan Briers
e4f3ea7798 You can now walk over dead people. 2019-10-30 16:27:49 +01:00
Pieter-Jan Briers
10ac4418e4 Update submodule 2019-10-25 12:00:55 +02:00
Pieter-Jan Briers
3a0856505d Correctly clear inventory UI when HumanInventoryInterfaceController detaches.
Fixes #405
2019-10-25 11:49:26 +02:00
Swept
8a6751711a Changed the color of buckshot and bullets (#409) 2019-10-24 22:04:35 +02:00
Pieter-Jan Briers
50755a040b Gas mask sprite from Eris. 2019-10-22 23:34:15 +02:00
Pieter-Jan Briers
69796bf1bc Make targeting doll less ugly. 2019-10-22 23:16:12 +02:00
Pieter-Jan Briers
9ac0e02574 Fixed client crashing when destroying certain entities. 2019-10-22 00:07:36 +02:00
DamianX
738fbdd376 Wire colors are now unique (#407) 2019-10-21 23:54:29 +02:00
Pieter-Jan Briers
0e1eb71149 Make research point source turn on/off with power. 2019-10-21 23:46:16 +02:00
Pieter-Jan Briers
f5cbbb5c84 Fix build on Framework. 2019-10-21 23:45:49 +02:00
Pieter-Jan Briers
9a1e4450d8 Make ID console fancier. 2019-10-21 22:54:16 +02:00
Pieter-Jan Briers
563dda69d4 Shorten wires menu by default. 2019-10-21 22:52:58 +02:00
Pieter-Jan Briers
44c9feaebf Fix Leave button in lobby being off to the right. 2019-10-20 22:39:22 +02:00
Pieter-Jan Briers
c457a2603a Document combat mode and change the keybind to Num1.
So I don't CONSTANTLY hit it while alt tabbing.
2019-10-20 22:31:49 +02:00
Pieter-Jan Briers
981c36dbdb Give reagent dispenser reagents window a vertical minimum size. 2019-10-20 01:40:13 +02:00
Pieter-Jan Briers
6630e454c6 Clean up reagent dispenser and make it slightly better. 2019-10-20 01:30:38 +02:00
Pieter-Jan Briers
9c60d4936d Update submodule. 2019-10-20 01:30:06 +02:00
Pieter-Jan Briers
e2511f8ad5 Update submodule 2019-10-18 14:29:23 +02:00
Pieter-Jan Briers
19379decd5 Fancy up the lobby GUI. 2019-10-18 14:28:39 +02:00
Pieter-Jan Briers
743ede2243 Implement more new styling stuff. 2019-10-18 14:28:24 +02:00
Pieter-Jan Briers
0edccd8934 Improve spacing on Escape Menu. 2019-10-18 14:26:45 +02:00
Pieter-Jan Briers
6f704f0320 Move gameticker commands to another file. 2019-10-18 14:25:55 +02:00
Pieter-Jan Briers
62db0573bd Update submodule. 2019-10-18 01:06:10 +02:00
Pieter-Jan Briers
4d5c34bd58 Increase horizontal margins on new buttons. 2019-10-15 13:59:25 +02:00
Pieter-Jan Briers
f0fb3eb434 Fancy new style buttons. 2019-10-15 13:13:21 +02:00
Pieter-Jan Briers
7de97eeb2c Fix icon smoothing not applying after entities are deleted. 2019-10-14 17:09:45 +02:00
Pieter-Jan Briers
def32d80dd Fix walls not smoothing with low walls. 2019-10-14 15:30:37 +02:00
Pieter-Jan Briers
e4bba4cb6f Fix off lights not being offset correctly. 2019-10-14 14:54:27 +02:00
Pieter-Jan Briers
9a38577a18 Improve autolathe & protolathe visuals.
Used correct Eris autolathe sprite.
Gave them an unlit layer.
2019-10-14 09:57:57 +02:00
Pieter-Jan Briers
f3f05b0396 Correctly implement opening animation for airlocks with open maintenance panel. 2019-10-14 00:20:01 +02:00
Pieter-Jan Briers
fd109436e5 Localize & fancify all the examine tooltips with markup. 2019-10-13 22:49:07 +02:00
Pieter-Jan Briers
d629dc449f Update submodule. 2019-10-13 22:48:37 +02:00
Pieter-Jan Briers
5db8cda0b6 Add sound effect to machine panel opening/closing 2019-10-13 19:45:25 +02:00
Pieter-Jan Briers
d113a738de Make Airlock hacking shut power to the entire PowerDevice.
This makes the power device report as "not powered" in the examine tooltip.
2019-10-13 18:29:57 +02:00
Pieter-Jan Briers
bd3fe8f86b Update submodule 2019-10-13 17:13:46 +02:00
Pieter-Jan Briers
445e88cce8 Airlocks do not self close when depowered.
Fixes #390
2019-10-13 17:13:16 +02:00
Pieter-Jan Briers
33e11c0c3a Highlight "not powered" orange in PowerDevice examine.
Also localizes it.
2019-10-13 17:01:53 +02:00
Pieter-Jan Briers
370f4e140d Using crowbar on powered airlock returns true on attackby.
Fixes #388
2019-10-13 16:56:43 +02:00
Pieter-Jan Briers
b556fd0019 Mute lights on base airlock sprites.
They are only visible when unpowered and as such should be muted.
2019-10-13 16:52:26 +02:00
Pieter-Jan Briers
be9dc90738 Disable unlit layers on unpowered airlocks.
Fixes #389
2019-10-13 16:39:21 +02:00
Víctor Aguilera Puerto
8896f46ef3 Fix missing icons (#387) 2019-10-13 16:27:09 +02:00
Pieter-Jan Briers
dcffe0ef04 Add Load, save, run ticks, save test to ensure map is inert. 2019-10-13 16:26:39 +02:00
moneyl
a5b19b10e0 Add rejuvenate command (#380)
* Add rejuvenate command

Takes one or more entity uids as input. Attempts to find a DamageableComponent on that mob and heal all damage on it.

* Add rejuvenate to right click menu

* Update engine submodule

* Make suggested changes

- Remove redundant error checks in rejuvenate console command, add in relevant ones along with shell messages so the user knows what's going on.
- Remove localization of group check on rejuvenate verb, since the translated version wouldn't be in groups.yml this would've broken the verb in other locales.
- Have the rejuvenate verb attempt to heal the user by default if no arguments were provided to it.

* More localization + help message formatting improvement

* Add more suggested changes
2019-10-13 15:30:44 +02:00
Pieter-Jan Briers
d6e378c3bf Update submodule 2019-10-13 01:28:11 +02:00
Pieter-Jan Briers
6893541f9c Add R&D and medbay to the map. 2019-10-13 01:20:33 +02:00
Pieter-Jan Briers
74dd24f39c Add science & medical closets. 2019-10-13 01:19:16 +02:00
Pieter-Jan Briers
3634b17be4 Add science & medbay airlocks. 2019-10-13 00:23:31 +02:00
Pieter-Jan Briers
006341daf7 Increase APC range. 2019-10-13 00:23:07 +02:00
moneyl
427836fec9 Add basic chemical reactions (#376)
* Add basic chemical reaction system

What it adds:
- Reactions defined in yaml with an arbitrary amount of reactants (can be catalysts), products, and effects.

What it doesn't add:
- Temperature dependent reactions
- Metabolism or other medical/health effects

* Add many common SS13 chemicals and reactions

Added many of the common SS13 medicines and other chemicals, and their chemical reactions. Note that many of them are lacking their effects since we don't have medical yet.

* Add ExplosiveReactionEffect

Shows how IReactionEffect can be implemented to have effects that occur with a reaction by adding ExplosionReactionEffect and the potassium + water explosion reaction.

* Move ReactionSystem logic into SolutionComponent

No need for this to be a system currently so the behavior for reaction checking has been moved into SolutionComponent. Now it only checks for reactions when a reagent or solution is added to a solution. Also fixed a bug with SolutionValidReaction incorrectly returning true.

* Move explosion logic out of ExplosiveComponent

Allows you to create explosions without needing to add an ExplosiveComponent to an entity first.

* Add SolutionComponent.SolutionChanged event. Trigger dispenser ui updates with it.

This removes the need for SolutionComponent having a reference to the dispenser it's in. Instead the dispenser subscribes to the event and updates it's UI whenever the event is triggered.

* Add forgotten checks

`SolutionComponent.TryAddReagent` and `SolutionComponent.TryAddSolution` now check to see if `skipReactionCheck` is false before checking for a chemical reaction to avoid unnecessarily checking themselves for a reaction again when `SolutionComponent.PerformReaction` calls either of them.

* Change SolutionComponent.SolutionChanged to an Action

The arguments for event handler have no use here, and any class that can access SolutionChanged can access the SolutionComponent so it can just be an Action with no arguments instead.
2019-10-11 22:57:16 +02:00
metalgearsloth
bd5a4e33ab Port outerclothing from discordia (#382)
Inhand sprites are half-scaled of the sprite
2019-10-11 18:09:34 +02:00
Pieter-Jan Briers
d7360f8709 Fix some compiler warnings. 2019-10-10 15:48:11 +02:00
Pieter-Jan Briers
6249a129c0 Fix verbs for children of the component defining the verb.
This fixes clothing not having a pick up verb, for example.
2019-10-10 12:17:17 +02:00
Pieter-Jan Briers
21612794c5 Update submodule. 2019-10-10 12:15:52 +02:00
Pieter-Jan Briers
fa2d633313 Remove PickUpVerb completely from held items. 2019-10-10 11:53:37 +02:00
Pieter-Jan Briers
09ca46fbd0 Give VerbAttribute [MeansImplicitUse] 2019-10-10 11:51:38 +02:00
Pieter-Jan Briers
cf97ef7ad1 Give flashlights feedback about dead/missing cell. 2019-10-10 11:50:53 +02:00
moneyl
963bb28f0f Reagent dispensers (#360)
* Expose more private values of Solution and SolutionComponent

Expose SolutionComponent.ContainedSolution and Solution.Contents. Both needed by ReagentDispenserComponent.

* Implement IExamine for SolutionComponent

Allows players to see the contents of a solution by examining the entity which contains it.

* Implement ReagentDispenserComponent

Adds ReagentDispenserComponent. A component which can add or remove reagents from a solution container. It's written in a general way so that it can be used for things such as the Chemical dispensers in chemistry, but also the booze and soda dispensers in the bar. 

The chemicals it may dispense are defined in yaml, similar to the way that vending machines define which entities they can dispense, by defining a reagent pack.

* Add chemical dispenser and equipment

Adds the chemical dispenser, beaker, large beaker, dropper, and a few more chemicals.

* Add booze and soda dispensers.

Adds the booze and soda dispensers, and a few chemicals for them to dispense. There's no drink mixing or drunkenness yet.

* Update engine submodule.

* Remove unneeded and commented out code

Had a few WIP notes and debug code bits I forgot to remove beforehand.

* Make SolutionComponent._containedSolution and it's values private again

- Remove `SolutionComponent.ContainedSolution` property, replace with specific access functions to maintain safety.
- Make Solution.Contents return a `ReadOnlyCollection` instead of `_contents` to prevent uncontrolled access to the Solution values.
- Add `SolutionComponent.RemoveAllSolution()`

* Update Content.Shared/Chemistry/Solution.cs

Commits a suggestion from RemieRichards to match the coding style of the rest of the codebase. Using `IReadOnlyList` instead of `IReadOnlyCollection`.

Co-Authored-By: Remie Richards <remierichards@gmail.com>

* Update Content.Shared/GameObjects/Components/Chemistry/SolutionComponent.cs

Commits a suggestion from RemieRichards to match the coding style of the rest of the codebase. Using `IReadOnlyList` instead of `IReadOnlyCollection`.

Co-Authored-By: Remie Richards <remierichards@gmail.com>

* Add import for IReadOnlyList to Shared/SolutionComponent.cs

* Add documentation

* Improve localization

Improve use of ILocalizationManager.

* Resolve ReagentDispenserWindow._localizationManager before using it

Forgot to do this in the last commit, resulting in a crash. Oops.

* Add SolutionCaps.FitsInDispenser. Use in ReagentDispenserComponent.

Used to limit large containers like buckets or mop buckets from being placed in a dispenser. Both have large capacities (500) and weren't designed to hold certain chemicals like a beaker is, so for now they can be blocked from being put in a dispenser by giving them that flag.

* Add colors to new reagents

* Update engine submodule
2019-10-05 15:10:05 +02:00
Pieter-Jan Briers
0cc980b26a Bye Waffle. 2019-10-02 21:47:32 +02:00
Pieter-Jan Briers
a2d8fc1ef9 Sandbox panel 2019-10-02 21:47:32 +02:00
Pieter-Jan Briers
18392610c9 Update submodule 2019-10-02 21:47:32 +02:00
Pieter-Jan Briers
fc13e21e73 Attempt #2 Azure Pipelines 2019-10-01 17:28:57 +02:00
Pieter-Jan Briers
a346eb3e12 Attempt #1 Azure Pipelines
[skip ci]
2019-10-01 17:26:36 +02:00
Acruid
ef99cac28f Update BuildChecker.csproj to .Net Framework v4.7.2. 2019-09-27 15:33:13 -07:00
Pieter-Jan Briers
02d509fc5f Shitty combat mode & animations. (#367)
* Combat mode UI & targeting zones.

* Fix inventory hud positioning.

* Crappy attack animations.

* Import TG combat sounds

* More work on arcs.

* Implement hit sounds.

* Lunging, hit effects, some more stuff.
2019-09-26 22:32:32 +02:00
Pieter-Jan Briers
ac55ccf46e Update submodule 2019-09-26 21:31:38 +02:00
Acruid
bd96190bbc Fixes prototype resource paths for power. The paths were changed in #356, but the yaml was never updated. 2019-09-25 13:03:36 -07:00
Víctor Aguilera Puerto
61e516c4ac Update technologies.yml (#366)
Fixed YAML formatting error.
2019-09-24 17:25:40 -07:00
Swept
66c327805f Added a bunch more textures (#356) 2019-09-24 22:15:26 +02:00
moneyl
31487c1cf1 Disable speaking while unconscious/dead (#362)
* Disable speaking while unconscious/dead

Fixes #359

* Add CanSpeak ActionBlocker

Matches the existing technique for other actions like CanUse() and CanThrow().
2019-09-24 09:55:38 +02:00
moneyl
3e972b501a Fix explosives (#361)
Currently explosives do no damage to tiles or damage the wrong area of the map. This is because `Owner.Delete` is called before the adjacent tiles / entities are damaged. This results in the explosives grid always being 0, and it's position always being (0, 0), meaning that the tiles in range of the explosive are not properly calculated. This fixes that (Issue #358).
2019-09-24 09:54:24 +02:00
Pieter-Jan Briers
59c758fa1c Update submodule 2019-09-23 12:35:52 +02:00
DamianX
10ca375284 Changed YouTool and EngiVend to use sprites from Bay (#352) 2019-09-20 20:40:36 +02:00
DamianX
76f732058a Added a bunch of things to vending machines (#354) 2019-09-20 20:40:10 +02:00
DamianX
a4aab7dbd1 Vending machines now show entity name rather than prototype ID (#355) 2019-09-20 20:39:54 +02:00
moneyl
0090af6b3b Disable footstep sounds for ghosts (#343)
* Disable footstep sounds for ghosts

Adds a check before playing footstep sounds to check if the mover is a ghost. Doesn't play footstep sounds if they are a ghost.

* Add FootstepSoundComponent

Adds FootstepSoundComponent. If a mob has this component, it will make footstep sounds. Otherwise they will not. As of this commit humans have this component and ghosts do not.
2019-09-20 19:42:45 +02:00
DamianX
de141c49c5 Fixed computers and vending machines collision (#350)
* Fixed computers and vending machines collision

* Also fixed mapped entities
2019-09-20 19:42:01 +02:00
DamianX
892d0ee162 RegisterIgnore WirePlacer (#351) 2019-09-19 16:52:53 -07:00
Acruid
e8485ee6c5 Added a new WirePlacerComponent. This is used on an item, and allows a client to place electrical wires in the world by AfterAttacking a a grid square without any other wires on it. 2019-09-19 11:47:45 -07:00
Acruid
e668bbbade The default space tile is now considered a subfloor. This flag is only used for draw culling, so it should have no unintended side effects. Previously it was not a sub floor, so things like wires would be hidden when placed in open space. 2019-09-19 11:46:03 -07:00
Acruid
cbea6bf41f The SubFloorHideComponent can now be properly started/stopped. Previously this did nothing. 2019-09-19 11:43:46 -07:00
Pieter-Jan Briers
35e88ea62c Add unit test asserting that saving, loading & saving the map produces identical output. (#348)
Also fixed PowerProvider violating this.
2019-09-19 13:46:01 +02:00
Pieter-Jan Briers
6c97b63e59 Property animations test component. (#340) 2019-09-18 22:14:21 +02:00
moneyl
ed1271c30b Add construction room and airlock to west side of station (#344)
Adds a room with some construction materials and an airlock to the west side of the station. Connects to the maintenance tunnel and a hallway.
2019-09-18 22:13:55 +02:00
DamianX
b496e8ef29 Fixed airlock maintenance panel showing when the airlock was open (#346) 2019-09-18 22:12:35 +02:00
DamianX
375813e5e2 Fixed external airlock maintenance panel (#345) 2019-09-18 22:11:53 +02:00
Acruid
dd06c71735 Updates the content components to be compatible with the changes in 55a50ff7ba.
Updates engine submodule.
2019-09-18 11:36:58 -07:00
DamianX
364279e0f7 Added medical scanner (#338)
* Added medical scanner

* RegisterIgnore
2019-09-18 20:24:55 +02:00
moneyl
415ac8fa46 Replace non-existent prototype in ToolLockerFillComponent (#341)
Previously this component would sometimes try to use a non-existent prototype, `HelmetEngineering`. This would result in a server crash upon loading any map that uses the `Tool Locker (Filled)` component. This replaces that non existent prototype with `HatHardhatRed`, which does exist, and is something you might expect to find in a tool locker.
2019-09-17 17:56:37 -07:00
Acruid
fc5d7835c0 Updates various systems to the new InputCommandHandler delegate signature, implementing the new handled return value.
Modifies the construction system to use the newer InputHandler system, instead of the older ClickComponent system.
Updates the engine submodule.
2019-09-17 16:10:59 -07:00
Acruid
b55d6cbf75 Update engine submodule. 2019-09-16 16:27:42 -07:00
Acruid
d5283fca09 Update engine submodule. 2019-09-16 11:21:38 -07:00
Acruid
311f843ea1 Keeps the items inside a storage container spread out with a small local offset. Previously, all of the items were set to the container's position, resulting in a single pile.
Updates engine submodule.
2019-09-15 15:29:16 -07:00
Víctor Aguilera Puerto
3f4c9a8326 Use improved ItemLists (#335)
* Use improved ItemList

* Address reviews

* Update submodule
2019-09-13 15:35:06 +02:00
Pieter-Jan Briers
e570f10d69 Add benchmark for dependency injection. 2019-09-11 20:08:43 +02:00
Pieter-Jan Briers
97e18b9ed4 Fix camera recoil restore overshooting at low framerates. 2019-09-11 20:08:43 +02:00
Acruid
c56d56c2d1 Storage containers no longer eat the items out of a container, or eat world machines like APCs and Wires. Previously they would eat any entity that would fit inside them. This resolves https://github.com/space-wizards/space-station-14/issues/254. 2019-09-09 14:56:13 -07:00
moneyl
4e2f694a0d Fix crash caused by invalid construction ghost shader name (#336)
* Fix construction ghost shader name

Construction ghosts were using a shader called `selection_outline_unshader`, which is not a valid shader name. This caused a crash when constructing things. This fixes that problem by updating them to use a valid shader `unshaded`.

I haven't followed the changes to shaders, but it's likely that this was caused by the shader name being changed and not updated in the construction ghost prototype.

* Remove specific outline shader for construction ghost prototypes

No longer needed.
2019-09-09 22:04:34 +02:00
Acruid
892b9d041d Update engine submodule. 2019-09-09 12:34:58 -07:00
Acruid
ea58ebed50 If the player mob does not have an InventoryComponent, don't spawn the default inventory items. Previously the items were blindly spawned in, and if they were not able to be equipped to the player, ended up at the map origin. 2019-09-09 10:52:45 -07:00
Acruid
adb7e1b598 InventoryComponent now handles the ContainerContentsModifiedMessage sent when the underlying Entity Container system may have removed of of the InventoryComponent's entities. This resolves one of the underlying bugs in https://github.com/space-wizards/space-station-14/issues/254.
Updates engine submodule.
2019-09-09 10:46:20 -07:00
DamianX
b9f6ea68ce Layer popup messages above UI windows (#330)
* Layer popup messages above UI windows

* Added PopupRoot
2019-09-08 16:13:09 +02:00
DamianX
63bb4c4d0a Create BoundUserInterfaces with DynamicTypeFactory (content) (#332)
* Create BoundUserInterfaces with DynamicTypeFactory (content)

* maybe this time I did it right
2019-09-08 16:05:34 +02:00
Acruid
5c40300474 Sanitize the OpenSlotStorageUIMessage handling on the server. 2019-09-06 16:28:21 -07:00
Acruid
6ca9e16670 Changes the interaction system so that if a client sends a UseItemInHand input command while not having a valid attached entity, the handler method will block the command. Previously, the handler method would throw a NullRefException if this occurred. 2019-09-06 10:13:48 -07:00
DamianX
deb2b984eb Fixed TechnologyDatabaseState warning (#334) 2019-09-06 15:25:07 +02:00
DamianX
0d7674b934 Remove remnants of BoundingBox (#333) 2019-09-06 15:24:23 +02:00
Pieter-Jan Briers
fc1dd51e63 Update submodule 2019-09-06 15:23:52 +02:00
DamianX
0815050b2a Horrendously makes PowerDevice connect to the closest Provider (#326)
* Somewhat decently makes PowerDevice connect to the closest Provider

* Fix NullRef on server shutdown

* Fix null reference

* piss

* revert bad fix
2019-09-06 10:05:17 +02:00
DamianX
36078382e4 Airlock hacking (#329)
* Airlock hacking

* Added status text

* Whoops don't need this

* Update Content.Server/GameObjects/Components/Doors/AirlockComponent.cs

Co-Authored-By: Pieter-Jan Briers <pieterjan.briers@gmail.com>

* ComponentReference ServerDoorComponent

* Suggested name
2019-09-06 10:05:02 +02:00
Víctor Aguilera Puerto
34f4731c9b Fix bug where research console GUI didn't close correctly (#331) 2019-09-06 08:13:17 +02:00
DamianX
6e2799f048 ID card console (#324)
* ID card console

* Container -> ContainerSlot
2019-09-06 08:12:44 +02:00
Víctor Aguilera Puerto
ba8b495ec0 Adds Research, unlockable technologies, Protolathes... (#264)
* Work on Research so far
More work on UI...
Fix ResearchClient and Protolathe UI stuff.
Fix infinite select -> request state -> select -> ... loop
Add UI to ResearchClient, etc.
Technology Database states, and a bit of work on the research console ui
A bit of work on Research Console UI
Protolathe sync
Stuff that actually does things
Protolathe databases yay
Alright got my motivation back
Yeah, no. It's almost 3 AM already
Fix serialization bug again
More work on stuff
Stuff
Adds files for most new components/systems.

* Protolathes actually work now

* Research. Just Research.

* Adds icons from Eris.

* Address reviews

* Change LatheMenu resize behaviour

* Update Content.Client/GameObjects/Components/Research/ResearchConsoleBoundUserInterface.cs

Co-Authored-By: Pieter-Jan Briers <pieterjan.briers@gmail.com>

* Update Content.Client/Research/ResearchConsoleMenu.cs

Co-Authored-By: Pieter-Jan Briers <pieterjan.briers@gmail.com>

* Move IoC Resolve out of for loop

* Address review

* Localize stuff
2019-09-03 22:51:19 +02:00
metalgearsloth
b62fb4a318 Add ammo boxes (#325)
Same as magazines except they can't be loaded into a weapon.
You can transfer ammo to and from magazines / boxes freely where the caliber matches so you don't need to necessarily match the magazine type.
Used popups to reflect transfer amount.
Also fixed the mag sprite showing as 0 capacity on spawn and the 10mm smg flash magazine prototype
2019-09-03 22:35:19 +02:00
Acruid
9353a060f2 Physics Shapes (#306)
* Removed BoundingBoxComponent.

* Updated prototypes to use refactored CollidableComponent.

* Renamed ICollidable to IPhysBody.
Moved ICollidable to the Shared/Physics namespace.

* Migrated more yaml files to use PhysShapes.

* Updated YAML to use the new list-of-bodies system.

* Updated the new prototypes.

* Update submodule

* Update submodule again, whoops
2019-09-03 22:14:04 +02:00
Acruid
5aafe89d95 Set the max size of a Storage to a constant, regardless of the physical size of the Storage. This lets lockers eat objects larger than themselves (like mobs). This resolves https://github.com/space-wizards/space-station-14/issues/294. 2019-09-01 17:08:15 -07:00
Acruid
7a55c07e22 The entity spawned when an item is thrown from a stack now properly sets its stack size to 1. This resolves https://github.com/space-wizards/space-station-14/issues/312. 2019-09-01 16:23:30 -07:00
Acruid
9ffbb51fd1 Items are now dropped at the pointer location if the pointer is withing the interaction range. Previously the dropped item was always placed at the player entity location.
Resolves https://github.com/space-wizards/space-station-14/issues/235.
2019-09-01 16:01:57 -07:00
DamianX
f3b460c8b4 IDs and access (#319)
* something about access

* Fixed deny animation
2019-09-01 22:57:22 +02:00
ShadowCommander
0329150109 Fix OpenStorage button in HumanInventoryWindow (#322) 2019-09-01 22:50:13 +02:00
DamianX
264a63b7f6 Wires! (#315)
* Wires!

* Use state instead of messages

* cleanup

* Update submodule

* actually fix conflict

* Maybe fix conflicts?

* Localized strings, removed hardcoded sprite path

* cleanup

* More localization and sounds
2019-09-01 22:15:34 +02:00
Pieter-Jan Briers
70e3cffa90 Update submodule 2019-09-01 09:56:21 +02:00
Acruid
fc046fb8ca Move ClientChangedHandMsg to Content assembly. This Resolves https://github.com/space-wizards/RobustToolbox/issues/703.
Update Submodule.
2019-08-31 17:49:18 -07:00
Acruid
9acf37e99d PowerStorageComponent now prints battery statistics to the examine window. 2019-08-30 23:36:07 -07:00
Pieter-Jan Briers
1645cb2dd0 Fix attempt 2. 2019-08-30 18:34:07 +02:00
Pieter-Jan Briers
12e635a411 Re-implement missing TLS version hack.
Accidentally dropped it when making the current launcher.
2019-08-30 18:11:44 +02:00
Pieter-Jan Briers
b40a96f545 Make TabContainer spaced a bit more. 2019-08-30 09:39:22 +02:00
Pieter-Jan Briers
64f148cb6e Update submodule 2019-08-29 23:50:49 +02:00
Pieter-Jan Briers
4d31537add Fix Content.Tests not being ran on .NET Core. 2019-08-29 23:50:09 +02:00
metalgearsloth
ecd77d6c48 Add more hats (#318)
Removed existing ones for simplicity.

Will need to add toggles later.
2019-08-29 19:26:30 +02:00
Pieter-Jan Briers
5716671dcf Fix some compiler warnings. 2019-08-29 16:59:59 +02:00
Pieter-Jan Briers
6bdd26506c Don't spawn all bullets in a magazine until necessary. 2019-08-29 15:05:42 +02:00
Pieter-Jan Briers
5858de0b08 More parallax debugging stuff. 2019-08-29 14:36:31 +02:00
Pieter-Jan Briers
ffc9f10399 Explicitly dispose ImageSharp images. 2019-08-28 12:08:10 +02:00
Pieter-Jan Briers
0f0a3eb822 Mark a bunch of packages as private assets.
This will prevent them being referenced in content, so stuff like OpenTK isn't "accessible" from Content.Client.
2019-08-27 22:51:31 +02:00
Pieter-Jan Briers
9ba5f9f2a3 Remove accidental reference to Mono.Cecil. 2019-08-27 22:50:06 +02:00
Pieter-Jan Briers
ecb7cd3c66 Update all the NuGet packages & submodule. 2019-08-27 22:39:32 +02:00
Pieter-Jan Briers
293f88599d Update submodule. 2019-08-27 22:00:38 +02:00
Pieter-Jan Briers
55f9411493 Verbs don't open an invisible popup if there's no entities. 2019-08-25 22:53:36 +02:00
Pieter-Jan Briers
4232397aa8 Fix file paths for gun noises. 2019-08-25 15:11:51 +02:00
Pieter-Jan Briers
ee029de5e7 Allow stack component count to be VV'd. 2019-08-25 15:07:26 +02:00
ShadowCommander
34e7edb5f5 Fix for clicking an item in the offhand with the activehand (#314)
* Fix for clicking an item in the offhand with the activehand

* Renamed attacked to entity
2019-08-25 13:10:59 +02:00
metalgearsloth
03ac153417 Fix 9mm guns (#311)
I r dumb.

Fix calibers and fix my script import of the top-mounted image.
2019-08-25 13:00:37 +02:00
Odin Hultgren Van Der Horst
8a88ee7f90 Airlock collision (#316)
* Added colmask to airlock

* Removed superfluous space
2019-08-25 13:00:08 +02:00
Pieter-Jan Briers
d6113b6147 Update submodule 2019-08-25 12:58:45 +02:00
Pieter-Jan Briers
7306bcc35a Fix VerbSystem crash.
Fixes #317
2019-08-25 12:54:55 +02:00
Pieter-Jan Briers
77216af44e Don't copy project dependencies for content projects.
#850
2019-08-22 18:44:35 +02:00
Pieter-Jan Briers
ffbff0d765 Fix build 2019-08-22 11:10:14 +02:00
metalgearsloth
9431011ba5 Add more dakka (#307)
* Add more dakka

Some slight codebase changes to facilitate more robust behaviour.

* Update Content.Server/GameObjects/Components/Projectiles/ProjectileComponent.cs

Co-Authored-By: Pieter-Jan Briers <pieterjan.briers@gmail.com>

* Remix last stereo to mono

hpistol + ltrifle
2019-08-22 11:08:32 +02:00
Pieter-Jan Briers
44fdf4022e Fix accidental C# 8.0 usage. 2019-08-22 11:06:10 +02:00
Pieter-Jan Briers
09b1066122 Try to fix weird integration tests issues, update submodule. 2019-08-22 10:21:54 +02:00
Pieter-Jan Briers
ecbf9a7706 Fix build 2019-08-22 09:28:24 +02:00
Pieter-Jan Briers
ab1108b731 Update submodule 2019-08-21 23:40:22 +02:00
DamianX
f0053f15bf Added pickaxe, asteroid rock and floors (#301) 2019-08-21 22:50:15 +02:00
Pieter-Jan Briers
4175f891fb Mark some text input keybinds as repeat. 2019-08-21 19:46:06 +02:00
Pieter-Jan Briers
c207297c57 Merge branches 'pseudo-classes', 'input-refactor' and '19-08-21-update-submodule' 2019-08-21 17:25:01 +02:00
Pieter-Jan Briers
6ca70b0156 Apply suggestions from PR 2019-08-21 17:24:05 +02:00
Pieter-Jan Briers
6e6aef62c6 Update submodule 2019-08-21 17:22:24 +02:00
Pieter-Jan Briers
27d7337abf Gitignore some stuff in BuildChecker 2019-08-21 17:20:28 +02:00
ShadowCommander
66c5affa65 Merge branch 'master' of https://github.com/space-wizards/space-station-14 2019-08-20 18:27:12 -07:00
DamianX
be4197351a Fixed examine things (#308) 2019-08-19 22:27:25 +02:00
DamianX
4dcbf28714 IoC'd random (#302)
* Implemented RobustRandom

* update submodule

* update submodule

* Fix benchmark
2019-08-17 12:09:09 -07:00
ShadowCommander
4c67856dd6 Update RobustToolbox 2019-08-16 23:14:43 -07:00
ShadowCommander
1c81f6097f Merge branch 'master' of https://github.com/space-wizards/space-station-14 2019-08-16 15:46:06 -07:00
ShadowCommander
7f5654bb06 Update RobustToolbox 2019-08-16 15:15:00 -07:00
DamianX
534af65f7c Fixed inventory button not updating when window was closed (#304)
* Fixed inventory button not updating when window was closed

* Only add the event handler once

* uhhhh

* UHHHHHH
2019-08-16 21:52:00 +02:00
Pieter-Jan Briers
73dc55d8ef Update submodule 2019-08-16 21:45:34 +02:00
DamianX
225bc86d73 Revert "full mime outfit, HoP hat and uniform" (#305)
This reverts commit 89c485200b.
2019-08-16 18:08:43 +02:00
CatTheSystem
3b48926d75 Help me 2019-08-15 21:26:56 +03:00
Pieter-Jan Briers
b38a014b02 Fix build 2019-08-15 15:49:16 +02:00
Pieter-Jan Briers
aed1a0d985 And I messed up again. 2019-08-14 22:06:52 +02:00
Pieter-Jan Briers
9cb37a6376 UI system refactor; submodule update. 2019-08-14 22:04:35 +02:00
Pieter-Jan Briers
6be135a137 Fix human inventory window ID slot using mask texture. 2019-08-14 21:10:56 +02:00
DamianX
20c387158a RegisterIgnore vending machines (#300) 2019-08-14 18:15:26 +02:00
DamianX
88920696f3 Vending Machines (#296)
* Vending Machines

* addressed review
2019-08-14 10:49:28 +02:00
Pieter-Jan Briers
3c476d2b40 Update submodule. 2019-08-13 23:56:15 +02:00
Pieter-Jan Briers
72ba0d9458 Don't let bullets fly forever. Give them 10 seconds until deleted. 2019-08-13 23:43:44 +02:00
Pieter-Jan Briers
c95f17f54b Add more logging to the launcher. 2019-08-13 12:24:49 +02:00
Pieter-Jan Briers
f116e887ea Make light positioning not look as stupid. 2019-08-13 12:05:33 +02:00
Pieter-Jan Briers
d2a1309cb6 Update submodule. 2019-08-13 10:31:38 +02:00
Pieter-Jan Briers
73ab543cc2 Fix bad RSI state names causing issues on Windows. 2019-08-13 10:27:28 +02:00
Pieter-Jan Briers
96eb4afdf6 Import a ton of backpack types. 2019-08-12 21:34:35 +02:00
Pieter-Jan Briers
3a25395ff9 Chairs. 2019-08-12 19:16:25 +02:00
Pieter-Jan Briers
afd1215b03 Ignore computer component on client. 2019-08-12 18:47:49 +02:00
Pieter-Jan Briers
d4384aef73 Eris computer sprites, visualizer. 2019-08-12 18:43:24 +02:00
Pieter-Jan Briers
a2e6500d54 Allow packaging script to do .NET Core builds. 2019-08-11 21:42:52 +02:00
Pieter-Jan Briers
418522b714 Fix benchmarks compile on Framework. 2019-08-11 20:45:22 +02:00
Pieter-Jan Briers
e142155b01 Update submodule. 2019-08-11 16:46:12 +02:00
ShadowCommander
844518d921 Update RobustToolbox 2019-08-10 16:30:12 -07:00
Pieter-Jan Briers
589d52158c Update submodule. 2019-08-11 01:23:16 +02:00
ShadowCommander
e5090cd10d Update RobustToolbox 2019-08-10 16:09:31 -07:00
ShadowCommander
0071321c53 Merge branch 'master' into Input 2019-08-10 16:08:48 -07:00
Pieter-Jan Briers
7b9038e6da Hahah color lerp. 2019-08-11 01:02:59 +02:00
ShadowCommander
b59c1a2b0f Merge remote-tracking branch 'upstream/master' 2019-08-10 15:46:57 -07:00
Acruid
8b593d28c6 AI Wander & Barker (#286)
* Retrofit the AI system with new IoC features.
Fixed bug with turret rotation.

* Added new AI WanderProcessor, and it works.

* RNG walking directions are a bit more random now.

* Wander now actually uses the MoverSystem to move.
Wander now talks when he reaches his destination.

* Adds a new Static Barker AI for vending machines, so that they periodically advertise their brand.

* Barker now says some generic slogans.
Misc bug cleanup.

* Removed useless UsedImplicitly attribute from AI dependencies, suppressed unused variable warnings instead.
2019-08-10 14:19:52 +02:00
metalgearsloth
4b30c7e710 Add more tiles and walls (#289) 2019-08-10 14:17:49 +02:00
Pieter-Jan Briers
d7505ca8b5 Fix compiling benchmarks. 2019-08-10 13:45:35 +02:00
Pieter-Jan Briers
831af2f157 Add Mime outfit 🎭 & HoP outfit 📎 . (#292)
Add Mime outfit 🎭 & HoP outfit 📎 .
2019-08-10 13:45:26 +02:00
Pieter-Jan Briers
ab954c9f53 Merge branch 'master' into clothing 2019-08-10 13:42:44 +02:00
Pieter-Jan Briers
7c3cf945cf Adds Captain outfit. 👑 (#291)
Adds Captain outfit. 👑
2019-08-10 13:41:00 +02:00
ShadowCommander
b38e780f01 Updates Submodule and updates Inventory Button variables 2019-08-09 15:47:59 -07:00
scuffedjays
89c485200b full mime outfit, HoP hat and uniform 2019-08-08 21:46:25 -05:00
Sauberstaub
c759d35dea Update hats.yml 2019-08-08 14:05:33 -05:00
scuffedjays
81fd26f5db Capitan 2019-08-08 12:34:05 -05:00
ShadowCommander
7753ae7c3a Added OpenStorage trigger on ActivateItemInWorld
When ActivateItemInWorld is pressed when hovering over a InventoryButton and the InventorySlot contains a storage item, it will open the StorageGUI.
2019-08-07 15:56:22 -07:00
Pieter-Jan Briers
41cb27dd67 Color interpolation benchmark. 2019-08-07 21:00:26 +02:00
Pieter-Jan Briers
d3947c73ab Update submodule. 2019-08-07 20:59:58 +02:00
Sauberstaub
4cd99fc624 his name chef 👨‍🍳 (#290)
* his name chef

* fixed inhand sprites!!!!!

* typo fix
2019-08-07 20:56:08 +02:00
Pieter-Jan Briers
3c09c18943 Update submodule, noise removal, parallax optimizations. 2019-08-07 18:10:55 +02:00
Pieter-Jan Briers
6079950220 Update submodule. 2019-08-06 14:25:53 +02:00
ShadowCommander
8c59d2e3b9 Removed extra dependencies 2019-08-06 01:09:44 -07:00
ShadowCommander
fb9dbd8e16 Merge remote-tracking branch 'upstream/master' 2019-08-05 23:28:01 -07:00
ShadowCommander
7454c62ff2 Upgraded ChatBox and fixed FocusChat hotkey
Upgraded ChatBox to use Keybinds.
Put cursor at the end of text of ChatBox history.
Fixed FocusChat hotkey getting called when typing in a LineEdit.
Added Keybinds.
2019-08-05 22:59:37 -07:00
Pieter-Jan Briers
cf234fe66f Update submodule. 2019-08-05 15:59:12 +02:00
ShadowCommander
ca9cc36a93 Merge remote-tracking branch 'upstream/master' into Input 2019-08-04 16:10:06 -07:00
ShadowCommander
7422d9148a Refactored input system 2019-08-04 16:03:51 -07:00
Pieter-Jan Briers
6c76c5d917 Update submodule 2019-08-04 18:26:26 +02:00
Pieter-Jan Briers
687d22188a Fix airlock placement. 2019-08-04 11:54:58 +02:00
ShadowCommander
b996466b3d Update RobustToolbox 2019-08-03 19:08:22 -07:00
Pieter-Jan Briers
02da078baf Unified those messy FrameEventArgs. 2019-08-04 01:08:55 +02:00
ShadowCommander
121d440ac9 Merge remote-tracking branch 'upstream/master' 2019-08-03 12:41:06 -07:00
Pieter-Jan Briers
a246d7e48d Update submodule.
Fixes #283
2019-08-03 15:21:29 +02:00
Pieter-Jan Briers
ed60c41c35 Update submodule. 2019-08-03 15:07:54 +02:00
Pieter-Jan Briers
3488ca0173 Try to work around space-wizards/space-station-14#284 2019-08-03 15:07:47 +02:00
Pieter-Jan Briers
de334904b4 Spears get inhands. 2019-08-02 22:45:41 +02:00
Pieter-Jan Briers
8cf5195db6 .NET Core support.
Also dropped x86 because apparently unit tests still work.
2019-08-02 22:45:41 +02:00
ShadowCommander
041038fa1b Merge remote-tracking branch 'upstream/master' 2019-08-01 17:36:19 -07:00
ShadowCommander
151d3a3672 Fixed HandsGui and added an icon to access worn inventories (#279)
* Fixed HandsGui children so that HandsGui is clickable

* Added TextureButton for opening Storage items

* Update ClientInventoryComponent.cs

Fixed HandleComponentState so that it only updates the inventory when there are changes.

* Implemented storage button on Inventory

Adds a small button on the bottom right of Storage items when inside the inventory. When the button is pressed it opens the Storage UI.
2019-08-01 01:38:24 +02:00
Pieter-Jan Briers
1d9d01b355 Update submodule. 2019-08-01 00:13:58 +02:00
Pieter-Jan Briers
d5ec234fd3 Ignore some client components on the server. 2019-07-31 22:42:36 +02:00
Pieter-Jan Briers
e95bf0a642 Fix duplicate mop definition. 2019-07-31 22:39:51 +02:00
Pieter-Jan Briers
a7f1520d1f Fix speech bubble UI blocking world interaction. 2019-07-31 16:45:54 +02:00
ShadowCommander
d9628d39eb Implemented storage button on Inventory
Adds a small button on the bottom right of Storage items when inside the inventory. When the button is pressed it opens the Storage UI.
2019-07-29 13:32:04 -07:00
ShadowCommander
bb20243b05 Update ClientInventoryComponent.cs
Fixed HandleComponentState so that it only updates the inventory when there are changes.
2019-07-28 17:24:15 -07:00
ShadowCommander
e5d9634cfe Added TextureButton for opening Storage items 2019-07-27 03:32:25 -07:00
ShadowCommander
d3053c3c8c Fixed HandsGui children so that HandsGui is clickable 2019-07-27 00:32:59 -07:00
7057 changed files with 71743 additions and 6255 deletions

View File

@@ -1,5 +1,5 @@
version: 1.0.{build}
image: Visual Studio 2017
image: Visual Studio 2019
platform: x64
configuration: Debug
@@ -12,10 +12,9 @@ build:
before_build:
- cmd: py -3 -m pip install --user requests
- cmd: py -3 RUN_THIS.py --no-prompt
- cmd: nuget restore SpaceStation14.sln
build_script:
- ps: msbuild SpaceStation14.sln /v:m /nologo /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /p:Platform=x64 /p:Configuration=Debug /p:AppVeyor=yes
- ps: dotnet build SpaceStation14.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /p:AppVeyor=yes
test:
assemblies:

3
.gitignore vendored
View File

@@ -261,7 +261,8 @@ __pycache__/
*.pyc
# Visual Studio Code workspace settings.
.vscode/
.vscode/*
!.vscode/extensions.json
# Release package files go here:
release/

View File

@@ -2,6 +2,8 @@ language: csharp
dist: trusty
sudo: false
mono: none
# dotnet: 3.1.100 # Travis is shitting itself right now and it can't locate .NET Core packages.
os:
- linux
@@ -20,7 +22,9 @@ cache:
directories:
- packages/
- RobustToolbox/Dependencies/
- RobustToolbox/SS14.Client.Godot/.mono/assemblies/
install:
- curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 3.1.100
#before_install:
# - if [ $TRAVIS_OS_NAME = osx ]; then brew update && brew upgrade python; fi
@@ -28,11 +32,9 @@ cache:
before_script:
#- "if [ $TRAVIS_OS_NAME = linux ]; then pyenv shell 3.6; fi"
- "python3.5 -m pip install --user requests"
- "nuget restore SpaceStation14.sln"
- "python3.5 RUN_THIS.py --no-prompt"
script:
- "msbuild /p:Configuration=Debug /p:Platform=x64 /nologo /m SpaceStation14.sln /p:Python=python3.5"
- "mono packages/nunit.consolerunner/3.10.0/tools/nunit3-console.exe bin/Content.Tests/Content.Tests.dll bin/Content.IntegrationTests/Content.IntegrationTests.dll"
- "Tools/run_travis.sh"

6
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"recommendations": [
"ms-vscode.csharp",
"editorconfig.editorconfig"
]
}

View File

@@ -1,2 +1,5 @@
INSTALLED_HOOKS_VERSION
DISABLE_SUBMODULE_AUTOUPDATE
*.nuget*
project.assets.json
project.packagespec.json

View File

@@ -17,7 +17,7 @@ https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
<Python>python3</Python>
<Python Condition="'$(OS)'=='Windows_NT' Or '$(OS)'=='Windows'">py -3</Python>
<ProjectGuid>{C899FCA4-7037-4E49-ABC2-44DE72487110}</ProjectGuid>
<TargetFrameworkMoniker>.NETFramework, Version=v4.7.1</TargetFrameworkMoniker>
<TargetFrameworkMoniker>.NETFramework, Version=v4.7.2</TargetFrameworkMoniker>
</PropertyGroup>
<PropertyGroup>
<OutputType>Library</OutputType>

View File

@@ -83,7 +83,7 @@ def install_hooks():
for filename in os.listdir(str(hooks_source_dir)):
print("Copying hook {}".format(filename))
shutil.copyfile(str(hooks_source_dir/filename),
shutil.copy2(str(hooks_source_dir/filename),
str(hooks_target_dir/filename))

View File

@@ -1,5 +0,0 @@
#!/bin/bash
cd "$(dirname "$0")"
exec mono bin/SS14.Launcher.exe

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>SS14L</string>
<key>CFBundleDisplayName</key>
<string>Space Station 14 Launcher</string>
<key>CFBundleExecutable</key>
<string>SS14</string>
<!--
Just a note about this icon.
MacOS seems REALLY iffy about this and even when the file is correct,
it can take forever before it decides to actually update it and display it.
TL;DR Apple is stupid.
-->
<key>CFBundleIconFile</key>
<string>ss14</string>
</dict>
</plist>

View File

@@ -1,8 +0,0 @@
#!/bin/sh
# cd to file containing script or something?
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
cd "$BASEDIR"
exec /Library/Frameworks/Mono.framework/Versions/Current/Commands/mono ../Resources/SS14.Launcher.exe

View File

@@ -5,4 +5,4 @@ BASEDIR=$(dirname "$0")
echo "$BASEDIR"
cd "$BASEDIR"
exec /Library/Frameworks/Mono.framework/Versions/Current/Commands/mono ../Resources/Robust.Client.exe
exec ../Resources/Robust.Client "$@"

View File

@@ -1 +0,0 @@
call bin\SS14.Launcher.exe

View File

@@ -0,0 +1,166 @@
#if NETCOREAPP
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
#endif
using System;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using Robust.Shared.Maths;
using Robust.Shared.Random;
using SysVector4 = System.Numerics.Vector4;
namespace Content.Benchmarks
{
[DisassemblyDiagnoser]
public class ColorInterpolateBenchmark
{
#if NETCOREAPP
private const MethodImplOptions AggressiveOpt = MethodImplOptions.AggressiveOptimization;
#else
private const MethodImplOptions AggressiveOpt = default;
#endif
private (Color, Color)[] _colors;
private Color[] _output;
[Params(100)] public int N { get; set; }
[GlobalSetup]
public void Setup()
{
var random = new Random(3005);
_colors = new (Color, Color)[N];
_output = new Color[N];
for (var i = 0; i < N; i++)
{
var r1 = random.NextFloat();
var g1 = random.NextFloat();
var b1 = random.NextFloat();
var a1 = random.NextFloat();
var r2 = random.NextFloat();
var g2 = random.NextFloat();
var b2 = random.NextFloat();
var a2 = random.NextFloat();
_colors[i] = (new Color(r1, g1, b1, a1), new Color(r2, g2, b2, a2));
}
}
[Benchmark]
public void BenchSimple()
{
for (var i = 0; i < N; i++)
{
ref var tuple = ref _colors[i];
_output[i] = InterpolateSimple(tuple.Item1, tuple.Item2, 0.5f);
}
}
[Benchmark]
public void BenchSysVector4In()
{
for (var i = 0; i < N; i++)
{
ref var tuple = ref _colors[i];
_output[i] = InterpolateSysVector4In(tuple.Item1, tuple.Item2, 0.5f);
}
}
[Benchmark]
public void BenchSysVector4()
{
for (var i = 0; i < N; i++)
{
ref var tuple = ref _colors[i];
_output[i] = InterpolateSysVector4(tuple.Item1, tuple.Item2, 0.5f);
}
}
#if NETCOREAPP
[Benchmark]
public void BenchSimd()
{
for (var i = 0; i < N; i++)
{
ref var tuple = ref _colors[i];
_output[i] = InterpolateSimd(tuple.Item1, tuple.Item2, 0.5f);
}
}
[Benchmark]
public void BenchSimdIn()
{
for (var i = 0; i < N; i++)
{
ref var tuple = ref _colors[i];
_output[i] = InterpolateSimdIn(tuple.Item1, tuple.Item2, 0.5f);
}
}
#endif
[MethodImpl(AggressiveOpt)]
public static Color InterpolateSimple(Color a, Color b, float lambda)
{
return new Color(
a.R + (b.R - a.R) * lambda,
a.G + (b.G - a.G) * lambda,
a.B + (b.G - a.B) * lambda,
a.A + (b.A - a.A) * lambda
);
}
[MethodImpl(AggressiveOpt)]
public static Color InterpolateSysVector4(Color a, Color b,
float lambda)
{
ref var sva = ref Unsafe.As<Color, SysVector4>(ref a);
ref var svb = ref Unsafe.As<Color, SysVector4>(ref b);
var res = SysVector4.Lerp(sva, svb, lambda);
return Unsafe.As<SysVector4, Color>(ref res);
}
[MethodImpl(AggressiveOpt)]
public static Color InterpolateSysVector4In(in Color endPoint1, in Color endPoint2,
float lambda)
{
ref var sva = ref Unsafe.As<Color, SysVector4>(ref Unsafe.AsRef(endPoint1));
ref var svb = ref Unsafe.As<Color, SysVector4>(ref Unsafe.AsRef(endPoint2));
var res = SysVector4.Lerp(svb, sva, lambda);
return Unsafe.As<SysVector4, Color>(ref res);
}
#if NETCOREAPP
[MethodImpl(AggressiveOpt)]
public static Color InterpolateSimd(Color a, Color b,
float lambda)
{
var vecA = Unsafe.As<Color, Vector128<float>>(ref a);
var vecB = Unsafe.As<Color, Vector128<float>>(ref b);
vecB = Fma.MultiplyAdd(Sse.Subtract(vecB, vecA), Vector128.Create(lambda), vecA);
return Unsafe.As<Vector128<float>, Color>(ref vecB);
}
[MethodImpl(AggressiveOpt)]
public static Color InterpolateSimdIn(in Color a, in Color b,
float lambda)
{
var vecA = Unsafe.As<Color, Vector128<float>>(ref Unsafe.AsRef(a));
var vecB = Unsafe.As<Color, Vector128<float>>(ref Unsafe.AsRef(b));
vecB = Fma.MultiplyAdd(Sse.Subtract(vecB, vecA), Vector128.Create(lambda), vecA);
return Unsafe.As<Vector128<float>, Color>(ref vecB);
}
#endif
}
}

View File

@@ -29,7 +29,7 @@ namespace Content.Benchmarks
dummyReg.SetupGet(p => p.Type).Returns(typeof(DummyComponent));
dummyReg.SetupGet(p => p.NetID).Returns((uint?) null);
dummyReg.SetupGet(p => p.NetworkSynchronizeExistence).Returns(false);
dummyReg.SetupGet(p => p.References).Returns(new Type[] {typeof(DummyComponent)});
dummyReg.SetupGet(p => p.References).Returns(new [] {typeof(DummyComponent)});
var componentFactory = new Mock<IComponentFactory>();
componentFactory.Setup(p => p.GetComponent<DummyComponent>()).Returns(new DummyComponent());

View File

@@ -1,14 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
<TargetFramework>$(TargetFramework)</TargetFramework>
<OutputPath>..\bin\Content.Benchmarks\</OutputPath>
<IsPackable>false</IsPackable>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Platforms>x86;x64</Platforms>
<Platforms>x64</Platforms>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>8</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.11.5" />
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Content.Client\Content.Client.csproj" />

View File

@@ -0,0 +1,69 @@
/*
using BenchmarkDotNet.Attributes;
using Robust.Shared.IoC;
namespace Content.Benchmarks
{
// To actually run this benchmark you'll have to make DependencyCollection public so it's accessible.
public class DependencyInjectBenchmark
{
[Params(InjectMode.Reflection, InjectMode.DynamicMethod)]
public InjectMode Mode { get; set; }
private DependencyCollection _dependencyCollection;
[GlobalSetup]
public void Setup()
{
_dependencyCollection = new DependencyCollection();
_dependencyCollection.Register<X1, X1>();
_dependencyCollection.Register<X2, X2>();
_dependencyCollection.Register<X3, X3>();
_dependencyCollection.Register<X4, X4>();
_dependencyCollection.Register<X5, X5>();
_dependencyCollection.BuildGraph();
switch (Mode)
{
case InjectMode.Reflection:
break;
case InjectMode.DynamicMethod:
// Running this without oneOff will cause DependencyCollection to cache the DynamicMethod injector.
// So future injections (even with oneOff) will keep using the DynamicMethod.
// AKA, be fast.
_dependencyCollection.InjectDependencies(new TestDummy());
break;
}
}
[Benchmark]
public void Inject()
{
_dependencyCollection.InjectDependencies(new TestDummy(), true);
}
public enum InjectMode
{
Reflection,
DynamicMethod
}
private sealed class X1 { }
private sealed class X2 { }
private sealed class X3 { }
private sealed class X4 { }
private sealed class X5 { }
private sealed class TestDummy
{
[Dependency] private readonly X1 _x1;
[Dependency] private readonly X2 _x2;
[Dependency] private readonly X3 _x3;
[Dependency] private readonly X4 _x4;
[Dependency] private readonly X5 _x5;
}
}
}
*/

View File

@@ -1,9 +1,8 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Running;
namespace Content.Benchmarks
{
internal class Program
internal static class Program
{
public static void Main(string[] args)
{

View File

@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Content.Tests")]

View File

@@ -1,13 +1,13 @@
using System.Collections.Generic;
using Content.Shared.Chat;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Input;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using Robust.Shared.Localization;
using Robust.Shared.IoC;
namespace Content.Client.Chat
{
@@ -21,15 +21,15 @@ namespace Content.Client.Chat
private readonly IList<string> _inputHistory = new List<string>();
private ILocalizationManager localize = IoCManager.Resolve<ILocalizationManager>();
private readonly ILocalizationManager localize = IoCManager.Resolve<ILocalizationManager>();
public LineEdit Input { get; private set; }
public OutputPanel contents;
public OutputPanel Contents { get; }
// Buttons for filtering
public Button AllButton;
public Button LocalButton;
public Button OOCButton;
public Button AllButton { get; }
public Button LocalButton { get; }
public Button OOCButton { get; }
/// <summary>
/// Index while cycling through the input history. -1 means not going through history.
@@ -48,17 +48,15 @@ namespace Content.Client.Chat
public bool ReleaseFocusOnEnter { get; set; } = true;
protected override void Initialize()
public ChatBox()
{
base.Initialize();
MarginLeft = -475.0f;
/*MarginLeft = -475.0f;
MarginTop = 10.0f;
MarginRight = -10.0f;
MarginBottom = 235.0f;
AnchorLeft = 1.0f;
AnchorRight = 1.0f;
AnchorRight = 1.0f;*/
var outerVBox = new VBoxContainer();
@@ -79,12 +77,12 @@ namespace Content.Client.Chat
MarginLeftOverride = 4, MarginRightOverride = 4,
SizeFlagsVertical = SizeFlags.FillExpand
};
contents = new OutputPanel();
contentMargin.AddChild(contents);
Contents = new OutputPanel();
contentMargin.AddChild(Contents);
vBox.AddChild(contentMargin);
Input = new LineEdit();
Input.OnKeyDown += InputKeyDown;
Input.OnKeyBindDown += InputKeyBindDown;
Input.OnTextEntered += Input_OnTextEntered;
vBox.AddChild(Input);
@@ -121,23 +119,27 @@ namespace Content.Client.Chat
AddChild(outerVBox);
}
protected override void MouseDown(GUIMouseButtonEventArgs e)
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
{
base.MouseDown(e);
base.KeyBindDown(args);
if (!args.CanFocus)
{
return;
}
Input.GrabKeyboardFocus();
}
private void InputKeyDown(GUIKeyEventArgs e)
private void InputKeyBindDown(GUIBoundKeyEventArgs args)
{
if (e.Key == Keyboard.Key.Escape)
if (args.Function == EngineKeyFunctions.TextReleaseFocus)
{
Input.ReleaseKeyboardFocus();
e.Handle();
args.Handle();
return;
}
if (e.Key == Keyboard.Key.Up)
else if (args.Function == EngineKeyFunctions.TextHistoryPrev)
{
if (_inputIndex == -1 && _inputHistory.Count != 0)
{
@@ -153,12 +155,12 @@ namespace Content.Client.Chat
{
Input.Text = _inputHistory[_inputIndex];
}
Input.CursorPos = Input.Text.Length;
e.Handle();
args.Handle();
return;
}
if (e.Key == Keyboard.Key.Down)
else if (args.Function == EngineKeyFunctions.TextHistoryNext)
{
if (_inputIndex == 0)
{
@@ -171,20 +173,9 @@ namespace Content.Client.Chat
_inputIndex--;
Input.Text = _inputHistory[_inputIndex];
}
Input.CursorPos = Input.Text.Length;
e.Handle();
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
TextSubmitted = null;
Input = null;
contents = null;
args.Handle();
}
}
@@ -203,7 +194,7 @@ namespace Content.Client.Chat
formatted.PushColor(color);
formatted.AddText(message);
formatted.Pop();
contents.AddMessage(formatted);
Contents.AddMessage(formatted);
}
private void Input_OnTextEntered(LineEdit.LineEditEventArgs args)

View File

@@ -1,7 +1,6 @@
using System.Collections.Generic;
using Content.Client.Interfaces.Chat;
using Content.Shared.Chat;
using Robust.Client;
using Robust.Client.Console;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.UserInterface;
@@ -13,6 +12,7 @@ using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Client.Chat
@@ -81,13 +81,16 @@ namespace Content.Client.Chat
{
_netManager.RegisterNetMessage<MsgChatMessage>(MsgChatMessage.NAME, _onChatMessage);
_speechBubbleRoot = new Control();
_speechBubbleRoot.SetAnchorPreset(Control.LayoutPreset.Wide);
_speechBubbleRoot = new LayoutContainer
{
MouseFilter = Control.MouseFilterMode.Ignore
};
LayoutContainer.SetAnchorPreset(_speechBubbleRoot, LayoutContainer.LayoutPreset.Wide);
_userInterfaceManager.StateRoot.AddChild(_speechBubbleRoot);
_speechBubbleRoot.SetPositionFirst();
}
public void FrameUpdate(RenderFrameEventArgs delta)
public void FrameUpdate(FrameEventArgs delta)
{
// Update queued speech bubbles.
if (_queuedSpeechBubbles.Count == 0)
@@ -103,7 +106,7 @@ namespace Content.Client.Chat
continue;
}
queueData.TimeLeft -= delta.Elapsed;
queueData.TimeLeft -= delta.DeltaSeconds;
if (queueData.TimeLeft > 0)
{
continue;
@@ -270,7 +273,7 @@ namespace Content.Client.Chat
private void RepopulateChat(IEnumerable<StoredChatMessage> filteredMessages)
{
_currentChatBox.contents.Clear();
_currentChatBox.Contents.Clear();
foreach (var msg in filteredMessages)
{
@@ -328,7 +331,7 @@ namespace Content.Client.Chat
{
// Word is STILL too long.
// Truncate it with an ellipse.
messages.Add($"{word.Substring(0, SingleBubbleCharLimit-3)}...");
messages.Add($"{word.Substring(0, SingleBubbleCharLimit - 3)}...");
currentWordLength = 0;
continue;
}

View File

@@ -1,11 +1,11 @@
using Content.Client.Interfaces.Chat;
using Robust.Client;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Timers;
using Robust.Shared.Timing;
namespace Content.Client.Chat
{
@@ -31,14 +31,12 @@ namespace Content.Client.Chat
private readonly IEntity _senderEntity;
private readonly IChatManager _chatManager;
private Control _panel;
private float _timeLeft = TotalTime;
public float VerticalOffset { get; set; }
private float _verticalOffsetAchieved;
public float ContentHeight { get; }
public float ContentHeight { get; private set; }
public SpeechBubble(string text, IEntity senderEntity, IEyeManager eyeManager, IChatManager chatManager)
{
@@ -57,7 +55,7 @@ namespace Content.Client.Chat
};
label.SetMessage(text);
_panel = new PanelContainer
var panel = new PanelContainer
{
StyleClasses = { "tooltipBox" },
Children = { label },
@@ -65,19 +63,24 @@ namespace Content.Client.Chat
ModulateSelfOverride = Color.White.WithAlpha(0.75f)
};
AddChild(_panel);
AddChild(panel);
_panel.Size = _panel.CombinedMinimumSize;
ContentHeight = _panel.Height;
Size = (_panel.Width, 0);
ForceRunStyleUpdate();
ContentHeight = panel.CombinedMinimumSize.Y;
_verticalOffsetAchieved = -ContentHeight;
}
protected override void FrameUpdate(RenderFrameEventArgs args)
protected override Vector2 CalculateMinimumSize()
{
return (base.CalculateMinimumSize().X, 0);
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
_timeLeft -= args.Elapsed;
_timeLeft -= args.DeltaSeconds;
if (_timeLeft <= FadeTime)
{
@@ -99,7 +102,7 @@ namespace Content.Client.Chat
}
else
{
_verticalOffsetAchieved = FloatMath.Lerp(_verticalOffsetAchieved, VerticalOffset, 10 * args.Elapsed);
_verticalOffsetAchieved = FloatMath.Lerp(_verticalOffsetAchieved, VerticalOffset, 10 * args.DeltaSeconds);
}
var worldPos = _senderEntity.Transform.WorldPosition;
@@ -107,10 +110,10 @@ namespace Content.Client.Chat
var lowerCenter = _eyeManager.WorldToScreen(worldPos) / UIScale;
var screenPos = lowerCenter - (Width / 2, ContentHeight + _verticalOffsetAchieved);
Position = screenPos;
LayoutContainer.SetPosition(this, screenPos);
var height = (lowerCenter.Y - screenPos.Y).Clamp(0, ContentHeight);
Size = (Size.X, height);
LayoutContainer.SetSize(this, (Size.X, height));
}
private void Die()

View File

@@ -0,0 +1,32 @@
using Content.Client.Chat;
using Content.Client.GameTicking;
using Content.Client.Interfaces;
using Content.Client.Interfaces.Chat;
using Content.Client.Interfaces.Parallax;
using Content.Client.Parallax;
using Content.Client.Sandbox;
using Content.Client.UserInterface;
using Content.Client.Utility;
using Content.Shared.Interfaces;
using Robust.Shared.IoC;
namespace Content.Client
{
internal static class ClientContentIoC
{
public static void Register()
{
IoCManager.Register<IGameHud, GameHud>();
IoCManager.Register<IClientNotifyManager, ClientNotifyManager>();
IoCManager.Register<ISharedNotifyManager, ClientNotifyManager>();
IoCManager.Register<IClientGameTicker, ClientGameTicker>();
IoCManager.Register<IParallaxManager, ParallaxManager>();
IoCManager.Register<IChatManager, ChatManager>();
IoCManager.Register<IEscapeMenuOwner, EscapeMenuOwner>();
IoCManager.Register<ISandboxManager, SandboxManager>();
IoCManager.Register<IModuleManager, ClientModuleManager>();
IoCManager.Register<IClientPreferencesManager, ClientPreferencesManager>();
IoCManager.Register<IItemSlotManager, ItemSlotManager>();
}
}
}

View File

@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using Content.Client.Interfaces;
using Content.Shared;
using Robust.Client;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Input;
@@ -14,6 +13,7 @@ using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Client
@@ -59,8 +59,8 @@ namespace Content.Client
{
var label = new PopupLabel {Text = message};
var minimumSize = label.CombinedMinimumSize;
label.InitialPos = label.Position = coordinates.Position - minimumSize / 2;
_userInterfaceManager.StateRoot.AddChild(label);
LayoutContainer.SetPosition(label, label.InitialPos = coordinates.Position - minimumSize / 2);
_userInterfaceManager.PopupRoot.AddChild(label);
_aliveLabels.Add(label);
}
@@ -69,42 +69,38 @@ namespace Content.Client
PopupMessage(new ScreenCoordinates(_inputManager.MouseScreenPosition), message);
}
public void FrameUpdate(RenderFrameEventArgs eventArgs)
public void FrameUpdate(FrameEventArgs eventArgs)
{
foreach (var label in _aliveLabels)
_aliveLabels.ForEach(l =>
{
label.Update(eventArgs);
}
if (l.TimeLeft > 3f)
{
l.Dispose();
}
});
_aliveLabels.RemoveAll(l => l.Disposed);
}
private class PopupLabel : Label
{
private float _timeLeft;
public float TimeLeft { get; private set; }
public Vector2 InitialPos { get; set; }
protected override void Initialize()
public PopupLabel()
{
base.Initialize();
ShadowOffsetXOverride = 1;
ShadowOffsetYOverride = 1;
FontColorShadowOverride = Color.Black;
}
public void Update(RenderFrameEventArgs eventArgs)
protected override void Update(FrameEventArgs eventArgs)
{
_timeLeft += eventArgs.Elapsed;
Position = InitialPos - new Vector2(0, 20 * (_timeLeft * _timeLeft + _timeLeft));
if (_timeLeft > 0.5f)
TimeLeft += eventArgs.DeltaSeconds;
LayoutContainer.SetPosition(this, InitialPos - (0, 20 * (TimeLeft * TimeLeft + TimeLeft)));
if (TimeLeft > 0.5f)
{
Modulate = Color.White.WithAlpha(1f - 0.2f * (float)Math.Pow(_timeLeft - 0.5f, 3f));
if (_timeLeft > 3f)
{
Dispose();
}
Modulate = Color.White.WithAlpha(1f - 0.2f * (float)Math.Pow(TimeLeft - 0.5f, 3f));
}
}
}

View File

@@ -0,0 +1,74 @@
using System.Linq;
using Content.Client.Interfaces;
using Content.Shared.Preferences;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
namespace Content.Client
{
/// <summary>
/// Receives <see cref="PlayerPreferences" /> and <see cref="GameSettings" /> from the server during the initial
/// connection.
/// Stores preferences on the server through <see cref="SelectCharacter" /> and <see cref="UpdateCharacter" />.
/// </summary>
public class ClientPreferencesManager : SharedPreferencesManager, IClientPreferencesManager
{
#pragma warning disable 649
[Dependency] private readonly IClientNetManager _netManager;
#pragma warning restore 649
public GameSettings Settings { get; private set; }
public PlayerPreferences Preferences { get; private set; }
public void Initialize()
{
_netManager.RegisterNetMessage<MsgPreferencesAndSettings>(nameof(MsgPreferencesAndSettings),
HandlePreferencesAndSettings);
}
public void SelectCharacter(ICharacterProfile profile)
{
SelectCharacter(Preferences.IndexOfCharacter(profile));
}
public void SelectCharacter(int slot)
{
Preferences = new PlayerPreferences(Preferences.Characters, slot);
var msg = _netManager.CreateNetMessage<MsgSelectCharacter>();
msg.SelectedCharacterIndex = slot;
_netManager.ClientSendMessage(msg);
}
public void UpdateCharacter(ICharacterProfile profile, int slot)
{
var characters = Preferences.Characters.ToArray();
characters[slot] = profile;
Preferences = new PlayerPreferences(characters, Preferences.SelectedCharacterIndex);
var msg = _netManager.CreateNetMessage<MsgUpdateCharacter>();
msg.Profile = profile;
msg.Slot = slot;
_netManager.ClientSendMessage(msg);
}
public void CreateCharacter(ICharacterProfile profile)
{
UpdateCharacter(profile, Preferences.FirstEmptySlot);
}
public void DeleteCharacter(ICharacterProfile profile)
{
DeleteCharacter(Preferences.IndexOfCharacter(profile));
}
public void DeleteCharacter(int slot)
{
UpdateCharacter(null, slot);
}
private void HandlePreferencesAndSettings(MsgPreferencesAndSettings message)
{
Preferences = message.Preferences;
Settings = message.Settings;
}
}
}

View File

@@ -3,62 +3,50 @@ using System.Collections.Generic;
using System.Linq;
using Content.Client.GameObjects.Components.Construction;
using Content.Shared.Construction;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Interfaces.GameObjects;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Placement;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Placement;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.Utility;
using Robust.Shared.Enums;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Construction
{
public class ConstructionMenu : SS14Window
{
#pragma warning disable CS0649
[Dependency]
readonly IPrototypeManager PrototypeManager;
[Dependency]
readonly IResourceCache ResourceCache;
[Dependency] readonly IPrototypeManager PrototypeManager;
[Dependency] readonly IResourceCache ResourceCache;
#pragma warning restore
public ConstructorComponent Owner { get; set; }
Button BuildButton;
Button EraseButton;
LineEdit SearchBar;
Tree RecipeList;
TextureRect InfoIcon;
Label InfoLabel;
ItemList StepList;
private readonly Button BuildButton;
private readonly Button EraseButton;
private readonly LineEdit SearchBar;
private readonly Tree RecipeList;
private readonly TextureRect InfoIcon;
private readonly Label InfoLabel;
private readonly ItemList StepList;
private CategoryNode RootCategory;
CategoryNode RootCategory;
// This list is flattened in such a way that the top most deepest category is first.
List<CategoryNode> FlattenedCategories;
PlacementManager Placement;
private List<CategoryNode> FlattenedCategories;
private readonly PlacementManager Placement;
protected override Vector2? CustomSize => (500, 350);
public ConstructionMenu()
{
Size = new Vector2(500.0f, 350.0f);
}
protected override void Initialize()
{
base.Initialize();
IoCManager.InjectDependencies(this);
Placement = (PlacementManager)IoCManager.Resolve<IPlacementManager>();
Placement = (PlacementManager) IoCManager.Resolve<IPlacementManager>();
Placement.PlacementCanceled += OnPlacementCanceled;
Title = "Construction";
@@ -66,18 +54,18 @@ namespace Content.Client.Construction
var hSplitContainer = new HSplitContainer();
// Left side
var recipes = new VBoxContainer("Recipes") {CustomMinimumSize = new Vector2(150.0f, 0.0f)};
SearchBar = new LineEdit("Search") {PlaceHolder = "Search"};
RecipeList = new Tree("Tree") {SizeFlagsVertical = SizeFlags.FillExpand, HideRoot = true};
var recipes = new VBoxContainer {CustomMinimumSize = new Vector2(150.0f, 0.0f)};
SearchBar = new LineEdit {PlaceHolder = "Search"};
RecipeList = new Tree {SizeFlagsVertical = SizeFlags.FillExpand, HideRoot = true};
recipes.AddChild(SearchBar);
recipes.AddChild(RecipeList);
hSplitContainer.AddChild(recipes);
// Right side
var guide = new VBoxContainer("Guide");
var info = new HBoxContainer("Info");
InfoIcon = new TextureRect("TextureRect");
InfoLabel = new Label("Label")
var guide = new VBoxContainer();
var info = new HBoxContainer();
InfoIcon = new TextureRect();
InfoLabel = new Label
{
SizeFlagsHorizontal = SizeFlags.FillExpand, SizeFlagsVertical = SizeFlags.ShrinkCenter
};
@@ -85,7 +73,7 @@ namespace Content.Client.Construction
info.AddChild(InfoLabel);
guide.AddChild(info);
var stepsLabel = new Label("Label")
var stepsLabel = new Label
{
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
SizeFlagsVertical = SizeFlags.ShrinkCenter,
@@ -93,14 +81,14 @@ namespace Content.Client.Construction
};
guide.AddChild(stepsLabel);
StepList = new ItemList("StepsList")
StepList = new ItemList
{
SizeFlagsVertical = SizeFlags.FillExpand, SelectMode = ItemList.ItemListSelectMode.None
};
guide.AddChild(StepList);
var buttonsContainer = new HBoxContainer("Buttons");
BuildButton = new Button("BuildButton")
var buttonsContainer = new HBoxContainer();
BuildButton = new Button
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
TextAlign = Button.AlignMode.Center,
@@ -108,7 +96,7 @@ namespace Content.Client.Construction
Disabled = true,
ToggleMode = false
};
EraseButton = new Button("EraseButton")
EraseButton = new Button
{
TextAlign = Button.AlignMode.Center, Text = "Clear Ghosts", ToggleMode = true
};
@@ -140,7 +128,7 @@ namespace Content.Client.Construction
void OnItemSelected()
{
var prototype = (ConstructionPrototype)RecipeList.Selected.Metadata;
var prototype = (ConstructionPrototype) RecipeList.Selected.Metadata;
if (prototype == null)
{
@@ -163,6 +151,7 @@ namespace Content.Client.Construction
{
continue;
}
Texture icon;
string text;
switch (forward)
@@ -171,47 +160,55 @@ namespace Content.Client.Construction
switch (mat.Material)
{
case ConstructionStepMaterial.MaterialType.Metal:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/sheet_metal.png");
icon = ResourceCache.GetResource<TextureResource>(
"/Textures/Objects/Materials/sheet_metal.png");
text = $"Metal x{mat.Amount}";
break;
case ConstructionStepMaterial.MaterialType.Glass:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/sheet_glass.png");
icon = ResourceCache.GetResource<TextureResource>(
"/Textures/Objects/Materials/sheet_glass.png");
text = $"Glass x{mat.Amount}";
break;
case ConstructionStepMaterial.MaterialType.Cable:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/cable_coil.png");
icon = ResourceCache.GetResource<TextureResource>(
"/Textures/Objects/Tools/cable_coil.png");
text = $"Cable Coil x{mat.Amount}";
break;
default:
throw new NotImplementedException();
}
break;
case ConstructionStepTool tool:
switch (tool.Tool)
{
case ConstructionStepTool.ToolType.Wrench:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/wrench.png");
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/Tools/wrench.png");
text = "Wrench";
break;
case ConstructionStepTool.ToolType.Crowbar:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/crowbar.png");
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/Tools/crowbar.png");
text = "Crowbar";
break;
case ConstructionStepTool.ToolType.Screwdriver:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/screwdriver.png");
icon = ResourceCache.GetResource<TextureResource>(
"/Textures/Objects/Tools/screwdriver.png");
text = "Screwdriver";
break;
case ConstructionStepTool.ToolType.Welder:
icon = ResourceCache.GetResource<RSIResource>("/Textures/Objects/tools.rsi").RSI["welder"].Frame0;
icon = ResourceCache.GetResource<RSIResource>("/Textures/Objects/tools.rsi")
.RSI["welder"].Frame0;
text = $"Welding tool ({tool.Amount} fuel)";
break;
case ConstructionStepTool.ToolType.Wirecutters:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/wirecutter.png");
icon = ResourceCache.GetResource<TextureResource>(
"/Textures/Objects/Tools/wirecutter.png");
text = "Wirecutters";
break;
default:
throw new NotImplementedException();
}
break;
default:
throw new NotImplementedException();
@@ -228,9 +225,9 @@ namespace Content.Client.Construction
PopulateTree(string.IsNullOrWhiteSpace(str) ? null : str.ToLowerInvariant());
}
void OnBuildPressed(Button.ButtonEventArgs args)
void OnBuildPressed(BaseButton.ButtonEventArgs args)
{
var prototype = (ConstructionPrototype)RecipeList.Selected.Metadata;
var prototype = (ConstructionPrototype) RecipeList.Selected.Metadata;
if (prototype == null)
{
return;
@@ -278,6 +275,7 @@ namespace Content.Client.Construction
subNode = new CategoryNode(category, currentNode);
currentNode.ChildCategories.Add(category, subNode);
}
currentNode = subNode;
}
@@ -334,7 +332,7 @@ namespace Content.Client.Construction
{
var found = false;
// TODO: don't run ToLowerInvariant() constantly.
if (prototype.Name.ToLowerInvariant().IndexOf(searchTerm) != -1)
if (prototype.Name.ToLowerInvariant().IndexOf(searchTerm, StringComparison.Ordinal) != -1)
{
found = true;
}
@@ -343,7 +341,7 @@ namespace Content.Client.Construction
foreach (var keyw in prototype.Keywords.Concat(prototype.CategorySegments))
{
// TODO: don't run ToLowerInvariant() constantly.
if (keyw.ToLowerInvariant().IndexOf(searchTerm) != -1)
if (keyw.ToLowerInvariant().IndexOf(searchTerm, StringComparison.Ordinal) != -1)
{
found = true;
break;
@@ -356,6 +354,7 @@ namespace Content.Client.Construction
continue;
}
}
var subItem = RecipeList.CreateItem(ItemForNode(node));
subItem.Text = prototype.Name;
subItem.Metadata = prototype;
@@ -370,14 +369,17 @@ namespace Content.Client.Construction
private static int ComparePrototype(ConstructionPrototype x, ConstructionPrototype y)
{
return x.Name.CompareTo(y.Name);
return String.Compare(x.Name, y.Name, StringComparison.Ordinal);
}
class CategoryNode
{
public readonly string Name;
public readonly CategoryNode Parent;
public SortedDictionary<string, CategoryNode> ChildCategories = new SortedDictionary<string, CategoryNode>();
public SortedDictionary<string, CategoryNode>
ChildCategories = new SortedDictionary<string, CategoryNode>();
public List<ConstructionPrototype> Prototypes = new List<ConstructionPrototype>();
public int FlattenedIndex = -1;

View File

@@ -1,14 +1,11 @@
using Content.Client.GameObjects.Components.Construction;
using Content.Shared.Construction;
using Robust.Client.Graphics;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Placement;
using Robust.Client.ResourceManagement;
using Robust.Client.Utility;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Content.Client.Construction
{
@@ -46,7 +43,6 @@ namespace Content.Client.Construction
{
base.StartHijack(manager);
var res = IoCManager.Resolve<IResourceCache>();
manager.CurrentBaseSprite = Prototype.Icon.DirFrame0();
}
}

View File

@@ -1,21 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<LangVersion>7.3</LangVersion>
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
<TargetFramework>$(TargetFramework)</TargetFramework>
<LangVersion>8</LangVersion>
<IsPackable>false</IsPackable>
<Platforms>x64;x86</Platforms>
<Platforms>x64</Platforms>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<OutputPath>..\bin\Content.Client\</OutputPath>
<OutputType Condition="'$(FullRelease)' != 'True'">Exe</OutputType>
</PropertyGroup>
<Import Project="..\RobustToolbox\MSBuild\Robust.DefineConstants.targets" />
<ItemGroup>
<PackageReference Include="Nett" Version="0.11.0" />
<PackageReference Include="Nett" Version="0.13.0" />
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0007" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-beta0006" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="YamlDotNet" Version="6.0.0" />
<PackageReference Include="SharpZipLib" Version="1.1.0" />
<PackageReference Include="YamlDotNet" Version="8.1.0" />
<PackageReference Include="SharpZipLib" Version="1.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RobustToolbox\Lidgren.Network\Lidgren.Network.csproj" />
@@ -24,4 +26,6 @@
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
</ItemGroup>
<Import Project="..\RobustToolbox\MSBuild\Robust.Engine.targets" />
<Target Name="ContentAfterBuild" DependsOnTargets="ClientAfterBuild" AfterTargets="Build" />
</Project>

View File

@@ -1,27 +1,32 @@
using System;
using Content.Client.Chat;
using Content.Client.GameObjects.Components.Actor;
using Content.Client.GameTicking;
using Content.Client.Input;
using Content.Client.Interfaces;
using Content.Client.Interfaces.Chat;
using Content.Client.Interfaces.Parallax;
using Content.Client.Parallax;
using Content.Client.Sandbox;
using Content.Client.State;
using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.Components.Cargo;
using Content.Shared.GameObjects.Components.Chemistry;
using Content.Shared.GameObjects.Components.Markers;
using Content.Shared.GameObjects.Components.Research;
using Content.Shared.Interfaces;
using Robust.Client;
using Content.Shared.GameObjects.Components.VendingMachines;
using Robust.Client.Interfaces;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.State;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Shared.ContentPack;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client
{
@@ -29,6 +34,8 @@ namespace Content.Client
{
#pragma warning disable 649
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IBaseClient _baseClient;
[Dependency] private readonly IStateManager _stateManager;
[Dependency] private readonly IEscapeMenuOwner _escapeMenuOwner;
#pragma warning restore 649
@@ -41,6 +48,10 @@ namespace Content.Client
var registerIgnore = new[]
{
"Wrenchable",
"AmmoBox",
"Breakable",
"Pickaxe",
"Interactable",
"Destructible",
"Temperature",
@@ -57,7 +68,6 @@ namespace Content.Client
"EmitSoundOnUse",
"FootstepModifier",
"HeatResistance",
"CombatMode",
"Teleportable",
"ItemTeleporter",
"Portal",
@@ -66,7 +76,6 @@ namespace Content.Client
"Wirecutter",
"Screwdriver",
"Multitool",
"Welder",
"Wrench",
"Crowbar",
"HitscanWeapon",
@@ -74,7 +83,6 @@ namespace Content.Client
"Projectile",
"MeleeWeapon",
"Storeable",
"Stack",
"Dice",
"Construction",
"Apc",
@@ -82,17 +90,46 @@ namespace Content.Client
"PoweredLight",
"Smes",
"Powercell",
"HandheldLight",
"LightBulb",
"Healing",
"Catwalk",
"BallisticMagazine",
"BallisticMagazineWeapon",
"BallisticBullet",
"HitscanWeaponCapacitor",
"PowerCell",
"WeaponCapacitorCharger",
"PowerCellCharger",
"AiController",
"PlayerInputMover",
"Computer",
"AsteroidRock",
"ResearchServer",
"ResearchPointSource",
"ResearchClient",
"IdCard",
"Access",
"AccessReader",
"IdCardConsole",
"Airlock",
"MedicalScanner",
"WirePlacer",
"Species",
"Drink",
"Food",
"DrinkFoodContainer",
"Stomach",
"Hunger",
"Thirst",
"Rotatable",
"MagicMirror",
"MedkitFill",
"FloorTile",
"FootstepSound",
"UtilityBeltClothingFill",
"ShuttleController",
"HumanInventoryController",
"UseDelay",
"Pourable"
};
foreach (var ignoreName in registerIgnore)
@@ -100,19 +137,22 @@ namespace Content.Client
factory.RegisterIgnore(ignoreName);
}
factory.Register<SharedResearchConsoleComponent>();
factory.Register<SharedLatheComponent>();
factory.Register<SharedSpawnPointComponent>();
factory.Register<SolutionComponent>();
prototypes.RegisterIgnore("material");
factory.Register<SharedVendingMachineComponent>();
factory.Register<SharedWiresComponent>();
factory.Register<SharedCargoConsoleComponent>();
factory.Register<SharedReagentDispenserComponent>();
prototypes.RegisterIgnore("material");
prototypes.RegisterIgnore("reaction"); //Chemical reactions only needed by server. Reactions checks are server-side.
ClientContentIoC.Register();
IoCManager.Register<IGameHud, GameHud>();
IoCManager.Register<IClientNotifyManager, ClientNotifyManager>();
IoCManager.Register<ISharedNotifyManager, ClientNotifyManager>();
IoCManager.Register<IClientGameTicker, ClientGameTicker>();
IoCManager.Register<IParallaxManager, ParallaxManager>();
IoCManager.Register<IChatManager, ChatManager>();
IoCManager.Register<IEscapeMenuOwner, EscapeMenuOwner>();
if (TestingCallbacks != null)
{
var cast = (ClientModuleTestingCallbacks) TestingCallbacks;
@@ -132,6 +172,16 @@ namespace Content.Client
IoCManager.InjectDependencies(this);
_escapeMenuOwner.Initialize();
_baseClient.PlayerJoinedGame += (sender, args) =>
{
_stateManager.RequestStateChange<GameScreen>();
};
_baseClient.PlayerJoinedServer += (sender, args) =>
{
IoCManager.Resolve<IMapManager>().CreateNewMapEntity(MapId.Nullspace);
};
}
/// <summary>
@@ -174,19 +224,21 @@ namespace Content.Client
IoCManager.Resolve<IClientGameTicker>().Initialize();
IoCManager.Resolve<IOverlayManager>().AddOverlay(new ParallaxOverlay());
IoCManager.Resolve<IChatManager>().Initialize();
IoCManager.Resolve<ISandboxManager>().Initialize();
IoCManager.Resolve<IClientPreferencesManager>().Initialize();
IoCManager.Resolve<IItemSlotManager>().Initialize();
}
public override void Update(ModUpdateLevel level, float frameTime)
public override void Update(ModUpdateLevel level, FrameEventArgs frameEventArgs)
{
base.Update(level, frameTime);
base.Update(level, frameEventArgs);
switch (level)
{
case ModUpdateLevel.FramePreEngine:
var renderFrameEventArgs = new RenderFrameEventArgs(frameTime);
IoCManager.Resolve<IClientNotifyManager>().FrameUpdate(renderFrameEventArgs);
IoCManager.Resolve<IClientGameTicker>().FrameUpdate(renderFrameEventArgs);
IoCManager.Resolve<IChatManager>().FrameUpdate(renderFrameEventArgs);
IoCManager.Resolve<IClientNotifyManager>().FrameUpdate(frameEventArgs);
IoCManager.Resolve<IClientGameTicker>().FrameUpdate(frameEventArgs);
IoCManager.Resolve<IChatManager>().FrameUpdate(frameEventArgs);
break;
}
}

View File

@@ -1,3 +1,4 @@
using Content.Client.State;
using Content.Client.UserInterface;
using Robust.Client.Console;
using Robust.Client.Interfaces.Input;
@@ -10,7 +11,6 @@ using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Players;
using Robust.Shared.Prototypes;
namespace Content.Client
@@ -49,9 +49,8 @@ namespace Content.Client
_escapeMenu.OnClose += () => _gameHud.EscapeButtonDown = false;
var escapeMenuCommand = InputCmdHandler.FromDelegate(Enabled);
_inputManager.SetInputCommand(EngineKeyFunctions.EscapeMenu, escapeMenuCommand);
_inputManager.SetInputCommand(EngineKeyFunctions.EscapeMenu,
InputCmdHandler.FromDelegate(s => Enabled()));
}
else if (obj.OldState is GameScreen)
{
@@ -63,7 +62,7 @@ namespace Content.Client
}
}
private void Enabled(ICommonSession session)
private void Enabled()
{
if (_escapeMenu.IsOpen)
{

View File

@@ -0,0 +1,53 @@
using System.Collections.Generic;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
using static Content.Shared.GameObjects.Components.Access.SharedIdCardConsoleComponent;
namespace Content.Client.GameObjects.Components.Access
{
public class IdCardConsoleBoundUserInterface : BoundUserInterface
{
#pragma warning disable 649
[Dependency] private readonly ILocalizationManager _localizationManager;
[Dependency] private readonly IPrototypeManager _prototypeManager;
#pragma warning restore 649
public IdCardConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
private IdCardConsoleWindow _window;
protected override void Open()
{
base.Open();
_window = new IdCardConsoleWindow(this, _localizationManager, _prototypeManager);
_window.Title = Owner.Owner.Name;
_window.OnClose += Close;
_window.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
var castState = (IdCardConsoleBoundUserInterfaceState) state;
_window.UpdateState(castState);
}
public void ButtonPressed(UiButton button)
{
SendMessage(new IdButtonPressedMessage(button));
}
public void SubmitData(string newFullName, string newJobTitle, List<string> newAccessList)
{
SendMessage(new WriteToTargetIdMessage(
newFullName,
newJobTitle,
newAccessList));
}
}
}

View File

@@ -0,0 +1,211 @@
// Only unused on .NET Core due to KeyValuePair.Deconstruct
// ReSharper disable once RedundantUsingDirective
using Robust.Shared.Utility;
using System.Collections.Generic;
using System.Linq;
using Content.Shared.Access;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using static Content.Shared.GameObjects.Components.Access.SharedIdCardConsoleComponent;
namespace Content.Client.GameObjects.Components.Access
{
public class IdCardConsoleWindow : SS14Window
{
private readonly Button _privilegedIdButton;
private readonly Button _targetIdButton;
private readonly Label _privilegedIdLabel;
private readonly Label _targetIdLabel;
private readonly Label _fullNameLabel;
private readonly LineEdit _fullNameLineEdit;
private readonly Label _jobTitleLabel;
private readonly LineEdit _jobTitleLineEdit;
private readonly Button _fullNameSaveButton;
private readonly Button _jobTitleSaveButton;
private readonly IdCardConsoleBoundUserInterface _owner;
private readonly ILocalizationManager _loc;
private readonly Dictionary<string, Button> _accessButtons = new Dictionary<string, Button>();
private string _lastFullName;
private string _lastJobTitle;
protected override Vector2? CustomSize => (650, 270);
public IdCardConsoleWindow(IdCardConsoleBoundUserInterface owner, ILocalizationManager loc, IPrototypeManager prototypeManager)
{
_loc = loc;
_owner = owner;
var vBox = new VBoxContainer();
vBox.AddChild(new GridContainer
{
Columns = 3,
Children =
{
new Label {Text = loc.GetString("Privileged ID:")},
(_privilegedIdButton = new Button()),
(_privilegedIdLabel = new Label()),
new Label {Text = loc.GetString("Target ID:")},
(_targetIdButton = new Button()),
(_targetIdLabel = new Label())
}
});
_privilegedIdButton.OnPressed += _ => _owner.ButtonPressed(UiButton.PrivilegedId);
_targetIdButton.OnPressed += _ => _owner.ButtonPressed(UiButton.TargetId);
// Separator
vBox.AddChild(new Control {CustomMinimumSize = (0, 8)});
// Name and job title line edits.
vBox.AddChild(new GridContainer
{
Columns = 3,
HSeparationOverride = 4,
Children =
{
// Name
(_fullNameLabel = new Label
{
Text = loc.GetString("Full name:")
}),
(_fullNameLineEdit = new LineEdit
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
}),
(_fullNameSaveButton = new Button
{
Text = loc.GetString("Save"),
Disabled = true
}),
// Title
(_jobTitleLabel = new Label
{
Text = loc.GetString("Job title:")
}),
(_jobTitleLineEdit = new LineEdit
{
SizeFlagsHorizontal = SizeFlags.FillExpand
}),
(_jobTitleSaveButton = new Button
{
Text = loc.GetString("Save"),
Disabled = true
}),
},
});
_fullNameLineEdit.OnTextEntered += _ => SubmitData();
_fullNameLineEdit.OnTextChanged += _ =>
{
_fullNameSaveButton.Disabled = _fullNameSaveButton.Text == _lastFullName;
};
_fullNameSaveButton.OnPressed += _ => SubmitData();
_jobTitleLineEdit.OnTextEntered += _ => SubmitData();
_jobTitleLineEdit.OnTextChanged += _ =>
{
_jobTitleSaveButton.Disabled = _jobTitleLineEdit.Text == _lastJobTitle;
};
_jobTitleSaveButton.OnPressed += _ => SubmitData();
// Separator
vBox.AddChild(new Control {CustomMinimumSize = (0, 8)});
{
var grid = new GridContainer
{
Columns = 5,
SizeFlagsHorizontal = SizeFlags.ShrinkCenter
};
vBox.AddChild(grid);
foreach (var accessLevel in prototypeManager.EnumeratePrototypes<AccessLevelPrototype>())
{
var newButton = new Button
{
Text = accessLevel.Name,
ToggleMode = true,
};
grid.AddChild(newButton);
_accessButtons.Add(accessLevel.ID, newButton);
newButton.OnPressed += _ => SubmitData();
}
}
Contents.AddChild(vBox);
}
public void UpdateState(IdCardConsoleBoundUserInterfaceState state)
{
_privilegedIdButton.Text = state.IsPrivilegedIdPresent
? _loc.GetString("Eject")
: _loc.GetString("Insert");
_privilegedIdLabel.Text = state.PrivilegedIdName;
_targetIdButton.Text = state.IsTargetIdPresent
? _loc.GetString("Eject")
: _loc.GetString("Insert");
_targetIdLabel.Text = state.TargetIdName;
var interfaceEnabled =
state.IsPrivilegedIdPresent && state.IsPrivilegedIdAuthorized && state.IsTargetIdPresent;
var fullNameDirty = _lastFullName != null && _fullNameLineEdit.Text != state.TargetIdFullName;
var jobTitleDirty = _lastJobTitle != null && _jobTitleLineEdit.Text != state.TargetIdJobTitle;
_fullNameLabel.Modulate = interfaceEnabled ? Color.White : Color.Gray;
_fullNameLineEdit.Editable = interfaceEnabled;
if (!fullNameDirty)
{
_fullNameLineEdit.Text = state.TargetIdFullName;
}
_fullNameSaveButton.Disabled = !interfaceEnabled || !fullNameDirty;
_jobTitleLabel.Modulate = interfaceEnabled ? Color.White : Color.Gray;
_jobTitleLineEdit.Editable = interfaceEnabled;
if (!jobTitleDirty)
{
_jobTitleLineEdit.Text = state.TargetIdJobTitle;
}
_jobTitleSaveButton.Disabled = !interfaceEnabled || !jobTitleDirty;
foreach (var (accessName, button) in _accessButtons)
{
button.Disabled = !interfaceEnabled;
if (interfaceEnabled)
{
button.Pressed = state.TargetIdAccessList.Contains(accessName);
}
}
_lastFullName = state.TargetIdFullName;
_lastJobTitle = state.TargetIdJobTitle;
}
private void SubmitData()
{
_owner.SubmitData(
_fullNameLineEdit.Text,
_jobTitleLineEdit.Text,
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
_accessButtons.Where(x => x.Value.Pressed).Select(x => x.Key).ToList());
}
}
}

View File

@@ -36,6 +36,8 @@ namespace Content.Client.GameObjects.Components.Actor
/// </remarks>
public SS14Window Window { get; private set; }
private List<ICharacterUI> _uiComponents;
/// <summary>
/// Create the window with all character UIs and bind it to a keypress
/// </summary>
@@ -44,13 +46,13 @@ namespace Content.Client.GameObjects.Components.Actor
base.Initialize();
//Use all the character ui interfaced components to create the character window
var uiComponents = Owner.GetAllComponents<ICharacterUI>().ToList();
if (uiComponents.Count == 0)
_uiComponents = Owner.GetAllComponents<ICharacterUI>().ToList();
if (_uiComponents.Count == 0)
{
return;
}
Window = new CharacterWindow(uiComponents);
Window = new CharacterWindow(_uiComponents);
Window.OnClose += () => _gameHud.CharacterButtonDown = false;
}
@@ -61,7 +63,15 @@ namespace Content.Client.GameObjects.Components.Actor
{
base.OnRemove();
Window?.Dispose();
foreach (var component in _uiComponents)
{
// Make sure these don't get deleted when the window is disposed.
component.Scene.Orphan();
}
_uiComponents = null;
Window?.Close();
Window = null;
var inputMgr = IoCManager.Resolve<IInputManager>();
@@ -124,8 +134,6 @@ namespace Content.Client.GameObjects.Components.Actor
{
_contentsVBox.AddChild(element.Scene);
}
Size = CombinedMinimumSize;
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using Robust.Client.Animations;
using Robust.Client.GameObjects.Components.Animations;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Animations;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components
{
[RegisterComponent]
public sealed class AnimationsTestComponent : Component
{
public override string Name => "AnimationsTest";
public override void Initialize()
{
base.Initialize();
var animations = Owner.GetComponent<AnimationPlayerComponent>();
animations.Play(new Animation
{
Length = TimeSpan.FromSeconds(20),
AnimationTracks =
{
new AnimationTrackComponentProperty
{
ComponentType = typeof(ITransformComponent),
Property = nameof(ITransformComponent.LocalRotation),
InterpolationMode = AnimationInterpolationMode.Linear,
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(Angle.Zero, 0),
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(1440), 20)
}
},
new AnimationTrackComponentProperty
{
ComponentType = typeof(ISpriteComponent),
Property = "layer/0/texture",
KeyFrames =
{
new AnimationTrackProperty.KeyFrame("Objects/toolbox_r.png", 0),
new AnimationTrackProperty.KeyFrame("Objects/Toolbox_b.png", 5),
new AnimationTrackProperty.KeyFrame("Objects/Toolbox_y.png", 5),
new AnimationTrackProperty.KeyFrame("Objects/toolbox_r.png", 5),
}
}
}
}, "yes");
}
}
}

View File

@@ -0,0 +1,132 @@
using Content.Client.UserInterface.Cargo;
using Content.Shared.GameObjects.Components.Cargo;
using Content.Shared.Prototypes.Cargo;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.Cargo
{
public class CargoConsoleBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private CargoConsoleMenu _menu;
[ViewVariables]
private CargoConsoleOrderMenu _orderMenu;
[ViewVariables]
public GalacticMarketComponent Market { get; private set; }
[ViewVariables]
public CargoOrderDatabaseComponent Orders { get; private set; }
[ViewVariables]
public bool RequestOnly { get; private set; }
[ViewVariables]
public int BankId { get; private set; }
[ViewVariables]
public string BankName { get; private set; }
[ViewVariables]
public int BankBalance { get; private set; }
private CargoProductPrototype _product;
public CargoConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
if (!Owner.Owner.TryGetComponent(out GalacticMarketComponent market)
|| !Owner.Owner.TryGetComponent(out CargoOrderDatabaseComponent orders)) return;
Market = market;
Orders = orders;
_menu = new CargoConsoleMenu(this);
_orderMenu = new CargoConsoleOrderMenu();
_menu.OnClose += Close;
_menu.Populate();
Market.OnDatabaseUpdated += _menu.PopulateProducts;
Market.OnDatabaseUpdated += _menu.PopulateCategories;
Orders.OnDatabaseUpdated += _menu.PopulateOrders;
_menu.CallShuttleButton.OnPressed += (args) =>
{
SendMessage(new SharedCargoConsoleComponent.CargoConsoleShuttleMessage());
};
_menu.OnItemSelected += (args) =>
{
if (!(args.Button.Parent is CargoProductRow row))
return;
_product = row.Product;
_orderMenu.Requester.Text = null;
_orderMenu.Reason.Text = null;
_orderMenu.Amount.Value = 1;
_orderMenu.OpenCenteredMinSize();
};
_menu.OnOrderApproved += ApproveOrder;
_menu.OnOrderCanceled += RemoveOrder;
_orderMenu.SubmitButton.OnPressed += (args) =>
{
AddOrder();
_orderMenu.Close();
};
_menu.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (!(state is CargoConsoleInterfaceState cstate))
return;
if (RequestOnly != cstate.RequestOnly)
{
RequestOnly = cstate.RequestOnly;
_menu.UpdateRequestOnly();
}
BankId = cstate.BankId;
BankName = cstate.BankName;
BankBalance = cstate.BankBalance;
_menu.UpdateBankData();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
Market.OnDatabaseUpdated -= _menu.PopulateProducts;
Market.OnDatabaseUpdated -= _menu.PopulateCategories;
Orders.OnDatabaseUpdated -= _menu.PopulateOrders;
_menu?.Dispose();
_orderMenu?.Dispose();
}
internal void AddOrder()
{
SendMessage(new SharedCargoConsoleComponent.CargoConsoleAddOrderMessage(_orderMenu.Requester.Text,
_orderMenu.Reason.Text, _product.ID, _orderMenu.Amount.Value));
}
internal void RemoveOrder(BaseButton.ButtonEventArgs args)
{
if (!(args.Button.Parent.Parent is CargoOrderRow row))
return;
SendMessage(new SharedCargoConsoleComponent.CargoConsoleRemoveOrderMessage(row.Order.OrderNumber));
}
internal void ApproveOrder(BaseButton.ButtonEventArgs args)
{
if (!(args.Button.Parent.Parent is CargoOrderRow row))
return;
SendMessage(new SharedCargoConsoleComponent.CargoConsoleApproveOrderMessage(row.Order.OrderNumber));
}
}
}

View File

@@ -0,0 +1,56 @@
using Content.Shared.GameObjects.Components.Cargo;
using Content.Shared.Prototypes.Cargo;
using Robust.Shared.GameObjects;
using System;
using System.Collections.Generic;
namespace Content.Client.GameObjects.Components.Cargo
{
[RegisterComponent]
public class CargoOrderDatabaseComponent : SharedCargoOrderDatabaseComponent
{
private List<CargoOrderData> _orders = new List<CargoOrderData>();
public IReadOnlyList<CargoOrderData> Orders => _orders;
/// <summary>
/// Event called when the database is updated.
/// </summary>
public event Action OnDatabaseUpdated;
// TODO add account selector menu
/// <summary>
/// Removes all orders from the database.
/// </summary>
public virtual void Clear()
{
_orders.Clear();
}
/// <summary>
/// Adds an order to the database.
/// </summary>
/// <param name="order">The order to be added.</param>
public virtual void AddOrder(CargoOrderData order)
{
if (!_orders.Contains(order))
_orders.Add(order);
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if (!(curState is CargoOrderDatabaseState state))
return;
Clear();
if (state.Orders == null)
return;
foreach (var order in state.Orders)
{
AddOrder(order);
}
OnDatabaseUpdated?.Invoke();
}
}
}

View File

@@ -0,0 +1,38 @@
using Content.Shared.GameObjects.Components.Cargo;
using Content.Shared.Prototypes.Cargo;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using System;
namespace Content.Client.GameObjects.Components.Cargo
{
[RegisterComponent]
public class GalacticMarketComponent : SharedGalacticMarketComponent
{
#pragma warning disable CS0649
[Dependency] private IPrototypeManager _prototypeManager;
#pragma warning restore
/// <summary>
/// Event called when the database is updated.
/// </summary>
public event Action OnDatabaseUpdated;
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if (!(curState is GalacticMarketState state))
return;
_products.Clear();
foreach (var productId in state.Products)
{
if (!_prototypeManager.TryIndex(productId, out CargoProductPrototype product))
continue;
_products.Add(product);
}
OnDatabaseUpdated?.Invoke();
}
}
}

View File

@@ -0,0 +1,123 @@
using System.Collections.Generic;
using System.Linq;
using Content.Shared.GameObjects.Components.Chemistry;
using JetBrains.Annotations;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using static Content.Shared.GameObjects.Components.Chemistry.SharedReagentDispenserComponent;
namespace Content.Client.GameObjects.Components.Chemistry
{
/// <summary>
/// Initializes a <see cref="ReagentDispenserWindow"/> and updates it when new server messages are received.
/// </summary>
[UsedImplicitly]
public class ReagentDispenserBoundUserInterface : BoundUserInterface
{
#pragma warning disable 649
[Dependency] private readonly ILocalizationManager _localizationManager;
#pragma warning restore 649
private ReagentDispenserWindow _window;
private ReagentDispenserBoundUserInterfaceState _lastState;
public ReagentDispenserBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
/// <summary>
/// Called each time a dispenser UI instance is opened. Generates the dispenser window and fills it with
/// relevant info. Sets the actions for static buttons.
/// <para>Buttons which can change like reagent dispense buttons have their actions set in <see cref="UpdateReagentsList"/>.</para>
/// </summary>
protected override void Open()
{
base.Open();
//Setup window layout/elements
_window = new ReagentDispenserWindow
{
Title = _localizationManager.GetString("Reagent dispenser"),
};
_window.OpenCenteredMinSize();
_window.OnClose += Close;
//Setup static button actions.
_window.EjectButton.OnPressed += _ => ButtonPressed(UiButton.Eject);
_window.ClearButton.OnPressed += _ => ButtonPressed(UiButton.Clear);
_window.DispenseButton1.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount1);
_window.DispenseButton5.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount5);
_window.DispenseButton10.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount10);
_window.DispenseButton25.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount25);
_window.DispenseButton50.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount50);
_window.DispenseButton100.OnPressed += _ => ButtonPressed(UiButton.SetDispenseAmount100);
}
/// <summary>
/// Update the ui each time new state data is sent from the server.
/// </summary>
/// <param name="state">
/// Data of the <see cref="SharedReagentDispenserComponent"/> that this ui represents.
/// Sent from the server.
/// </param>
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
var castState = (ReagentDispenserBoundUserInterfaceState)state;
_lastState = castState;
_window?.UpdateState(castState); //Update window state
UpdateReagentsList(castState.Inventory); //Update reagents list & reagent button actions
}
/// <summary>
/// Update the list of reagents that this dispenser can dispense on the UI.
/// </summary>
/// <param name="inventory">A list of the reagents which can be dispensed.</param>
private void UpdateReagentsList(List<ReagentDispenserInventoryEntry> inventory)
{
_window.UpdateReagentsList(inventory);
for (int i = 0; i < _window.ChemicalList.Children.Count(); i++)
{
var button = (Button)_window.ChemicalList.Children.ElementAt(i);
var i1 = i;
button.OnPressed += _ => ButtonPressed(UiButton.Dispense, i1);
button.OnMouseEntered += _ =>
{
if (_lastState != null)
{
_window.UpdateContainerInfo(_lastState, inventory[i1].ID);
}
};
button.OnMouseExited += _ =>
{
if (_lastState != null)
{
_window.UpdateContainerInfo(_lastState);
}
};
}
}
private void ButtonPressed(UiButton button, int dispenseIndex = -1)
{
SendMessage(new UiButtonPressedMessage(button, dispenseIndex));
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_window.Dispose();
}
}
}
}

View File

@@ -0,0 +1,278 @@
using System.Collections.Generic;
using Content.Client.UserInterface;
using Content.Shared.Chemistry;
using Content.Shared.GameObjects.Components.Chemistry;
using Robust.Client.Graphics.Drawing;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using static Content.Shared.GameObjects.Components.Chemistry.SharedReagentDispenserComponent;
namespace Content.Client.GameObjects.Components.Chemistry
{
/// <summary>
/// Client-side UI used to control a <see cref="SharedReagentDispenserComponent"/>
/// </summary>
public class ReagentDispenserWindow : SS14Window
{
/// <summary>Contains info about the reagent container such as it's contents, if one is loaded into the dispenser.</summary>
private readonly VBoxContainer ContainerInfo;
/// <summary>Sets the dispense amount to 1 when pressed.</summary>
public Button DispenseButton1 { get; }
/// <summary>Sets the dispense amount to 5 when pressed.</summary>
public Button DispenseButton5 { get; }
/// <summary>Sets the dispense amount to 10 when pressed.</summary>
public Button DispenseButton10 { get; }
/// <summary>Sets the dispense amount to 25 when pressed.</summary>
public Button DispenseButton25 { get; }
/// <summary>Sets the dispense amount to 50 when pressed.</summary>
public Button DispenseButton50 { get; }
/// <summary>Sets the dispense amount to 100 when pressed.</summary>
public Button DispenseButton100 { get; }
/// <summary>Ejects the reagent container from the dispenser.</summary>
public Button ClearButton { get; }
/// <summary>Removes all reagents from the reagent container.</summary>
public Button EjectButton { get; }
/// <summary>A grid of buttons for each reagent which can be dispensed.</summary>
public GridContainer ChemicalList { get; }
#pragma warning disable 649
[Dependency] private readonly IPrototypeManager _prototypeManager;
[Dependency] private readonly ILocalizationManager _localizationManager;
#pragma warning restore 649
protected override Vector2? CustomSize => (500, 600);
/// <summary>
/// Create and initialize the dispenser UI client-side. Creates the basic layout,
/// actual data isn't filled in until the server sends data about the dispenser.
/// </summary>
public ReagentDispenserWindow()
{
IoCManager.InjectDependencies(this);
var dispenseAmountGroup = new ButtonGroup();
Contents.AddChild(new VBoxContainer
{
Children =
{
//First, our dispense amount buttons
new HBoxContainer
{
Children =
{
new Label {Text = _localizationManager.GetString("Amount")},
//Padding
new Control {CustomMinimumSize = (20, 0)},
(DispenseButton1 = new Button {Text = "1", Group = dispenseAmountGroup}),
(DispenseButton5 = new Button {Text = "5", Group = dispenseAmountGroup}),
(DispenseButton10 = new Button {Text = "10", Group = dispenseAmountGroup}),
(DispenseButton25 = new Button {Text = "25", Group = dispenseAmountGroup}),
(DispenseButton50 = new Button {Text = "50", Group = dispenseAmountGroup}),
(DispenseButton100 = new Button {Text = "100", Group = dispenseAmountGroup}),
}
},
//Padding
new Control {CustomMinimumSize = (0.0f, 10.0f)},
//Grid of which reagents can be dispensed.
(ChemicalList = new GridContainer
{
Columns = 5
}),
//Padding
new Control {CustomMinimumSize = (0.0f, 10.0f)},
new HBoxContainer
{
Children =
{
new Label {Text = _localizationManager.GetString("Container: ")},
(ClearButton = new Button {Text = _localizationManager.GetString("Clear")}),
(EjectButton = new Button {Text = _localizationManager.GetString("Eject")})
}
},
//Wrap the container info in a PanelContainer so we can color it's background differently.
new PanelContainer
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 6,
CustomMinimumSize = (0, 150),
PanelOverride = new StyleBoxFlat
{
BackgroundColor = new Color(27, 27, 30)
},
Children =
{
//Currently empty, when server sends state data this will have container contents and fill volume.
(ContainerInfo = new VBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
new Label
{
Text = _localizationManager.GetString("No container loaded.")
}
}
}),
}
},
}
});
}
/// <summary>
/// Update the button grid of reagents which can be dispensed.
/// <para>The actions for these buttons are set in <see cref="ReagentDispenserBoundUserInterface.UpdateReagentsList"/>.</para>
/// </summary>
/// <param name="inventory">Reagents which can be dispensed by this dispenser</param>
public void UpdateReagentsList(List<ReagentDispenserInventoryEntry> inventory)
{
if (ChemicalList == null) return;
if (inventory == null) return;
ChemicalList.Children.Clear();
foreach (var entry in inventory)
{
if (_prototypeManager.TryIndex(entry.ID, out ReagentPrototype proto))
{
ChemicalList.AddChild(new Button {Text = proto.Name});
}
else
{
ChemicalList.AddChild(new Button {Text = _localizationManager.GetString("Reagent name not found")});
}
}
}
/// <summary>
/// Update the UI state when new state data is received from the server.
/// </summary>
/// <param name="state">State data sent by the server.</param>
public void UpdateState(BoundUserInterfaceState state)
{
var castState = (ReagentDispenserBoundUserInterfaceState) state;
Title = castState.DispenserName;
UpdateContainerInfo(castState);
switch (castState.SelectedDispenseAmount)
{
case 1:
DispenseButton1.Pressed = true;
break;
case 5:
DispenseButton5.Pressed = true;
break;
case 10:
DispenseButton10.Pressed = true;
break;
case 25:
DispenseButton25.Pressed = true;
break;
case 50:
DispenseButton50.Pressed = true;
break;
case 100:
DispenseButton100.Pressed = true;
break;
}
}
/// <summary>
/// Update the fill state and list of reagents held by the current reagent container, if applicable.
/// <para>Also highlights a reagent if it's dispense button is being mouse hovered.</para>
/// </summary>
/// <param name="state">State data for the dispenser.</param>
/// <param name="highlightedReagentId">Prototype id of the reagent whose dispense button is currently being mouse hovered.</param>
public void UpdateContainerInfo(ReagentDispenserBoundUserInterfaceState state,
string highlightedReagentId = null)
{
ContainerInfo.Children.Clear();
if (!state.HasBeaker)
{
ContainerInfo.Children.Add(new Label {Text = _localizationManager.GetString("No container loaded.")});
return;
}
ContainerInfo.Children.Add(new HBoxContainer // Name of the container and its fill status (Ex: 44/100u)
{
Children =
{
new Label {Text = $"{state.ContainerName}: "},
new Label
{
Text = $"{state.BeakerCurrentVolume}/{state.BeakerMaxVolume}",
StyleClasses = {NanoStyle.StyleClassLabelSecondaryColor}
}
}
});
if (state.ContainerReagents == null)
{
return;
}
foreach (var reagent in state.ContainerReagents)
{
var name = _localizationManager.GetString("Unknown reagent");
//Try to the prototype for the given reagent. This gives us it's name.
if (_prototypeManager.TryIndex(reagent.ReagentId, out ReagentPrototype proto))
{
name = proto.Name;
}
//Check if the reagent is being moused over. If so, color it green.
if (proto != null && proto.ID == highlightedReagentId)
{
ContainerInfo.Children.Add(new HBoxContainer
{
Children =
{
new Label
{
Text = $"{name}: ",
StyleClasses = {NanoStyle.StyleClassPowerStateGood}
},
new Label
{
Text = $"{reagent.Quantity}u",
StyleClasses = {NanoStyle.StyleClassPowerStateGood}
}
}
});
}
else //Otherwise, color it the normal colors.
{
ContainerInfo.Children.Add(new HBoxContainer
{
Children =
{
new Label {Text = $"{name}: "},
new Label
{
Text = $"{reagent.Quantity}u",
StyleClasses = {NanoStyle.StyleClassLabelSecondaryColor}
}
}
});
}
}
}
}
}

View File

@@ -1,9 +1,9 @@
using System;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.GameObjects.Components.Items;
using Robust.Client.Graphics;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.Clothing
@@ -12,13 +12,27 @@ namespace Content.Client.GameObjects.Components.Clothing
[ComponentReference(typeof(ItemComponent))]
public class ClothingComponent : ItemComponent
{
private FemaleClothingMask _femaleMask;
public override string Name => "Clothing";
public override uint? NetID => ContentNetIDs.CLOTHING;
public override Type StateType => typeof(ClothingComponentState);
[ViewVariables(VVAccess.ReadWrite)]
public string ClothingEquippedPrefix { get; set; }
[ViewVariables(VVAccess.ReadWrite)]
public FemaleClothingMask FemaleMask
{
get => _femaleMask;
set => _femaleMask = value;
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _femaleMask, "femaleMask", FemaleClothingMask.UniformFull);
}
public (RSI rsi, RSI.StateId stateId)? GetEquippedStateInfo(EquipmentSlotDefines.SlotFlags slot)
{
if (RsiPath == null)
@@ -47,4 +61,11 @@ namespace Content.Client.GameObjects.Components.Clothing
EquippedPrefix = clothingComponentState.EquippedPrefix;
}
}
public enum FemaleClothingMask
{
NoMask = 0,
UniformFull,
UniformTop
}
}

View File

@@ -0,0 +1,76 @@
using Content.Shared.GameObjects.Components;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Content.Client.GameObjects.Components
{
public sealed class ComputerVisualizer2D : AppearanceVisualizer
{
private string KeyboardState = "generic_key";
private string ScreenState = "generic";
public override void LoadData(YamlMappingNode node)
{
base.LoadData(node);
if (node.TryGetNode("key", out var scalar))
{
KeyboardState = scalar.AsString();
}
if (node.TryGetNode("screen", out scalar))
{
ScreenState = scalar.AsString();
}
}
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
var sprite = entity.GetComponent<ISpriteComponent>();
sprite.LayerSetState(Layers.Screen, ScreenState);
sprite.LayerSetState(Layers.Keyboard, $"{KeyboardState}_off");
sprite.LayerSetState(Layers.KeyboardOn, KeyboardState);
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (!component.TryGetData(ComputerVisuals.Powered, out bool powered))
{
powered = true;
}
component.TryGetData(ComputerVisuals.Broken, out bool broken);
if (broken)
{
sprite.LayerSetState(Layers.Body, "broken");
sprite.LayerSetState(Layers.Screen, "computer_broken");
}
else
{
sprite.LayerSetState(Layers.Body, "computer");
sprite.LayerSetState(Layers.Screen, ScreenState);
}
sprite.LayerSetVisible(Layers.Screen, powered);
sprite.LayerSetVisible(Layers.KeyboardOn, powered);
}
public enum Layers
{
Body,
Screen,
Keyboard,
KeyboardOn
}
}
}

View File

@@ -1,7 +1,5 @@
using Content.Shared.Construction;
using Content.Shared.Construction;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.Construction
@@ -14,18 +12,5 @@ namespace Content.Client.GameObjects.Components.Construction
[ViewVariables] public ConstructionPrototype Prototype { get; set; }
[ViewVariables] public ConstructorComponent Master { get; set; }
[ViewVariables] public int GhostID { get; set; }
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case ClientEntityClickMsg clickMsg:
Master.TryStartConstruction(GhostID);
break;
}
}
}
}

View File

@@ -78,7 +78,7 @@ namespace Content.Client.GameObjects.Components.Construction
public void SpawnGhost(ConstructionPrototype prototype, GridCoordinates loc, Direction dir)
{
var entMgr = IoCManager.Resolve<IClientEntityManager>();
var ghost = entMgr.SpawnEntityAt("constructionghost", loc);
var ghost = entMgr.SpawnEntity("constructionghost", loc);
var comp = ghost.GetComponent<ConstructionGhostComponent>();
comp.Prototype = prototype;
comp.Master = this;

View File

@@ -20,9 +20,8 @@ namespace Content.Client.GameObjects
{
base.HandleComponentState(curState, nextState);
if(curState is DamageComponentState)
if(curState is DamageComponentState damagestate)
{
var damagestate = (DamageComponentState)curState;
CurrentDamage = damagestate.CurrentDamage;
}
}

View File

@@ -1,4 +1,5 @@
using System;
using Content.Client.GameObjects.Components.Wires;
using Content.Shared.GameObjects.Components.Doors;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
@@ -16,6 +17,7 @@ namespace Content.Client.GameObjects.Components.Doors
private Animation CloseAnimation;
private Animation OpenAnimation;
private Animation DenyAnimation;
public override void LoadData(YamlMappingNode node)
{
@@ -23,6 +25,7 @@ namespace Content.Client.GameObjects.Components.Doors
var openSound = node.GetNode("open_sound").AsString();
var closeSound = node.GetNode("close_sound").AsString();
var denySound = node.GetNode("deny_sound").AsString();
CloseAnimation = new Animation {Length = TimeSpan.FromSeconds(1.2f)};
{
@@ -36,6 +39,11 @@ namespace Content.Client.GameObjects.Components.Doors
flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit;
flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("closing_unlit", 0f));
var flickMaintenancePanel = new AnimationTrackSpriteFlick();
CloseAnimation.AnimationTracks.Add(flickMaintenancePanel);
flickMaintenancePanel.LayerKey = WiresVisualizer2D.WiresVisualLayers.MaintenancePanel;
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_closing", 0f));
var sound = new AnimationTrackPlaySound();
CloseAnimation.AnimationTracks.Add(sound);
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(closeSound, 0));
@@ -53,10 +61,27 @@ namespace Content.Client.GameObjects.Components.Doors
flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit;
flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("opening_unlit", 0f));
var flickMaintenancePanel = new AnimationTrackSpriteFlick();
OpenAnimation.AnimationTracks.Add(flickMaintenancePanel);
flickMaintenancePanel.LayerKey = WiresVisualizer2D.WiresVisualLayers.MaintenancePanel;
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_opening", 0f));
var sound = new AnimationTrackPlaySound();
OpenAnimation.AnimationTracks.Add(sound);
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(openSound, 0));
}
DenyAnimation = new Animation {Length = TimeSpan.FromSeconds(0.45f)};
{
var flick = new AnimationTrackSpriteFlick();
DenyAnimation.AnimationTracks.Add(flick);
flick.LayerKey = DoorVisualLayers.Base;
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("deny", 0f));
var sound = new AnimationTrackPlaySound();
DenyAnimation.AnimationTracks.Add(sound);
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(denySound, 0));
}
}
public override void InitializeEntity(IEntity entity)
@@ -76,22 +101,21 @@ namespace Content.Client.GameObjects.Components.Doors
state = DoorVisualState.Closed;
}
var unlitVisible = true;
switch (state)
{
case DoorVisualState.Closed:
sprite.LayerSetState(DoorVisualLayers.Base, "closed");
sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "closed_unlit");
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, true);
sprite.LayerSetState(WiresVisualizer2D.WiresVisualLayers.MaintenancePanel, "panel_open");
break;
case DoorVisualState.Closing:
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, true);
if (!animPlayer.HasRunningAnimation(AnimationKey))
{
animPlayer.Play(CloseAnimation, AnimationKey);
}
break;
case DoorVisualState.Opening:
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, true);
if (!animPlayer.HasRunningAnimation(AnimationKey))
{
animPlayer.Play(OpenAnimation, AnimationKey);
@@ -100,11 +124,25 @@ namespace Content.Client.GameObjects.Components.Doors
break;
case DoorVisualState.Open:
sprite.LayerSetState(DoorVisualLayers.Base, "open");
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, false);
unlitVisible = false;
break;
case DoorVisualState.Deny:
unlitVisible = false;
if (!animPlayer.HasRunningAnimation(AnimationKey))
{
animPlayer.Play(DenyAnimation, AnimationKey);
}
break;
default:
throw new ArgumentOutOfRangeException();
}
if (component.TryGetData(DoorVisuals.Powered, out bool powered) && !powered)
{
unlitVisible = false;
}
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, unlitVisible);
}
}

View File

@@ -1,14 +1,17 @@
using System.Collections.Generic;
// Only unused on .NET Core due to KeyValuePair.Deconstruct
// ReSharper disable once RedundantUsingDirective
using Robust.Shared.Utility;
using System.Collections.Generic;
using System.Linq;
using Content.Client.GameObjects.Components.Clothing;
using Content.Shared.GameObjects;
using Content.Shared.Preferences.Appearance;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
using static Content.Shared.GameObjects.SharedInventoryComponent.ClientInventoryMessage;
@@ -28,10 +31,16 @@ namespace Content.Client.GameObjects
private ISpriteComponent _sprite;
private bool _playerAttached = false;
public override void OnRemove()
{
base.OnRemove();
if (_playerAttached)
{
InterfaceController?.PlayerDetached();
}
InterfaceController?.Dispose();
}
@@ -77,15 +86,12 @@ namespace Content.Client.GameObjects
foreach (var (slot, entityUid) in cast.Entities)
{
if (_slots.ContainsKey(slot))
{
_slots.Remove(slot);
_clearSlot(slot);
}
var entity = Owner.EntityManager.GetEntity(entityUid);
_slots[slot] = entity;
_setSlot(slot, entity);
if (!_slots.ContainsKey(slot) || _slots[slot] != entity)
{
_slots[slot] = entity;
_setSlot(slot, entity);
}
doneSlots.Add(slot);
}
@@ -101,24 +107,55 @@ namespace Content.Client.GameObjects
private void _setSlot(Slots slot, IEntity entity)
{
if (_sprite != null && entity.TryGetComponent(out ClothingComponent clothing))
SetSlotVisuals(slot, entity);
InterfaceController?.AddToSlot(slot, entity);
}
internal void SetSlotVisuals(Slots slot, IEntity entity)
{
if (_sprite == null)
{
return;
}
if (entity != null && entity.TryGetComponent(out ClothingComponent clothing))
{
var flag = SlotMasks[slot];
var data = clothing.GetEquippedStateInfo(flag);
if (data == null)
{
_sprite.LayerSetVisible(slot, false);
}
else
if (data != null)
{
var (rsi, state) = data.Value;
_sprite.LayerSetVisible(slot, true);
_sprite.LayerSetRSI(slot, rsi);
_sprite.LayerSetState(slot, state);
if (slot == Slots.INNERCLOTHING)
{
_sprite.LayerSetState(HumanoidVisualLayers.StencilMask, clothing.FemaleMask switch
{
FemaleClothingMask.NoMask => "female_none",
FemaleClothingMask.UniformTop => "female_top",
_ => "female_full",
});
}
return;
}
}
InterfaceController?.AddToSlot(slot, entity);
_sprite.LayerSetVisible(slot, false);
}
internal void ClearAllSlotVisuals()
{
foreach (var slot in InventoryInstance.SlotMasks)
{
if (slot != Slots.NONE)
{
_sprite.LayerSetVisible(slot, false);
}
}
}
private void _clearSlot(Slots slot)
@@ -127,18 +164,23 @@ namespace Content.Client.GameObjects
_sprite?.LayerSetVisible(slot, false);
}
public void SendUnequipMessage(Slots slot)
{
var unequipmessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Unequip);
SendNetworkMessage(unequipmessage);
}
public void SendEquipMessage(Slots slot)
{
var equipmessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Equip);
SendNetworkMessage(equipmessage);
}
public void SendUseMessage(Slots slot)
{
var equipmessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Use);
SendNetworkMessage(equipmessage);
}
public void SendOpenStorageUIMessage(Slots slot)
{
SendNetworkMessage(new OpenSlotStorageUIMessage(slot));
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
IComponent component = null)
{
@@ -148,12 +190,19 @@ namespace Content.Client.GameObjects
{
case PlayerAttachedMsg _:
InterfaceController.PlayerAttached();
_playerAttached = true;
break;
case PlayerDetachedMsg _:
InterfaceController.PlayerDetached();
_playerAttached = false;
break;
}
}
public bool TryGetSlot(Slots slot, out IEntity item)
{
return _slots.TryGetValue(slot, out item);
}
}
}

View File

@@ -1,7 +1,7 @@
using System.Collections.Generic;
// Only unused on .NET Core due to KeyValuePair.Deconstruct
// ReSharper disable once RedundantUsingDirective
using Content.Client.Utility;
using JetBrains.Annotations;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
@@ -10,8 +10,9 @@ using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
using Content.Client.UserInterface;
using System.Collections.Generic;
namespace Content.Client.GameObjects
{
@@ -22,15 +23,17 @@ namespace Content.Client.GameObjects
#pragma warning disable 649
[Dependency] private readonly ILocalizationManager _loc;
[Dependency] private readonly IResourceCache _resourceCache;
[Dependency] private readonly IItemSlotManager _itemSlotManager;
#pragma warning restore 649
private readonly Dictionary<Slots, List<InventoryButton>> _inventoryButtons
= new Dictionary<Slots, List<InventoryButton>>();
private readonly Dictionary<Slots, List<ItemSlotButton>> _inventoryButtons
= new Dictionary<Slots, List<ItemSlotButton>>();
private InventoryButton _hudButtonPocket1;
private InventoryButton _hudButtonPocket2;
private InventoryButton _hudButtonBelt;
private InventoryButton _hudButtonBack;
private ItemSlotButton _hudButtonPocket1;
private ItemSlotButton _hudButtonPocket2;
private ItemSlotButton _hudButtonBelt;
private ItemSlotButton _hudButtonBack;
private ItemSlotButton _hudButtonId;
private Control _quickButtonsContainer;
public HumanInventoryInterfaceController(ClientInventoryComponent owner) : base(owner)
@@ -42,19 +45,22 @@ namespace Content.Client.GameObjects
base.Initialize();
_window = new HumanInventoryWindow(_loc, _resourceCache);
_window.OnClose += () => _gameHud.InventoryButtonDown = false;
foreach (var (slot, button) in _window.Buttons)
{
button.OnPressed = AddToInventory;
_inventoryButtons.Add(slot, new List<InventoryButton> {button});
button.OnPressed = (e) => AddToInventory(e, slot);
button.OnStoragePressed = (e) => OpenStorage(e, slot);
_inventoryButtons.Add(slot, new List<ItemSlotButton> {button});
}
void AddButton(out InventoryButton variable, Slots slot, string textureName)
void AddButton(out ItemSlotButton variable, Slots slot, string textureName)
{
var texture = _resourceCache.GetTexture($"/Textures/UserInterface/Inventory/{textureName}.png");
variable = new InventoryButton(slot, texture)
var storageTexture = _resourceCache.GetTexture("/Textures/UserInterface/Inventory/back.png");
variable = new ItemSlotButton(texture, storageTexture)
{
OnPressed = AddToInventory
OnPressed = (e) => AddToInventory(e, slot),
OnStoragePressed = (e) => OpenStorage(e, slot)
};
_inventoryButtons[slot].Add(variable);
}
@@ -63,11 +69,13 @@ namespace Content.Client.GameObjects
AddButton(out _hudButtonPocket2, Slots.POCKET2, "pocket");
AddButton(out _hudButtonBack, Slots.BACKPACK, "back");
AddButton(out _hudButtonBelt, Slots.BELT, "belt");
AddButton(out _hudButtonId, Slots.IDCARD, "id");
_quickButtonsContainer = new HBoxContainer
{
Children =
{
_hudButtonId,
_hudButtonBelt,
_hudButtonBack,
_hudButtonPocket1,
@@ -84,16 +92,12 @@ namespace Content.Client.GameObjects
base.AddToSlot(slot, entity);
if (!_inventoryButtons.TryGetValue(slot, out var buttons))
{
return;
}
entity.TryGetComponent(out ISpriteComponent sprite);
foreach (var button in buttons)
{
button.SpriteView.Sprite = sprite;
button.OnPressed = RemoveFromInventory;
_itemSlotManager.SetItemSlot(button, entity);
button.OnPressed = (e) => HandleInventoryKeybind(e, slot);
}
}
@@ -108,23 +112,48 @@ namespace Content.Client.GameObjects
foreach (var button in buttons)
{
button.SpriteView.Sprite = null;
button.OnPressed = AddToInventory;
ClearButton(button, slot);
}
}
protected override void HandleInventoryKeybind(BaseButton.ButtonEventArgs args, Slots slot)
{
if (!_inventoryButtons.TryGetValue(slot, out var buttons))
return;
if (!Owner.TryGetSlot(slot, out var item))
return;
if (_itemSlotManager.OnButtonPressed(args.Event, item))
return;
base.HandleInventoryKeybind(args, slot);
}
private void ClearButton(ItemSlotButton button, Slots slot)
{
button.OnPressed = (e) => AddToInventory(e, slot);
_itemSlotManager.SetItemSlot(button, null);
}
public override void PlayerAttached()
{
base.PlayerAttached();
GameHud.InventoryQuickButtonContainer.AddChild(_quickButtonsContainer);
_gameHud.InventoryQuickButtonContainer.AddChild(_quickButtonsContainer);
}
public override void PlayerDetached()
{
base.PlayerDetached();
GameHud.InventoryQuickButtonContainer.RemoveChild(_quickButtonsContainer);
_gameHud.InventoryQuickButtonContainer.RemoveChild(_quickButtonsContainer);
foreach (var (slot, list) in _inventoryButtons)
{
foreach (var button in list)
{
ClearButton(button, slot);
}
}
}
private class HumanInventoryWindow : SS14Window
@@ -133,29 +162,29 @@ namespace Content.Client.GameObjects
private const int ButtonSeparation = 2;
private const int RightSeparation = 2;
public IReadOnlyDictionary<Slots, InventoryButton> Buttons { get; }
public IReadOnlyDictionary<Slots, ItemSlotButton> Buttons { get; }
public HumanInventoryWindow(ILocalizationManager loc, IResourceCache resourceCache)
{
Title = loc.GetString("Your Inventory");
Resizable = false;
var buttonDict = new Dictionary<Slots, InventoryButton>();
var buttonDict = new Dictionary<Slots, ItemSlotButton>();
Buttons = buttonDict;
const int width = ButtonSize * 4 + ButtonSeparation * 3 + RightSeparation;
const int height = ButtonSize * 4 + ButtonSeparation * 3;
var windowContents = new Control {CustomMinimumSize = (width, height)};
var windowContents = new LayoutContainer {CustomMinimumSize = (width, height)};
Contents.AddChild(windowContents);
void AddButton(Slots slot, string textureName, Vector2 position)
{
var texture = resourceCache.GetTexture($"/Textures/UserInterface/Inventory/{textureName}.png");
var button = new InventoryButton(slot, texture)
{
Position = position
};
var storageTexture = resourceCache.GetTexture("/Textures/UserInterface/Inventory/back.png");
var button = new ItemSlotButton(texture, storageTexture);
LayoutContainer.SetPosition(button, position);
windowContents.AddChild(button);
buttonDict.Add(slot, button);
@@ -178,7 +207,7 @@ namespace Content.Client.GameObjects
// Right column
AddButton(Slots.EARS, "ears", (2 * (size + sep), 0));
AddButton(Slots.IDCARD, "mask", (2 * (size + sep), size + sep));
AddButton(Slots.IDCARD, "id", (2 * (size + sep), size + sep));
AddButton(Slots.GLOVES, "gloves", (2 * (size + sep), 2 * (size + sep)));
// Far right column.
@@ -186,8 +215,6 @@ namespace Content.Client.GameObjects
AddButton(Slots.BELT, "belt", (rSep + 3 * (size + sep), size + sep));
AddButton(Slots.POCKET1, "pocket", (rSep + 3 * (size + sep), 2 * (size + sep)));
AddButton(Slots.POCKET2, "pocket", (rSep + 3 * (size + sep), 3 * (size + sep)));
Size = CombinedMinimumSize;
}
}
}

View File

@@ -1,40 +0,0 @@
using System;
using Content.Shared.GameObjects.Components.Inventory;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects
{
public sealed class InventoryButton : MarginContainer
{
public EquipmentSlotDefines.Slots Slot { get; }
public EntityUid EntityUid { get; set; }
public BaseButton Button { get; }
public SpriteView SpriteView { get; }
public Action<BaseButton.ButtonEventArgs> OnPressed { get; set; }
public InventoryButton(EquipmentSlotDefines.Slots slot, Texture texture)
{
Slot = slot;
CustomMinimumSize = (64, 64);
AddChild(Button = new TextureButton
{
TextureNormal = texture,
Scale = (2, 2),
});
Button.OnPressed += e => OnPressed?.Invoke(e);
AddChild(SpriteView = new SpriteView
{
MouseFilter = MouseFilterMode.Ignore,
Scale = (2, 2)
});
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System;
using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components.Inventory;
using Content.Shared.Input;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Interfaces.GameObjects;
@@ -10,8 +11,9 @@ namespace Content.Client.GameObjects
{
public abstract class InventoryInterfaceController : IDisposable
{
// ReSharper disable once UnassignedGetOnlyAutoProperty
[field: Dependency] protected IGameHud GameHud { get; }
#pragma warning disable 649
[Dependency] protected readonly IGameHud _gameHud;
#pragma warning restore 649
protected InventoryInterfaceController(ClientInventoryComponent owner)
{
@@ -28,8 +30,8 @@ namespace Content.Client.GameObjects
public virtual void PlayerAttached()
{
GameHud.InventoryButtonVisible = true;
GameHud.InventoryButtonToggled = b =>
_gameHud.InventoryButtonVisible = true;
_gameHud.InventoryButtonToggled = b =>
{
if (b)
{
@@ -44,7 +46,7 @@ namespace Content.Client.GameObjects
public virtual void PlayerDetached()
{
GameHud.InventoryButtonVisible = false;
_gameHud.InventoryButtonVisible = false;
Window.Close();
}
@@ -60,20 +62,41 @@ namespace Content.Client.GameObjects
{
}
protected void RemoveFromInventory(BaseButton.ButtonEventArgs args)
protected virtual void HandleInventoryKeybind(BaseButton.ButtonEventArgs args, EquipmentSlotDefines.Slots slot)
{
args.Button.Pressed = false;
var control = (InventoryButton) args.Button.Parent;
Owner.SendUnequipMessage(control.Slot);
if (args.Event.CanFocus)
{
UseItemOnInventory(args, slot);
}
}
protected void AddToInventory(BaseButton.ButtonEventArgs args)
protected void AddToInventory(BaseButton.ButtonEventArgs args, EquipmentSlotDefines.Slots slot)
{
if (!args.Event.CanFocus)
{
return;
}
args.Button.Pressed = false;
Owner.SendEquipMessage(slot);
}
protected void UseItemOnInventory(BaseButton.ButtonEventArgs args, EquipmentSlotDefines.Slots slot)
{
args.Button.Pressed = false;
var control = (InventoryButton) args.Button.Parent;
Owner.SendEquipMessage(control.Slot);
Owner.SendUseMessage(slot);
}
protected void OpenStorage(BaseButton.ButtonEventArgs args, EquipmentSlotDefines.Slots slot)
{
if (!args.Event.CanFocus && args.Event.Function != ContentKeyFunctions.ActivateItemInWorld)
{
return;
}
args.Button.Pressed = false;
Owner.SendOpenStorageUIMessage(slot);
}
}
}

View File

@@ -0,0 +1,106 @@
using System;
using Content.Shared.GameObjects.Components;
using Robust.Client.Graphics.Drawing;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components
{
[RegisterComponent]
public sealed class HandheldLightComponent : SharedHandheldLightComponent, IItemStatus
{
[ViewVariables] public float? Charge { get; private set; }
public Control MakeControl()
{
return new StatusControl(this);
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
var cast = (HandheldLightComponentState) curState;
Charge = cast.Charge;
}
private sealed class StatusControl : Control
{
private const float TimerCycle = 1;
private readonly HandheldLightComponent _parent;
private readonly PanelContainer[] _sections = new PanelContainer[5];
private float _timer;
private static readonly StyleBoxFlat _styleBoxLit = new StyleBoxFlat
{
BackgroundColor = Color.Green
};
private static readonly StyleBoxFlat _styleBoxUnlit = new StyleBoxFlat
{
BackgroundColor = Color.Black
};
public StatusControl(HandheldLightComponent parent)
{
_parent = parent;
var wrapper = new HBoxContainer
{
SeparationOverride = 4,
SizeFlagsHorizontal = SizeFlags.ShrinkCenter
};
AddChild(wrapper);
for (var i = 0; i < _sections.Length; i++)
{
var panel = new PanelContainer {CustomMinimumSize = (20, 20)};
wrapper.AddChild(panel);
_sections[i] = panel;
}
}
protected override void Update(FrameEventArgs args)
{
base.Update(args);
_timer += args.DeltaSeconds;
_timer %= TimerCycle;
var charge = _parent.Charge ?? 0;
int level;
if (FloatMath.CloseTo(charge, 0))
{
level = 0;
}
else
{
level = 1 + (int) MathF.Round(charge * 6);
}
if (level == 1)
{
// Flash the last light.
_sections[0].PanelOverride = _timer > TimerCycle / 2 ? _styleBoxLit : _styleBoxUnlit;
}
else
{
_sections[0].PanelOverride = level > 2 ? _styleBoxLit : _styleBoxUnlit;
}
_sections[1].PanelOverride = level > 3 ? _styleBoxLit : _styleBoxUnlit;
_sections[2].PanelOverride = level > 4 ? _styleBoxLit : _styleBoxUnlit;
_sections[3].PanelOverride = level > 5 ? _styleBoxLit : _styleBoxUnlit;
_sections[4].PanelOverride = level > 6 ? _styleBoxLit : _styleBoxUnlit;
}
}
}
}

View File

@@ -0,0 +1,33 @@
using Robust.Client.UserInterface;
namespace Content.Client.GameObjects.Components
{
/// <summary>
/// Allows a component to provide status tooltips next to the hands interface.
/// </summary>
public interface IItemStatus
{
/// <summary>
/// Called to get a control that represents the status for this component.
/// </summary>
/// <returns>
/// The control to render as status.
/// </returns>
public Control MakeControl();
/// <summary>
/// Called when the item no longer needs this status (say, dropped from hand)
/// </summary>
/// <remarks>
/// <para>
/// Useful to allow you to drop the control for the GC, if you need to.
/// </para>
/// <para>
/// Note that this may be called after a second invocation of <see cref="MakeControl"/> (for example if the user switches the item between two hands).
/// </para>
/// </remarks>
public void DestroyControl(Control control)
{
}
}
}

View File

@@ -75,12 +75,13 @@ namespace Content.Client.GameObjects.Components.IconSmoothing
serializer.DataFieldCached(ref _mode, "mode", IconSmoothingMode.Corners);
}
public override void Startup()
/// <inheritdoc />
protected override void Startup()
{
base.Startup();
SnapGrid.OnPositionChanged += SnapGridOnPositionChanged;
Owner.EntityManager.RaiseEvent(Owner, new IconSmoothDirtyEvent(null, SnapGrid.Offset, Mode));
Owner.EntityManager.EventBus.RaiseEvent(Owner, new IconSmoothDirtyEvent(null, SnapGrid.Offset, Mode));
if (Mode == IconSmoothingMode.Corners)
{
var state0 = $"{StateBase}0";
@@ -196,17 +197,18 @@ namespace Content.Client.GameObjects.Components.IconSmoothing
Sprite.LayerSetState(CornerLayers.NW, $"{StateBase}{(int) cornerNW}");
}
public override void Shutdown()
/// <inheritdoc />
protected override void Shutdown()
{
SnapGrid.OnPositionChanged -= SnapGridOnPositionChanged;
Owner.EntityManager.RaiseEvent(Owner, new IconSmoothDirtyEvent(_lastPosition, SnapGrid.Offset, Mode));
base.Shutdown();
SnapGrid.OnPositionChanged -= SnapGridOnPositionChanged;
Owner.EntityManager.EventBus.RaiseEvent(Owner, new IconSmoothDirtyEvent(_lastPosition, SnapGrid.Offset, Mode));
}
private void SnapGridOnPositionChanged()
{
Owner.EntityManager.RaiseEvent(Owner, new IconSmoothDirtyEvent(_lastPosition, SnapGrid.Offset, Mode));
Owner.EntityManager.EventBus.RaiseEvent(Owner, new IconSmoothDirtyEvent(_lastPosition, SnapGrid.Offset, Mode));
_lastPosition = (Owner.Transform.GridID, SnapGrid.Position);
}

View File

@@ -0,0 +1,36 @@
using Content.Client.Instruments;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.Instruments
{
public class InstrumentBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private InstrumentMenu _instrumentMenu;
public InstrumentComponent Instrument { get; set; }
public InstrumentBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
if (!Owner.Owner.TryGetComponent<InstrumentComponent>(out var instrument)) return;
Instrument = instrument;
_instrumentMenu = new InstrumentMenu(this);
_instrumentMenu.OnClose += Close;
_instrumentMenu.OpenCentered();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_instrumentMenu?.Dispose();
}
}
}

View File

@@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Instruments;
using Robust.Shared.GameObjects;
using Robust.Client.Audio.Midi;
using Robust.Shared.Audio.Midi;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using Timer = Robust.Shared.Timers.Timer;
namespace Content.Client.GameObjects.Components.Instruments
{
[RegisterComponent]
public class InstrumentComponent : SharedInstrumentComponent
{
/// <summary>
/// Called when a midi song stops playing.
/// </summary>
public event Action OnMidiPlaybackEnded;
#pragma warning disable 649
[Dependency] private IMidiManager _midiManager;
[Dependency] private readonly IGameTiming _timing;
#pragma warning restore 649
private IMidiRenderer _renderer;
private int _instrumentProgram = 1;
/// <summary>
/// A queue of MidiEvents to be sent to the server.
/// </summary>
private Queue<MidiEvent> _eventQueue = new Queue<MidiEvent>();
/// <summary>
/// Whether a midi song will loop or not.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public bool LoopMidi
{
get => _renderer.LoopMidi;
set => _renderer.LoopMidi = value;
}
/// <summary>
/// Changes the instrument the midi renderer will play.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public int InstrumentProgram
{
get => _instrumentProgram;
set {
_instrumentProgram = value;
_renderer.MidiProgram = _instrumentProgram;
}
}
/// <summary>
/// Whether there's a midi song being played or not.
/// </summary>
[ViewVariables]
public bool IsMidiOpen => _renderer.Status == MidiRendererStatus.File;
/// <summary>
/// Whether the midi renderer is listening for midi input or not.
/// </summary>
[ViewVariables]
public bool IsInputOpen => _renderer.Status == MidiRendererStatus.Input;
public override void Initialize()
{
base.Initialize();
IoCManager.InjectDependencies(this);
_renderer = _midiManager.GetNewRenderer();
_renderer.MidiProgram = _instrumentProgram;
_renderer.TrackingEntity = Owner;
_renderer.OnMidiPlayerFinished += () => { OnMidiPlaybackEnded?.Invoke(); };
}
protected override void Shutdown()
{
base.Shutdown();
_renderer?.Dispose();
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _instrumentProgram, "program", 1);
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case InstrumentMidiEventMessage midiEventMessage:
// If we're the ones sending the MidiEvents, we ignore this message.
if (IsInputOpen || IsMidiOpen) break;
Timer.Spawn((int) (500 + _timing.CurTime.TotalMilliseconds - midiEventMessage.Timestamp),
() => _renderer.SendMidiEvent(midiEventMessage.MidiEvent));
break;
case InstrumentStopMidiMessage _:
_renderer.StopAllNotes();
if(IsInputOpen) CloseInput();
if(IsMidiOpen) CloseMidi();
break;
}
}
/// <inheritdoc cref="MidiRenderer.OpenInput"/>
public bool OpenInput()
{
if (_renderer.OpenInput())
{
_renderer.OnMidiEvent += RendererOnMidiEvent;
return true;
}
return false;
}
/// <inheritdoc cref="MidiRenderer.CloseInput"/>
public bool CloseInput()
{
if (!_renderer.CloseInput()) return false;
_renderer.OnMidiEvent -= RendererOnMidiEvent;
return true;
}
/// <inheritdoc cref="MidiRenderer.OpenMidi(string)"/>
public bool OpenMidi(string filename)
{
if (!_renderer.OpenMidi(filename)) return false;
_renderer.OnMidiEvent += RendererOnMidiEvent;
return true;
}
/// <inheritdoc cref="MidiRenderer.CloseMidi"/>
public bool CloseMidi()
{
if (!_renderer.CloseMidi()) return false;
_renderer.OnMidiEvent -= RendererOnMidiEvent;
return true;
}
/// <summary>
/// Called whenever the renderer receives a midi event.
/// </summary>
/// <param name="midiEvent">The received midi event</param>
private void RendererOnMidiEvent(MidiEvent midiEvent)
{
SendNetworkMessage(new InstrumentMidiEventMessage(midiEvent, _timing.CurTime.TotalMilliseconds));
}
}
}

View File

@@ -0,0 +1,59 @@
using Robust.Client.Graphics.Shaders;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
namespace Content.Client.GameObjects.Components
{
[RegisterComponent]
public class InteractionOutlineComponent : Component
{
private const string ShaderInRange = "selection_outline_inrange";
private const string ShaderOutOfRange = "selection_outline";
public override string Name => "InteractionOutline";
#pragma warning disable 649
[Dependency] private readonly IPrototypeManager _prototypeManager;
#pragma warning restore 649
private ShaderInstance _selectionShaderInstance;
private ShaderInstance _selectionShaderInRangeInstance;
/// <inheritdoc />
public override void Initialize()
{
base.Initialize();
_selectionShaderInRangeInstance = _prototypeManager.Index<ShaderPrototype>(ShaderInRange).Instance();
_selectionShaderInstance = _prototypeManager.Index<ShaderPrototype>(ShaderOutOfRange).Instance();
}
public void OnMouseEnter(bool inInteractionRange)
{
if (Owner.TryGetComponent(out ISpriteComponent sprite))
{
sprite.PostShader = inInteractionRange ? _selectionShaderInRangeInstance : _selectionShaderInstance;
sprite.RenderOrder = Owner.EntityManager.CurrentTick.Value;
}
}
public void OnMouseLeave()
{
if (Owner.TryGetComponent(out ISpriteComponent sprite))
{
sprite.PostShader = null;
sprite.RenderOrder = 0;
}
}
public void UpdateInRange(bool inInteractionRange)
{
if (Owner.TryGetComponent(out ISpriteComponent sprite))
{
sprite.PostShader = inInteractionRange ? _selectionShaderInRangeInstance : _selectionShaderInstance;
}
}
}
}

View File

@@ -1,4 +1,7 @@
using System.Collections.Generic;
// Only unused on .NET Core due to KeyValuePair.Deconstruct
// ReSharper disable once RedundantUsingDirective
using Robust.Shared.Utility;
using System.Collections.Generic;
using System.Linq;
using Content.Client.Interfaces.GameObjects;
using Content.Client.UserInterface;
@@ -10,7 +13,6 @@ using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects
@@ -183,8 +185,15 @@ namespace Content.Client.GameObjects
{
if (GetEntity(ActiveIndex) != null)
{
SendNetworkMessage(new ActivateInhandMsg());
SendNetworkMessage(new UseInHandMsg());
}
}
public void ActivateItemInHand(string handIndex)
{
if (GetEntity(handIndex) == null)
return;
SendNetworkMessage(new ActivateInHandMsg(handIndex));
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Items;
using Robust.Client.Graphics;
using Robust.Client.Interfaces.ResourceManagement;
@@ -18,7 +17,6 @@ namespace Content.Client.GameObjects
{
public override string Name => "Item";
public override uint? NetID => ContentNetIDs.ITEM;
public override Type StateType => typeof(ItemComponentState);
[ViewVariables] protected ResourcePath RsiPath;

View File

@@ -0,0 +1,12 @@
using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects
{
[RegisterComponent]
public class ItemStatusComponent : Component
{
public override string Name => "ItemStatus";
}
}

View File

@@ -1,9 +1,12 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using Content.Client.GameObjects.Components.IconSmoothing;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.ViewVariables;
using static Robust.Client.GameObjects.SpriteComponent;
namespace Content.Client.GameObjects.Components
@@ -26,19 +29,35 @@ namespace Content.Client.GameObjects.Components
public CornerFill LastCornerSW { get; private set; }
public CornerFill LastCornerNW { get; private set; }
public override void Startup()
[ViewVariables]
private IEntity _overlayEntity;
private ISpriteComponent _overlaySprite;
protected override void Startup()
{
base.Startup();
_overlayEntity = Owner.EntityManager.SpawnEntity("low_wall_overlay", Owner.Transform.GridPosition);
_overlayEntity.Transform.AttachParent(Owner);
_overlaySprite = _overlayEntity.GetComponent<ISpriteComponent>();
var overState0 = $"{StateBase}over_0";
Sprite.LayerMapSet(OverCornerLayers.SE, Sprite.AddLayerState(overState0));
Sprite.LayerSetDirOffset(OverCornerLayers.SE, DirectionOffset.None);
Sprite.LayerMapSet(OverCornerLayers.NE, Sprite.AddLayerState(overState0));
Sprite.LayerSetDirOffset(OverCornerLayers.NE, DirectionOffset.CounterClockwise);
Sprite.LayerMapSet(OverCornerLayers.NW, Sprite.AddLayerState(overState0));
Sprite.LayerSetDirOffset(OverCornerLayers.NW, DirectionOffset.Flip);
Sprite.LayerMapSet(OverCornerLayers.SW, Sprite.AddLayerState(overState0));
Sprite.LayerSetDirOffset(OverCornerLayers.SW, DirectionOffset.Clockwise);
_overlaySprite.LayerMapSet(OverCornerLayers.SE, _overlaySprite.AddLayerState(overState0));
_overlaySprite.LayerSetDirOffset(OverCornerLayers.SE, DirectionOffset.None);
_overlaySprite.LayerMapSet(OverCornerLayers.NE, _overlaySprite.AddLayerState(overState0));
_overlaySprite.LayerSetDirOffset(OverCornerLayers.NE, DirectionOffset.CounterClockwise);
_overlaySprite.LayerMapSet(OverCornerLayers.NW, _overlaySprite.AddLayerState(overState0));
_overlaySprite.LayerSetDirOffset(OverCornerLayers.NW, DirectionOffset.Flip);
_overlaySprite.LayerMapSet(OverCornerLayers.SW, _overlaySprite.AddLayerState(overState0));
_overlaySprite.LayerSetDirOffset(OverCornerLayers.SW, DirectionOffset.Clockwise);
}
protected override void Shutdown()
{
base.Shutdown();
_overlayEntity.Delete();
}
internal override void CalculateNewSprite()
@@ -159,10 +178,10 @@ namespace Content.Client.GameObjects.Components
Sprite.LayerSetState(CornerLayers.SW, $"{StateBase}{(int) cornerSW}");
Sprite.LayerSetState(CornerLayers.NW, $"{StateBase}{(int) cornerNW}");
Sprite.LayerSetState(OverCornerLayers.NE, $"{StateBase}over_{(int) lowCornerNE}");
Sprite.LayerSetState(OverCornerLayers.SE, $"{StateBase}over_{(int) lowCornerSE}");
Sprite.LayerSetState(OverCornerLayers.SW, $"{StateBase}over_{(int) lowCornerSW}");
Sprite.LayerSetState(OverCornerLayers.NW, $"{StateBase}over_{(int) lowCornerNW}");
_overlaySprite.LayerSetState(OverCornerLayers.NE, $"{StateBase}over_{(int) lowCornerNE}");
_overlaySprite.LayerSetState(OverCornerLayers.SE, $"{StateBase}over_{(int) lowCornerSE}");
_overlaySprite.LayerSetState(OverCornerLayers.SW, $"{StateBase}over_{(int) lowCornerSW}");
_overlaySprite.LayerSetState(OverCornerLayers.NW, $"{StateBase}over_{(int) lowCornerNW}");
LastCornerNE = cornerNE;
LastCornerSE = cornerSE;
@@ -178,7 +197,7 @@ namespace Content.Client.GameObjects.Components
}
}
[System.Diagnostics.Contracts.Pure]
[Pure]
private (bool connected, bool lowWall) MatchingWall(IEnumerable<IEntity> candidates)
{
foreach (var entity in candidates)

View File

@@ -0,0 +1,305 @@
using System;
using System.Linq;
using Content.Client.UserInterface;
using Content.Shared.Preferences.Appearance;
using JetBrains.Annotations;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects.Components.Renderable;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using static Content.Shared.GameObjects.Components.SharedMagicMirrorComponent;
using static Content.Client.StaticIoC;
namespace Content.Client.GameObjects.Components
{
[UsedImplicitly]
public class MagicMirrorBoundUserInterface : BoundUserInterface
{
private MagicMirrorWindow _window;
public MagicMirrorBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
_window = new MagicMirrorWindow(this);
_window.OnClose += Close;
_window.Open();
}
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
{
switch (message)
{
case MagicMirrorInitialDataMessage initialData:
_window.SetInitialData(initialData);
break;
}
}
internal void HairSelected(string name, bool isFacialHair)
{
SendMessage(new HairSelectedMessage(name, isFacialHair));
}
internal void HairColorSelected(Color color, bool isFacialHair)
{
SendMessage(new HairColorSelectedMessage((color.RByte, color.GByte, color.BByte),
isFacialHair));
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_window.Dispose();
}
}
}
public class FacialHairStylePicker : HairStylePicker
{
public override void Populate()
{
var humanFacialHairRSIPath = SharedSpriteComponent.TextureRoot / "Mob/human_facial_hair.rsi";
var humanFacialHairRSI = ResC.GetResource<RSIResource>(humanFacialHairRSIPath).RSI;
var styles = HairStyles.FacialHairStylesMap.ToList();
styles.Sort(HairStyles.FacialHairStyleComparer);
foreach (var (styleName, styleState) in HairStyles.FacialHairStylesMap)
{
Items.AddItem(styleName, humanFacialHairRSI[styleState].Frame0);
}
}
}
public class HairStylePicker : Control
{
public event Action<Color> OnHairColorPicked;
public event Action<string> OnHairStylePicked;
protected readonly ItemList Items;
private readonly ColorSlider _colorSliderR;
private readonly ColorSlider _colorSliderG;
private readonly ColorSlider _colorSliderB;
private Color _lastColor;
public void SetData(Color color, string styleName)
{
_lastColor = color;
_colorSliderR.ColorValue = color.RByte;
_colorSliderG.ColorValue = color.GByte;
_colorSliderB.ColorValue = color.BByte;
foreach (var item in Items)
{
item.Selected = item.Text == styleName;
}
UpdateStylePickerColor();
}
private void UpdateStylePickerColor()
{
foreach (var item in Items)
{
item.IconModulate = _lastColor;
}
}
public HairStylePicker()
{
var vBox = new VBoxContainer();
AddChild(vBox);
vBox.AddChild(_colorSliderR = new ColorSlider(NanoStyle.StyleClassSliderRed));
vBox.AddChild(_colorSliderG = new ColorSlider(NanoStyle.StyleClassSliderGreen));
vBox.AddChild(_colorSliderB = new ColorSlider(NanoStyle.StyleClassSliderBlue));
Action colorValueChanged = ColorValueChanged;
_colorSliderR.OnValueChanged += colorValueChanged;
_colorSliderG.OnValueChanged += colorValueChanged;
_colorSliderB.OnValueChanged += colorValueChanged;
Items = new ItemList
{
SizeFlagsVertical = SizeFlags.FillExpand,
CustomMinimumSize = (300, 250)
};
vBox.AddChild(Items);
Items.OnItemSelected += ItemSelected;
}
private void ColorValueChanged()
{
var newColor = new Color(
_colorSliderR.ColorValue,
_colorSliderG.ColorValue,
_colorSliderB.ColorValue
);
OnHairColorPicked?.Invoke(newColor);
_lastColor = newColor;
UpdateStylePickerColor();
}
public virtual void Populate()
{
var humanHairRSIPath = SharedSpriteComponent.TextureRoot / "Mob/human_hair.rsi";
var humanHairRSI = ResC.GetResource<RSIResource>(humanHairRSIPath).RSI;
var styles = HairStyles.HairStylesMap.ToList();
styles.Sort(HairStyles.HairStyleComparer);
foreach (var (styleName, styleState) in styles)
{
Items.AddItem(styleName, humanHairRSI[styleState].Frame0);
}
}
private void ItemSelected(ItemList.ItemListSelectedEventArgs args)
{
OnHairStylePicked?.Invoke(Items[args.ItemIndex].Text);
}
private sealed class ColorSlider : Control
{
private readonly Slider _slider;
private readonly LineEdit _textBox;
private byte _colorValue;
private bool _ignoreEvents;
public event Action OnValueChanged;
public byte ColorValue
{
get => _colorValue;
set
{
_ignoreEvents = true;
_colorValue = value;
_slider.Value = value;
_textBox.Text = value.ToString();
_ignoreEvents = false;
}
}
public ColorSlider(string styleClass)
{
_slider = new Slider
{
StyleClasses = {styleClass},
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsVertical = SizeFlags.ShrinkCenter,
MaxValue = byte.MaxValue
};
_textBox = new LineEdit
{
CustomMinimumSize = (50, 0)
};
AddChild(new HBoxContainer
{
Children =
{
_slider,
_textBox
}
});
_slider.OnValueChanged += _ =>
{
if (_ignoreEvents)
{
return;
}
_colorValue = (byte) _slider.Value;
_textBox.Text = _colorValue.ToString();
OnValueChanged?.Invoke();
};
_textBox.OnTextChanged += ev =>
{
if (_ignoreEvents)
{
return;
}
if (int.TryParse(ev.Text, out var result))
{
result = result.Clamp(0, byte.MaxValue);
_ignoreEvents = true;
_colorValue = (byte) result;
_slider.Value = result;
_ignoreEvents = false;
OnValueChanged?.Invoke();
}
};
}
}
}
public class MagicMirrorWindow : SS14Window
{
private readonly HairStylePicker _hairStylePicker;
private readonly FacialHairStylePicker _facialHairStylePicker;
protected override Vector2? CustomSize => (500, 360);
public MagicMirrorWindow(MagicMirrorBoundUserInterface owner)
{
Title = Loc.GetString("Magic Mirror");
_hairStylePicker = new HairStylePicker {SizeFlagsHorizontal = SizeFlags.FillExpand};
_hairStylePicker.Populate();
_hairStylePicker.OnHairStylePicked += newStyle => owner.HairSelected(newStyle, false);
_hairStylePicker.OnHairColorPicked += newColor => owner.HairColorSelected(newColor, false);
_facialHairStylePicker = new FacialHairStylePicker {SizeFlagsHorizontal = SizeFlags.FillExpand};
_facialHairStylePicker.Populate();
_facialHairStylePicker.OnHairStylePicked += newStyle => owner.HairSelected(newStyle, true);
_facialHairStylePicker.OnHairColorPicked += newColor => owner.HairColorSelected(newColor, true);
Contents.AddChild(new HBoxContainer
{
SeparationOverride = 8,
Children = {_hairStylePicker, _facialHairStylePicker}
});
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_hairStylePicker.Dispose();
_facialHairStylePicker.Dispose();
}
}
public void SetInitialData(MagicMirrorInitialDataMessage initialData)
{
_facialHairStylePicker.SetData(initialData.FacialHairColor, initialData.FacialHairName);
_hairStylePicker.SetData(initialData.HairColor, initialData.HairName);
}
}
}

View File

@@ -0,0 +1,32 @@
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Shared.GameObjects.Components.UserInterface;
using static Content.Shared.GameObjects.Components.Medical.SharedMedicalScannerComponent;
namespace Content.Client.GameObjects.Components.MedicalScanner
{
public class MedicalScannerBoundUserInterface : BoundUserInterface
{
public MedicalScannerBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
private MedicalScannerWindow _window;
protected override void Open()
{
base.Open();
_window = new MedicalScannerWindow
{
Title = Owner.Owner.Name,
};
_window.OnClose += Close;
_window.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
_window.Populate((MedicalScannerBoundUserInterfaceState) state);
}
}
}

View File

@@ -0,0 +1,57 @@
using System;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using static Content.Shared.GameObjects.Components.Medical.SharedMedicalScannerComponent;
using static Content.Shared.GameObjects.Components.Medical.SharedMedicalScannerComponent.MedicalScannerStatus;
namespace Content.Client.GameObjects.Components.MedicalScanner
{
public class MedicalScannerVisualizer2D : AppearanceVisualizer
{
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (!component.TryGetData(MedicalScannerVisuals.Status, out MedicalScannerStatus status)) return;
sprite.LayerSetState(MedicalScannerVisualLayers.Machine, StatusToMachineStateId(status));
sprite.LayerSetState(MedicalScannerVisualLayers.Terminal, StatusToTerminalStateId(status));
}
private string StatusToMachineStateId(MedicalScannerStatus status)
{
switch (status)
{
case Off: return "scanner_off";
case Open: return "scanner_open";
case Red: return "scanner_red";
case Death: return "scanner_death";
case Green: return "scanner_green";
case Yellow: return "scanner_yellow";
default:
throw new ArgumentOutOfRangeException(nameof(status), status, "unknown MedicalScannerStatus");
}
}
private string StatusToTerminalStateId(MedicalScannerStatus status)
{
switch (status)
{
case Off: return "scanner_terminal_off";
case Open: return "scanner_terminal_blue";
case Red: return "scanner_terminal_red";
case Death: return "scanner_terminal_dead";
case Green: return "scanner_terminal_green";
case Yellow: return "scanner_terminal_blue";
default:
throw new ArgumentOutOfRangeException(nameof(status), status, "unknown MedicalScannerStatus");
}
}
public enum MedicalScannerVisualLayers
{
Machine,
Terminal,
}
}
}

View File

@@ -0,0 +1,38 @@
// Only unused on .NET Core due to KeyValuePair.Deconstruct
// ReSharper disable once RedundantUsingDirective
using Robust.Shared.Utility;
using System.Text;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Maths;
using static Content.Shared.GameObjects.Components.Medical.SharedMedicalScannerComponent;
namespace Content.Client.GameObjects.Components.MedicalScanner
{
public class MedicalScannerWindow : SS14Window
{
protected override Vector2? CustomSize => (485, 90);
public void Populate(MedicalScannerBoundUserInterfaceState state)
{
Contents.RemoveAllChildren();
var text = new StringBuilder();
if (state.MaxHealth == 0)
{
text.Append("No patient data.");
} else
{
text.Append($"Patient's health: {state.CurrentHealth}/{state.MaxHealth}\n");
if (state.DamageDictionary != null)
{
foreach (var (dmgType, amount) in state.DamageDictionary)
{
text.Append($"\n{dmgType}: {amount}");
}
}
}
Contents.AddChild(new Label(){Text = text.ToString()});
}
}
}

View File

@@ -13,10 +13,10 @@ namespace Content.Client.GameObjects.Components.Mobs
public sealed class CameraRecoilComponent : SharedCameraRecoilComponent
{
// Maximum rate of magnitude restore towards 0 kick.
private const float RestoreRateMax = 1.5f;
private const float RestoreRateMax = 2f;
// Minimum rate of magnitude restore towards 0 kick.
private const float RestoreRateMin = 0.5f;
private const float RestoreRateMin = 1f;
// Time in seconds since the last kick that lerps RestoreRateMin and RestoreRateMax
private const float RestoreRateRamp = 0.05f;
@@ -29,6 +29,10 @@ namespace Content.Client.GameObjects.Components.Mobs
private EyeComponent _eye;
// Basically I needed a way to chain this effect for the attack lunge animation.
// Sorry!
public Vector2 BaseOffset { get; set; }
public override void Initialize()
{
base.Initialize();
@@ -77,13 +81,25 @@ namespace Content.Client.GameObjects.Components.Mobs
var normalized = _currentKick.Normalized;
var restoreRate = FloatMath.Lerp(RestoreRateMin, RestoreRateMax, Math.Min(1, _lastKickTime/RestoreRateRamp));
var restore = normalized * restoreRate * frameTime;
_currentKick -= restore;
var (x, y) = _currentKick - restore;
if (Math.Sign(x) != Math.Sign(_currentKick.X))
{
x = 0;
}
if (Math.Sign(y) != Math.Sign(_currentKick.Y))
{
y = 0;
}
_currentKick = (x, y);
_updateEye();
}
private void _updateEye()
{
_eye.Offset = _currentKick;
_eye.Offset = BaseOffset + _currentKick;
}
}
}

View File

@@ -0,0 +1,110 @@
using System.Collections.Generic;
using Content.Client.Graphics.Overlays;
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Client.GameObjects;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Log;
namespace Content.Client.GameObjects
{
/// <summary>
/// A character UI component which shows the current damage state of the mob (living/dead)
/// </summary>
[RegisterComponent]
[ComponentReference(typeof(SharedOverlayEffectsComponent))]
public sealed class ClientOverlayEffectsComponent : SharedOverlayEffectsComponent//, ICharacterUI
{
/// <summary>
/// An enum representing the current state being applied to the user
/// </summary>
private ScreenEffects _currentEffect = ScreenEffects.None;
#pragma warning disable 649
// Required dependencies
[Dependency] private readonly IOverlayManager _overlayManager;
[Dependency] private readonly IPlayerManager _playerManager;
#pragma warning restore 649
/// <summary>
/// Holds the screen effects that can be applied mapped ot their relevant overlay
/// </summary>
private Dictionary<ScreenEffects, Overlay> _effectsDictionary;
/// <summary>
/// Allows calculating if we need to act due to this component being controlled by the current mob
/// </summary>
private bool CurrentlyControlled => _playerManager.LocalPlayer.ControlledEntity == Owner;
public override void OnAdd()
{
base.OnAdd();
_effectsDictionary = new Dictionary<ScreenEffects, Overlay>()
{
{ ScreenEffects.CircleMask, new CircleMaskOverlay() },
{ ScreenEffects.GradientCircleMask, new GradientCircleMask() }
};
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
{
switch (message)
{
case PlayerAttachedMsg _:
SetOverlay(_currentEffect);
break;
case PlayerDetachedMsg _:
RemoveOverlay();
break;
}
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if (!(curState is OverlayEffectComponentState state) || _currentEffect == state.ScreenEffect) return;
SetOverlay(state.ScreenEffect);
}
private void SetOverlay(ScreenEffects effect)
{
RemoveOverlay();
_currentEffect = effect;
ApplyOverlay();
}
private void RemoveOverlay()
{
if (CurrentlyControlled && _currentEffect != ScreenEffects.None)
{
var appliedEffect = _effectsDictionary[_currentEffect];
_overlayManager.RemoveOverlay(appliedEffect.ID);
}
_currentEffect = ScreenEffects.None;
}
private void ApplyOverlay()
{
if (CurrentlyControlled && _currentEffect != ScreenEffects.None)
{
var overlay = _effectsDictionary[_currentEffect];
if (_overlayManager.HasOverlay(overlay.ID))
{
return;
}
_overlayManager.AddOverlay(overlay);
Logger.InfoS("overlay", $"Changed overlay to {overlay}");
}
}
}
}

View File

@@ -0,0 +1,114 @@
using System.Collections.Generic;
using System.Linq;
using Content.Client.UserInterface;
using Content.Client.Utility;
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Log;
namespace Content.Client.GameObjects.Components.Mobs
{
/// <inheritdoc/>
[RegisterComponent]
public sealed class ClientStatusEffectsComponent : SharedStatusEffectsComponent
{
#pragma warning disable 649
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IResourceCache _resourceCache;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
#pragma warning restore 649
private StatusEffectsUI _ui;
private IDictionary<StatusEffect, string> _icons = new Dictionary<StatusEffect, string>();
/// <summary>
/// Allows calculating if we need to act due to this component being controlled by the current mob
/// </summary>
private bool CurrentlyControlled => _playerManager.LocalPlayer != null && _playerManager.LocalPlayer.ControlledEntity == Owner;
protected override void Shutdown()
{
base.Shutdown();
PlayerDetached();
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case PlayerAttachedMsg _:
PlayerAttached();
break;
case PlayerDetachedMsg _:
PlayerDetached();
break;
}
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if (!(curState is StatusEffectComponentState state) || _icons == state.StatusEffects) return;
_icons = state.StatusEffects;
UpdateIcons();
}
private void PlayerAttached()
{
if (!CurrentlyControlled || _ui != null)
{
return;
}
_ui = new StatusEffectsUI();
_userInterfaceManager.StateRoot.AddChild(_ui);
UpdateIcons();
}
private void PlayerDetached()
{
if (!CurrentlyControlled)
{
return;
}
_ui?.Dispose();
}
public void UpdateIcons()
{
if (!CurrentlyControlled || _ui == null)
{
return;
}
_ui.VBox.DisposeAllChildren();
foreach (var effect in _icons.OrderBy(x => (int) x.Key))
{
TextureRect newIcon = new TextureRect
{
TextureScale = (2, 2),
Texture = _resourceCache.GetTexture(effect.Value)
};
newIcon.Texture = _resourceCache.GetTexture(effect.Value);
_ui.VBox.AddChild(newIcon);
}
}
public void RemoveIcon(StatusEffect name)
{
_icons.Remove(name);
UpdateIcons();
Logger.InfoS("statuseffects", $"Removed icon {name}");
}
}
}

View File

@@ -0,0 +1,59 @@
using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.Mobs
{
[RegisterComponent]
public sealed class CombatModeComponent : SharedCombatModeComponent
{
[ViewVariables(VVAccess.ReadWrite)]
public bool IsInCombatMode { get; private set; }
[ViewVariables(VVAccess.ReadWrite)]
public TargetingZone ActiveZone { get; private set; }
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
#pragma warning restore 649
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
var state = (CombatModeComponentState) curState;
IsInCombatMode = state.IsInCombatMode;
ActiveZone = state.TargetingZone;
UpdateHud();
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case PlayerAttachedMsg _:
_gameHud.CombatPanelVisible = true;
UpdateHud();
break;
case PlayerDetachedMsg _:
_gameHud.CombatPanelVisible = false;
break;
}
}
private void UpdateHud()
{
_gameHud.CombatModeActive = IsInCombatMode;
_gameHud.TargetingZone = ActiveZone;
}
}
}

View File

@@ -0,0 +1,65 @@
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.Preferences;
using Content.Shared.Preferences.Appearance;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Mobs
{
[RegisterComponent]
public sealed class HumanoidAppearanceComponent : SharedHumanoidAppearanceComponent
{
public override HumanoidCharacterAppearance Appearance
{
get => base.Appearance;
set
{
base.Appearance = value;
UpdateLooks();
}
}
public override Sex Sex
{
get => base.Sex;
set
{
base.Sex = value;
UpdateLooks();
}
}
protected override void Startup()
{
base.Startup();
UpdateLooks();
}
private void UpdateLooks()
{
if (Appearance is null) return;
var sprite = Owner.GetComponent<SpriteComponent>();
sprite.LayerSetColor(HumanoidVisualLayers.Hair, Appearance.HairColor);
sprite.LayerSetColor(HumanoidVisualLayers.FacialHair, Appearance.FacialHairColor);
sprite.LayerSetState(HumanoidVisualLayers.Chest, Sex == Sex.Male ? "human_chest_m" : "human_chest_f");
sprite.LayerSetState(HumanoidVisualLayers.Head, Sex == Sex.Male ? "human_head_m" : "human_head_f");
sprite.LayerSetVisible(HumanoidVisualLayers.StencilMask, Sex == Sex.Female);
var hairStyle = Appearance.HairStyleName;
if (string.IsNullOrWhiteSpace(hairStyle) || !HairStyles.HairStylesMap.ContainsKey(hairStyle))
hairStyle = HairStyles.DefaultHairStyle;
sprite.LayerSetState(HumanoidVisualLayers.Hair,
HairStyles.HairStylesMap[hairStyle]);
var facialHairStyle = Appearance.FacialHairStyleName;
if (string.IsNullOrWhiteSpace(facialHairStyle) || !HairStyles.FacialHairStylesMap.ContainsKey(facialHairStyle))
facialHairStyle = HairStyles.DefaultFacialHairStyle;
sprite.LayerSetState(HumanoidVisualLayers.FacialHair,
HairStyles.FacialHairStylesMap[facialHairStyle]);
}
}
}

View File

@@ -0,0 +1,65 @@
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Mobs
{
[RegisterComponent]
public sealed class MeleeLungeComponent : Component
{
public override string Name => "MeleeLunge";
private const float ResetTime = 0.3f;
private const float BaseOffset = 0.25f;
private Angle _angle;
private float _time;
public void SetData(Angle angle)
{
_angle = angle;
_time = 0;
}
public void Update(float frameTime)
{
_time += frameTime;
var offset = Vector2.Zero;
var deleteSelf = false;
if (_time > ResetTime)
{
deleteSelf = true;
}
else
{
offset = _angle.RotateVec((BaseOffset, 0));
offset *= (ResetTime - _time) / ResetTime;
}
if (Owner.TryGetComponent(out CameraRecoilComponent recoilComponent))
{
recoilComponent.BaseOffset = offset;
}
else if (Owner.TryGetComponent(out EyeComponent eyeComponent))
{
eyeComponent.Offset = offset;
}
if (Owner.TryGetComponent(out ISpriteComponent spriteComponent))
{
// We have to account for rotation so the offset still checks out.
// SpriteComponent.Offset is applied before transform rotation (as expected).
var worldRotation = Owner.Transform.WorldRotation;
spriteComponent.Offset = new Angle(-worldRotation).RotateVec(offset);
}
if (deleteSelf)
{
Owner.RemoveComponent<MeleeLungeComponent>();
}
}
}
}

View File

@@ -1,171 +0,0 @@
using System.Collections.Generic;
using Content.Client.GameObjects.Components.Actor;
using Content.Client.Graphics.Overlays;
using Content.Client.UserInterface;
using Content.Client.Utility;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Renderable;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
namespace Content.Client.GameObjects
{
/// <summary>
/// A character UI component which shows the current damage state of the mob (living/dead)
/// </summary>
[RegisterComponent]
public class SpeciesUI : SharedSpeciesComponent//, ICharacterUI
{
private StatusEffectsUI _ui;
/// <summary>
/// Holds the godot control for the species window
/// </summary>
private SpeciesWindow _window;
/// <summary>
/// An enum representing the current state being applied to the user
/// </summary>
private ScreenEffects _currentEffect = ScreenEffects.None;
#pragma warning disable 649
// Required dependencies
[Dependency] private readonly IOverlayManager _overlayManager;
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
[Dependency] private readonly IResourceCache _resourceCache;
#pragma warning restore 649
//Relevant interface implementation for the character UI controller
public Control Scene => _window;
public UIPriority Priority => UIPriority.Species;
/// <summary>
/// Allows calculating if we need to act due to this component being controlled by the current mob
/// </summary>
private bool CurrentlyControlled => _playerManager.LocalPlayer.ControlledEntity == Owner;
/// <summary>
/// Holds the screen effects that can be applied mapped ot their relevant overlay
/// </summary>
private Dictionary<ScreenEffects, Overlay> EffectsDictionary;
public override void OnRemove()
{
base.OnRemove();
_window.Dispose();
}
public override void OnAdd()
{
base.OnAdd();
_window = new SpeciesWindow();
_ui = new StatusEffectsUI();
EffectsDictionary = new Dictionary<ScreenEffects, Overlay>()
{
{ ScreenEffects.CircleMask, new CircleMaskOverlay() },
{ ScreenEffects.GradientCircleMask, new GradientCircleMask() }
};
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
{
switch (message)
{
case HudStateChange msg:
if (CurrentlyControlled)
{
ChangeHudIcon(msg);
}
break;
case PlayerAttachedMsg _:
_ui.Parent?.RemoveChild(_ui);
_userInterfaceManager.StateRoot.AddChild(_ui);
ApplyOverlay();
break;
case PlayerDetachedMsg _:
_ui.Parent?.RemoveChild(_ui);
RemoveOverlay();
break;
}
}
private void ChangeHudIcon(HudStateChange changeMessage)
{
var path = SharedSpriteComponent.TextureRoot / changeMessage.StateSprite;
var texture = _resourceCache.GetTexture(path);
_window.SetIcon(texture);
_ui.SetHealthIcon(texture);
SetOverlay(changeMessage);
}
private void SetOverlay(HudStateChange message)
{
RemoveOverlay();
_currentEffect = message.effect;
ApplyOverlay();
}
private void RemoveOverlay()
{
if (_currentEffect != ScreenEffects.None)
{
var appliedEffect = EffectsDictionary[_currentEffect];
_overlayManager.RemoveOverlay(appliedEffect.ID);
}
_currentEffect = ScreenEffects.None;
}
private void ApplyOverlay()
{
if (_currentEffect != ScreenEffects.None)
{
var overlay = EffectsDictionary[_currentEffect];
if (_overlayManager.HasOverlay(overlay.ID))
{
return;
}
_overlayManager.AddOverlay(overlay);
}
}
private class SpeciesWindow : TextureRect
{
public SpeciesWindow()
{
SizeFlagsHorizontal = SizeFlags.ShrinkCenter;
SizeFlagsVertical = SizeFlags.None;
Texture = IoCManager.Resolve<IResourceCache>().GetTexture("/Textures/Mob/UI/Human/human0.png");
}
public void SetIcon(Texture texture)
{
Texture = texture;
}
}
}
}

View File

@@ -1,18 +1,12 @@
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Mobs
{
public class SpeciesVisualizer2D : AppearanceVisualizer
{
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
@@ -20,7 +14,7 @@ namespace Content.Client.GameObjects.Components.Mobs
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (component.TryGetData<SharedSpeciesComponent.MobState>(SharedSpeciesComponent.MobVisuals.RotationState, out var state))
{
switch (state)
switch (state)
{
case SharedSpeciesComponent.MobState.Stand:
sprite.Rotation = 0;
@@ -32,4 +26,4 @@ namespace Content.Client.GameObjects.Components.Mobs
}
}
}
}
}

View File

@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Nutrition;
using Content.Shared.Utility;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Content.Client.GameObjects.Components.Nutrition
{
[UsedImplicitly]
public sealed class DrinkFoodContainerVisualizer2D : AppearanceVisualizer
{
private string _baseState;
private int _steps;
private DrinkFoodContainerVisualMode _mode;
public override void LoadData(YamlMappingNode node)
{
base.LoadData(node);
_baseState = node.GetNode("base_state").AsString();
_steps = node.GetNode("steps").AsInt();
try
{
_mode = node.GetNode("mode").AsEnum<DrinkFoodContainerVisualMode>();
}
catch (KeyNotFoundException)
{
_mode = DrinkFoodContainerVisualMode.Rounded;
}
}
public override void OnChangeData(AppearanceComponent component)
{
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (!component.TryGetData<int>(DrinkFoodContainerVisuals.Current, out var current))
{
return;
}
if (!component.TryGetData<int>(DrinkFoodContainerVisuals.Capacity, out var capacity))
{
return;
}
int step;
switch (_mode)
{
case DrinkFoodContainerVisualMode.Discrete:
step = Math.Min(_steps - 1, current);
break;
case DrinkFoodContainerVisualMode.Rounded:
step = ContentHelpers.RoundToLevels(current, capacity, _steps);
break;
default:
throw new NullReferenceException();
}
sprite.LayerSetState(0, $"{_baseState}-{step}");
}
}
}

View File

@@ -0,0 +1,42 @@
using Content.Shared.GameObjects.Components.Nutrition;
using Content.Shared.Utility;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Content.Client.GameObjects.Components.Nutrition
{
[UsedImplicitly]
public sealed class DrinkFoodVisualizer2D : AppearanceVisualizer
{
private int _steps;
public override void LoadData(YamlMappingNode node)
{
base.LoadData(node);
_steps = node.GetNode("steps").AsInt();
}
public override void OnChangeData(AppearanceComponent component)
{
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (!component.TryGetData<int>(SharedFoodComponent.FoodVisuals.MaxUses, out var maxUses))
{
return;
}
if (component.TryGetData<int>(SharedFoodComponent.FoodVisuals.Visual, out var usesLeft))
{
var step = ContentHelpers.RoundToLevels(usesLeft, maxUses, _steps);
sprite.LayerSetState(0, $"icon-{step}");
}
else
{
sprite.LayerSetState(0, "icon-0");
}
}
}
}

View File

@@ -1,18 +1,12 @@
using System;
using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components.Power;
using NJsonSchema.Validation;
using OpenTK.Graphics.OpenGL4;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
namespace Content.Client.GameObjects.Components.Power
{
@@ -27,10 +21,7 @@ namespace Content.Client.GameObjects.Components.Power
{
base.Open();
_window = new ApcWindow
{
MarginRight = 426.0f, MarginBottom = 270.0f
};
_window = new ApcWindow();
_window.OnClose += Close;
_window.OpenCenteredMinSize();
@@ -72,34 +63,34 @@ namespace Content.Client.GameObjects.Components.Power
_chargeBar.Value = castState.Charge;
UpdateChargeBarColor(castState.Charge);
float ChargePercentage = (castState.Charge / _chargeBar.MaxValue) * 100.0f;
_window.ChargePercentage.Text = " " + ChargePercentage.ToString("0.00") + "%";
var chargePercentage = (castState.Charge / _chargeBar.MaxValue) * 100.0f;
_window.ChargePercentage.Text = " " + chargePercentage.ToString("0.00") + "%";
}
private void UpdateChargeBarColor(float charge)
{
float normalizedCharge = charge / _chargeBar.MaxValue;
var normalizedCharge = charge / _chargeBar.MaxValue;
float leftHue = 0.0f;// Red
float middleHue = 0.066f;// Orange
float rightHue = 0.33f;// Green
float saturation = 1.0f;// Uniform saturation
float value = 0.8f;// Uniform value / brightness
float alpha = 1.0f;// Uniform alpha
const float leftHue = 0.0f; // Red
const float middleHue = 0.066f; // Orange
const float rightHue = 0.33f; // Green
const float saturation = 1.0f; // Uniform saturation
const float value = 0.8f; // Uniform value / brightness
const float alpha = 1.0f; // Uniform alpha
// These should add up to 1.0 or your transition won't be smooth
float leftSideSize = 0.5f;// Fraction of _chargeBar lerped from leftHue to middleHue
float rightSideSize = 0.5f;// Fraction of _chargeBar lerped from middleHue to rightHue
const float leftSideSize = 0.5f; // Fraction of _chargeBar lerped from leftHue to middleHue
const float rightSideSize = 0.5f; // Fraction of _chargeBar lerped from middleHue to rightHue
float finalHue;
if (normalizedCharge <= leftSideSize)
{
normalizedCharge /= leftSideSize;// Adjust range to 0.0 to 1.0
normalizedCharge /= leftSideSize; // Adjust range to 0.0 to 1.0
finalHue = FloatMath.Lerp(leftHue, middleHue, normalizedCharge);
}
else
{
normalizedCharge = (normalizedCharge - leftSideSize) / rightSideSize;// Adjust range to 0.0 to 1.0.
normalizedCharge = (normalizedCharge - leftSideSize) / rightSideSize; // Adjust range to 0.0 to 1.0.
finalHue = FloatMath.Lerp(middleHue, rightHue, normalizedCharge);
}
@@ -109,7 +100,7 @@ namespace Content.Client.GameObjects.Components.Power
_chargeBar.ForegroundStyleBoxOverride = new StyleBoxFlat();
}
var foregroundStyleBoxOverride = (StyleBoxFlat)_chargeBar.ForegroundStyleBoxOverride;
var foregroundStyleBoxOverride = (StyleBoxFlat) _chargeBar.ForegroundStyleBoxOverride;
foregroundStyleBoxOverride.BackgroundColor =
Color.FromHsv(new Vector4(finalHue, saturation, value, alpha));
}
@@ -134,37 +125,37 @@ namespace Content.Client.GameObjects.Components.Power
public ApcWindow()
{
Title = "APC";
var rows = new VBoxContainer("Rows");
var rows = new VBoxContainer();
var statusHeader = new Label("StatusHeader") { Text = "Power Status: " };
var statusHeader = new Label {Text = "Power Status: "};
rows.AddChild(statusHeader);
var breaker = new HBoxContainer("Breaker");
var breakerLabel = new Label("Label") { Text = "Main Breaker: " };
BreakerButton = new CheckButton {Name = "Breaker", Text = "Toggle"};
var breaker = new HBoxContainer();
var breakerLabel = new Label {Text = "Main Breaker: "};
BreakerButton = new CheckButton {Text = "Toggle"};
breaker.AddChild(breakerLabel);
breaker.AddChild(BreakerButton);
rows.AddChild(breaker);
var externalStatus = new HBoxContainer("ExternalStatus");
var externalStatusLabel = new Label("Label") { Text = "External Power: " };
ExternalPowerStateLabel = new Label("Status") { Text = "Good" };
var externalStatus = new HBoxContainer();
var externalStatusLabel = new Label {Text = "External Power: "};
ExternalPowerStateLabel = new Label {Text = "Good"};
ExternalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateGood);
externalStatus.AddChild(externalStatusLabel);
externalStatus.AddChild(ExternalPowerStateLabel);
rows.AddChild(externalStatus);
var charge = new HBoxContainer("Charge");
var chargeLabel = new Label("Label") { Text = "Charge:" };
ChargeBar = new ProgressBar("Charge")
var charge = new HBoxContainer();
var chargeLabel = new Label {Text = "Charge:"};
ChargeBar = new ProgressBar
{
SizeFlagsHorizontal = Control.SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.FillExpand,
MinValue = 0.0f,
MaxValue = 1.0f,
Page = 0.0f,
Value = 0.5f
};
ChargePercentage = new Label("ChargePercentage");
ChargePercentage = new Label();
charge.AddChild(chargeLabel);
charge.AddChild(ChargeBar);
charge.AddChild(ChargePercentage);

View File

@@ -0,0 +1,77 @@
using Content.Shared.GameObjects.Components.Power;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Client.GameObjects.Components.Power
{
[UsedImplicitly]
public class PowerChargerVisualizer2D : AppearanceVisualizer
{
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
var sprite = entity.GetComponent<ISpriteComponent>();
// Base item
sprite.LayerMapSet(Layers.Base, sprite.AddLayerState("empty"));
// Light
sprite.LayerMapSet(Layers.Light, sprite.AddLayerState("light-off"));
sprite.LayerSetShader(Layers.Light, "unshaded");
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = component.Owner.GetComponent<ISpriteComponent>();
// Update base item
if (component.TryGetData(CellVisual.Occupied, out bool occupied))
{
// TODO: don't throw if it doesn't have a full state
sprite.LayerSetState(Layers.Base, occupied ? "full" : "empty");
}
else
{
sprite.LayerSetState(Layers.Base, "empty");
}
// Update lighting
if (component.TryGetData(CellVisual.Light, out CellChargerStatus status))
{
switch (status)
{
case CellChargerStatus.Off:
sprite.LayerSetState(Layers.Light, "light-off");
break;
case CellChargerStatus.Empty:
sprite.LayerSetState(Layers.Light, "light-empty");
break;
case CellChargerStatus.Charging:
sprite.LayerSetState(Layers.Light, "light-charging");
break;
case CellChargerStatus.Charged:
sprite.LayerSetState(Layers.Light, "light-charged");
break;
default:
sprite.LayerSetState(Layers.Light, "light-off");
break;
}
}
else
{
sprite.LayerSetState(Layers.Light, "light-off");
}
}
enum Layers
{
Base,
Light,
}
}
}

View File

@@ -0,0 +1,25 @@
using Content.Shared.GameObjects.Components.Power;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
namespace Content.Client.GameObjects.Components.Power
{
[UsedImplicitly]
public class PowerDeviceVisualizer2D : AppearanceVisualizer
{
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = component.Owner.GetComponent<ISpriteComponent>();
var powered = component.TryGetData(PowerDeviceVisuals.Powered, out bool poweredVar) && poweredVar;
sprite.LayerSetVisible(PowerDeviceVisualLayers.Powered, powered);
}
}
public enum PowerDeviceVisualLayers
{
Powered
}
}

View File

@@ -2,11 +2,6 @@
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Content.Client.GameObjects.Components.Power
{

View File

@@ -23,7 +23,7 @@ namespace Content.Client.GameObjects.Components.Research
public MaterialStorageComponent Storage { get; private set; }
public SharedLatheComponent Lathe { get; private set; }
public LatheDatabaseComponent Database { get; private set; }
public SharedLatheDatabaseComponent Database { get; private set; }
[ViewVariables]
public Queue<LatheRecipePrototype> QueuedRecipes => _queuedRecipes;
@@ -37,17 +37,18 @@ namespace Content.Client.GameObjects.Components.Research
protected override void Open()
{
base.Open();
IoCManager.InjectDependencies(this);
if (!Owner.Owner.TryGetComponent(out MaterialStorageComponent storage)
|| !Owner.Owner.TryGetComponent(out SharedLatheComponent lathe)
|| !Owner.Owner.TryGetComponent(out LatheDatabaseComponent database)) return;
|| !Owner.Owner.TryGetComponent(out SharedLatheDatabaseComponent database)) return;
Storage = storage;
Lathe = lathe;
Database = database;
menu = new LatheMenu {Owner = this};
menu = new LatheMenu(this);
queueMenu = new LatheQueueMenu { Owner = this };
menu.OnClose += Close;
@@ -57,6 +58,16 @@ namespace Content.Client.GameObjects.Components.Research
menu.QueueButton.OnPressed += (args) => { queueMenu.OpenCentered(); };
menu.ServerConnectButton.OnPressed += (args) =>
{
SendMessage(new SharedLatheComponent.LatheServerSelectionMessage());
};
menu.ServerSyncButton.OnPressed += (args) =>
{
SendMessage(new SharedLatheComponent.LatheServerSyncMessage());
};
storage.OnMaterialStorageChanged += menu.PopulateDisabled;
storage.OnMaterialStorageChanged += menu.PopulateMaterials;
@@ -74,10 +85,10 @@ namespace Content.Client.GameObjects.Components.Research
{
case SharedLatheComponent.LatheProducingRecipeMessage msg:
if (!_prototypeManager.TryIndex(msg.ID, out LatheRecipePrototype recipe)) break;
queueMenu.SetInfo(recipe);
queueMenu?.SetInfo(recipe);
break;
case SharedLatheComponent.LatheStoppedProducingRecipeMessage msg:
queueMenu.ClearInfo();
case SharedLatheComponent.LatheStoppedProducingRecipeMessage _:
queueMenu?.ClearInfo();
break;
case SharedLatheComponent.LatheFullQueueMessage msg:
_queuedRecipes.Clear();
@@ -86,7 +97,7 @@ namespace Content.Client.GameObjects.Components.Research
if (!_prototypeManager.TryIndex(id, out LatheRecipePrototype recipePrototype)) break;
_queuedRecipes.Enqueue(recipePrototype);
}
queueMenu.PopulateList();
queueMenu?.PopulateList();
break;
}
}

View File

@@ -0,0 +1,38 @@
using System;
using Content.Shared.GameObjects.Components.Research;
using Content.Shared.Research;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
namespace Content.Client.GameObjects.Components.Research
{
[RegisterComponent]
[ComponentReference(typeof(SharedLatheDatabaseComponent))]
public class ProtolatheDatabaseComponent : SharedProtolatheDatabaseComponent
{
#pragma warning disable CS0649
[Dependency]
private IPrototypeManager _prototypeManager;
#pragma warning restore
/// <summary>
/// Invoked when the database gets updated.
/// </summary>
public event Action OnDatabaseUpdated;
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if (!(curState is ProtolatheDatabaseState state)) return;
Clear();
foreach (var ID in state.Recipes)
{
if(!_prototypeManager.TryIndex(ID, out LatheRecipePrototype recipe)) continue;
AddRecipe(recipe);
}
OnDatabaseUpdated?.Invoke();
}
}
}

View File

@@ -0,0 +1,45 @@
using Content.Shared.GameObjects.Components.Research;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Shared.GameObjects.Components.UserInterface;
namespace Content.Client.GameObjects.Components.Research
{
public class ResearchClientBoundUserInterface : BoundUserInterface
{
private ResearchClientServerSelectionMenu _menu;
public ResearchClientBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
SendMessage(new SharedResearchClientComponent.ResearchClientSyncMessage());
}
protected override void Open()
{
base.Open();
_menu = new ResearchClientServerSelectionMenu() { Owner = this };
_menu.OnClose += Close;
_menu.OpenCentered();
}
public void SelectServer(int serverId)
{
SendMessage(new SharedResearchClientComponent.ResearchClientServerSelectedMessage(serverId));
}
public void DeselectServer()
{
SendMessage(new SharedResearchClientComponent.ResearchClientServerDeselectedMessage());
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (!(state is SharedResearchClientComponent.ResearchClientBoundInterfaceState rstate)) return;
_menu.Populate(rstate.ServerCount, rstate.ServerNames, rstate.ServerIds, rstate.SelectedServerId);
}
}
}

View File

@@ -0,0 +1,84 @@
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Research
{
public class ResearchClientServerSelectionMenu : SS14Window
{
private ItemList _servers;
private int _serverCount = 0;
private string[] _serverNames = new string[]{};
private int[] _serverIds = new int[]{};
private int _selectedServerId = -1;
#pragma warning disable 649
[Dependency] private readonly ILocalizationManager _localizationManager;
#pragma warning restore 649
protected override Vector2? CustomSize => (300, 300);
public ResearchClientBoundUserInterface Owner { get; set; }
public ResearchClientServerSelectionMenu()
{
IoCManager.InjectDependencies(this);
Title = _localizationManager.GetString("Research Server Selection");
_servers = new ItemList() {SelectMode = ItemList.ItemListSelectMode.Single};
_servers.OnItemSelected += OnItemSelected;
_servers.OnItemDeselected += OnItemDeselected;
var margin = new MarginContainer()
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.FillExpand,
/*MarginTop = 5f,
MarginLeft = 5f,
MarginRight = -5f,
MarginBottom = -5f,*/
};
margin.AddChild(_servers);
Contents.AddChild(margin);
}
public void OnItemSelected(ItemList.ItemListSelectedEventArgs itemListSelectedEventArgs)
{
Owner.SelectServer(_serverIds[itemListSelectedEventArgs.ItemIndex]);
}
public void OnItemDeselected(ItemList.ItemListDeselectedEventArgs itemListDeselectedEventArgs)
{
Owner.DeselectServer();
}
public void Populate(int serverCount, string[] serverNames, int[] serverIds, int selectedServerId)
{
_serverCount = serverCount;
_serverNames = serverNames;
_serverIds = serverIds;
_selectedServerId = selectedServerId;
// Disable so we can select the new selected server without triggering a new sync request.
_servers.OnItemSelected -= OnItemSelected;
_servers.OnItemDeselected -= OnItemDeselected;
_servers.Clear();
for (var i = 0; i < _serverCount; i++)
{
var id = _serverIds[i];
_servers.AddItem($"ID: {id} || {_serverNames[i]}");
if (id == _selectedServerId)
_servers[id].Selected = true;
}
_servers.OnItemSelected += OnItemSelected;
_servers.OnItemDeselected += OnItemDeselected;
}
}
}

View File

@@ -0,0 +1,80 @@
using Content.Client.Research;
using Content.Shared.GameObjects.Components.Research;
using Content.Shared.Research;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Shared.GameObjects.Components.UserInterface;
namespace Content.Client.GameObjects.Components.Research
{
public class ResearchConsoleBoundUserInterface : BoundUserInterface
{
public int Points { get; private set; } = 0;
public int PointsPerSecond { get; private set; } = 0;
private ResearchConsoleMenu _consoleMenu;
private TechnologyDatabaseComponent TechnologyDatabase;
public ResearchConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
SendMessage(new SharedResearchConsoleComponent.ConsoleServerSyncMessage());
}
protected override void Open()
{
base.Open();
if (!Owner.Owner.TryGetComponent(out TechnologyDatabase)) return;
_consoleMenu = new ResearchConsoleMenu(this);
_consoleMenu.OnClose += Close;
_consoleMenu.ServerSyncButton.OnPressed += (args) =>
{
SendMessage(new SharedResearchConsoleComponent.ConsoleServerSyncMessage());
};
_consoleMenu.ServerSelectionButton.OnPressed += (args) =>
{
SendMessage(new SharedResearchConsoleComponent.ConsoleServerSelectionMessage());
};
_consoleMenu.UnlockButton.OnPressed += (args) =>
{
SendMessage(new SharedResearchConsoleComponent.ConsoleUnlockTechnologyMessage(_consoleMenu.TechnologySelected.ID));
};
_consoleMenu.OpenCentered();
TechnologyDatabase.OnDatabaseUpdated += _consoleMenu.Populate;
}
public bool IsTechnologyUnlocked(TechnologyPrototype technology)
{
return TechnologyDatabase.IsTechnologyUnlocked(technology);
}
public bool CanUnlockTechnology(TechnologyPrototype technology)
{
return TechnologyDatabase.CanUnlockTechnology(technology);
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
var castState = (SharedResearchConsoleComponent.ResearchConsoleBoundInterfaceState)state;
Points = castState.Points;
PointsPerSecond = castState.PointsPerSecond;
// We update the user interface here.
_consoleMenu?.PopulatePoints();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_consoleMenu?.Dispose();
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using Content.Shared.GameObjects.Components.Research;
using Content.Shared.Research;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
namespace Content.Client.GameObjects.Components.Research
{
[RegisterComponent]
public class TechnologyDatabaseComponent : SharedTechnologyDatabaseComponent
{
/// <summary>
/// Event called when the database is updated.
/// </summary>
public event Action OnDatabaseUpdated;
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if (!(curState is TechnologyDatabaseState state)) return;
_technologies.Clear();
var protoManager = IoCManager.Resolve<IPrototypeManager>();
foreach (var techID in state.Technologies)
{
if (!protoManager.TryIndex(techID, out TechnologyPrototype technology)) continue;
_technologies.Add(technology);
}
OnDatabaseUpdated?.Invoke();
}
}
}

View File

@@ -1,10 +1,10 @@
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Sound;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
using Robust.Shared.Timers;
@@ -16,7 +16,9 @@ namespace Content.Client.GameObjects.Components.Sound
{
private readonly List<ScheduledSound> _schedules = new List<ScheduledSound>();
private AudioSystem _audioSystem;
private Random Random;
#pragma warning disable 649
[Dependency] private readonly IRobustRandom _random;
#pragma warning restore 649
public override void StopAllSounds()
{
@@ -46,9 +48,8 @@ namespace Content.Client.GameObjects.Components.Sound
public void Play(ScheduledSound schedule)
{
if (!schedule.Play) return;
if (Random == null) Random = new Random(Owner.Uid.GetHashCode() ^ DateTime.Now.GetHashCode());
Timer.Spawn((int) schedule.Delay + (Random.Next((int) schedule.RandomDelay)),() =>
Timer.Spawn((int) schedule.Delay + (_random.Next((int) schedule.RandomDelay)),() =>
{
if (!schedule.Play) return; // We make sure this hasn't changed.
if (_audioSystem == null) _audioSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>();
@@ -80,7 +81,7 @@ namespace Content.Client.GameObjects.Components.Sound
StopScheduledSound(msg.Filename);
break;
case StopAllSoundsMessage msg:
case StopAllSoundsMessage _:
StopAllSounds();
break;
}

View File

@@ -0,0 +1,61 @@
using Content.Client.UserInterface;
using Content.Client.Utility;
using Content.Shared.GameObjects.Components;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components
{
[RegisterComponent]
public class StackComponent : SharedStackComponent, IItemStatus
{
[ViewVariables] public int Count { get; private set; }
[ViewVariables] public int MaxCount { get; private set; }
[ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
public Control MakeControl() => new StatusControl(this);
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
var cast = (StackComponentState) curState;
Count = cast.Count;
MaxCount = cast.MaxCount;
_uiUpdateNeeded = true;
}
private sealed class StatusControl : Control
{
private readonly StackComponent _parent;
private readonly RichTextLabel _label;
public StatusControl(StackComponent parent)
{
_parent = parent;
_label = new RichTextLabel {StyleClasses = {NanoStyle.StyleClassItemStatus}};
AddChild(_label);
parent._uiUpdateNeeded = true;
}
protected override void Update(FrameEventArgs args)
{
base.Update(args);
if (!_parent._uiUpdateNeeded)
{
return;
}
_parent._uiUpdateNeeded = false;
_label.SetMarkup(Loc.GetString("Count: [color=white]{0}[/color]", _parent.Count));
}
}
}
}

View File

@@ -10,7 +10,6 @@ using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
namespace Content.Client.GameObjects.Components.Storage
{
@@ -30,7 +29,7 @@ namespace Content.Client.GameObjects.Components.Storage
base.OnAdd();
Window = new StorageWindow()
{ StorageEntity = this};
{StorageEntity = this};
}
public override void OnRemove()
@@ -39,12 +38,8 @@ namespace Content.Client.GameObjects.Components.Storage
base.OnRemove();
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
IComponent component = null)
{
switch (message)
{
@@ -53,10 +48,10 @@ namespace Content.Client.GameObjects.Components.Storage
HandleStorageMessage(msg);
break;
//Opens the UI
case OpenStorageUIMessage msg:
case OpenStorageUIMessage _:
OpenUI();
break;
case CloseStorageUIMessage msg:
case CloseStorageUIMessage _:
CloseUI();
break;
}
@@ -106,34 +101,29 @@ namespace Content.Client.GameObjects.Components.Storage
private Label Information;
public ClientStorageComponent StorageEntity;
protected override Vector2? CustomSize => (180, 320);
public StorageWindow()
{
Size = new Vector2(180.0f, 320.0f);
}
protected override void Initialize()
{
base.Initialize();
Title = "Storage Item";
RectClipContent = true;
VSplitContainer = new VBoxContainer("VSplitContainer");
Information = new Label("Information")
VSplitContainer = new VBoxContainer();
Information = new Label
{
Text = "Items: 0 Volume: 0/0 Stuff",
SizeFlagsVertical = SizeFlags.ShrinkCenter
};
VSplitContainer.AddChild(Information);
var listScrollContainer = new ScrollContainer("ListScrollContainer")
var listScrollContainer = new ScrollContainer
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.FillExpand,
HScrollEnabled = true,
VScrollEnabled = true
};
EntityList = new VBoxContainer("EntityList")
EntityList = new VBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand
};
@@ -182,7 +172,8 @@ namespace Content.Client.GameObjects.Components.Storage
//Sets information about entire storage container current capacity
if (StorageEntity.StorageCapacityMax != 0)
{
Information.Text = String.Format("Items: {0}, Stored: {1}/{2}", storagelist.Count, StorageEntity.StorageSizeUsed, StorageEntity.StorageCapacityMax);
Information.Text = String.Format("Items: {0}, Stored: {1}/{2}", storagelist.Count,
StorageEntity.StorageSizeUsed, StorageEntity.StorageCapacityMax);
}
else
{
@@ -196,7 +187,7 @@ namespace Content.Client.GameObjects.Components.Storage
/// <param name="args"></param>
private void OnItemButtonToggled(BaseButton.ButtonToggledEventArgs args)
{
var control = (EntityButton)args.Button.Parent;
var control = (EntityButton) args.Button.Parent;
args.Button.Pressed = false;
StorageEntity.Interact(control.EntityuID);
}
@@ -208,17 +199,15 @@ namespace Content.Client.GameObjects.Components.Storage
private class EntityButton : PanelContainer
{
public EntityUid EntityuID { get; set; }
public Button ActualButton { get; private set; }
public SpriteView EntitySpriteView { get; private set; }
public Control EntityControl { get; private set; }
public Label EntityName { get; private set; }
public Label EntitySize { get; private set; }
public Button ActualButton { get; }
public SpriteView EntitySpriteView { get; }
public Control EntityControl { get; }
public Label EntityName { get; }
public Label EntitySize { get; }
protected override void Initialize()
public EntityButton()
{
base.Initialize();
ActualButton = new Button("Button")
ActualButton = new Button
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsVertical = SizeFlags.FillExpand,
@@ -227,12 +216,12 @@ namespace Content.Client.GameObjects.Components.Storage
};
AddChild(ActualButton);
var hBoxContainer = new HBoxContainer("HBoxContainer") {MouseFilter = MouseFilterMode.Ignore};
EntitySpriteView = new SpriteView("SpriteView")
var hBoxContainer = new HBoxContainer {MouseFilter = MouseFilterMode.Ignore};
EntitySpriteView = new SpriteView
{
CustomMinimumSize = new Vector2(32.0f, 32.0f), MouseFilter = MouseFilterMode.Ignore
};
EntityName = new Label("Name")
EntityName = new Label
{
SizeFlagsVertical = SizeFlags.ShrinkCenter,
Text = "Backpack",
@@ -241,23 +230,23 @@ namespace Content.Client.GameObjects.Components.Storage
hBoxContainer.AddChild(EntitySpriteView);
hBoxContainer.AddChild(EntityName);
EntityControl = new Control("Control")
EntityControl = new Control
{
SizeFlagsHorizontal = SizeFlags.FillExpand, MouseFilter = MouseFilterMode.Ignore
};
EntitySize = new Label("Size")
EntitySize = new Label
{
SizeFlagsVertical = SizeFlags.ShrinkCenter,
Text = "Size 6",
Align = Label.AlignMode.Right,
AnchorLeft = 1.0f,
/*AnchorLeft = 1.0f,
AnchorRight = 1.0f,
AnchorBottom = 0.5f,
AnchorTop = 0.5f,
MarginLeft = -38.0f,
MarginTop = -7.0f,
MarginRight = -5.0f,
MarginBottom = 7.0f
MarginBottom = 7.0f*/
};
EntityControl.AddChild(EntitySize);

View File

@@ -1,22 +1,24 @@
using Content.Shared.Maps;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;
namespace Content.Client.GameObjects.Components
{
/// <summary>
/// Simple component that automatically hides the sibling <see cref="ISpriteComponent"/> when the tile it's on
/// is not a sub floor (plating).
/// Simple component that automatically hides the sibling
/// <see cref="ISpriteComponent" /> when the tile it's on is not a sub floor
/// (plating).
/// </summary>
/// <seealso cref="ContentTileDefinition.IsSubFloor"/>
/// <seealso cref="P:Content.Shared.Maps.ContentTileDefinition.IsSubFloor" />
[RegisterComponent]
public sealed class SubFloorHideComponent : Component
{
private SnapGridComponent _snapGridComponent;
/// <inheritdoc />
public override string Name => "SubFloorHide";
/// <inheritdoc />
public override void Initialize()
{
base.Initialize();
@@ -24,28 +26,32 @@ namespace Content.Client.GameObjects.Components
_snapGridComponent = Owner.GetComponent<SnapGridComponent>();
}
public override void Startup()
/// <inheritdoc />
protected override void Startup()
{
base.Startup();
_snapGridComponent.OnPositionChanged += SnapGridOnPositionChanged;
Owner.EntityManager.RaiseEvent(Owner, new SubFloorHideDirtyEvent());
Owner.EntityManager.EventBus.RaiseEvent(Owner, new SubFloorHideDirtyEvent());
}
public override void Shutdown()
/// <inheritdoc />
protected override void Shutdown()
{
base.Shutdown();
if(Owner.Transform.Running == false)
return;
_snapGridComponent.OnPositionChanged -= SnapGridOnPositionChanged;
Owner.EntityManager.EventBus.RaiseEvent(Owner, new SubFloorHideDirtyEvent());
}
private void SnapGridOnPositionChanged()
{
Owner.EntityManager.RaiseEvent(Owner, new SubFloorHideDirtyEvent());
Owner.EntityManager.EventBus.RaiseEvent(Owner, new SubFloorHideDirtyEvent());
}
}
internal sealed class SubFloorHideDirtyEvent : EntitySystemMessage
{
}
internal sealed class SubFloorHideDirtyEvent : EntitySystemMessage { }
}

View File

@@ -0,0 +1,61 @@
using Content.Client.VendingMachines;
using Content.Shared.GameObjects.Components.VendingMachines;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.VendingMachines
{
class VendingMachineBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private VendingMachineMenu _menu;
public SharedVendingMachineComponent VendingMachine { get; private set; }
public VendingMachineBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
SendMessage(new SharedVendingMachineComponent.InventorySyncRequestMessage());
}
protected override void Open()
{
base.Open();
if(!Owner.Owner.TryGetComponent(out SharedVendingMachineComponent vendingMachine))
{
return;
}
VendingMachine = vendingMachine;
_menu = new VendingMachineMenu() { Owner = this, Title = Owner.Owner.Name };
_menu.Populate(VendingMachine.Inventory);
_menu.OnClose += Close;
_menu.OpenCentered();
}
public void Eject(string ID)
{
SendMessage(new SharedVendingMachineComponent.VendingMachineEjectMessage(ID));
}
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
{
switch(message)
{
case SharedVendingMachineComponent.VendingMachineInventoryMessage msg:
_menu.Populate(msg.Inventory);
break;
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if(!disposing) { return; }
_menu?.Dispose();
}
}
}

View File

@@ -0,0 +1,98 @@
using System;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Client.GameObjects.Components.Animations;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using YamlDotNet.RepresentationModel;
using static Content.Shared.GameObjects.Components.VendingMachines.SharedVendingMachineComponent;
namespace Content.Client.GameObjects.Components.VendingMachines
{
public class VendingMachineVisualizer2D : AppearanceVisualizer
{
// TODO: The length of these animations is supposed to be dictated
// by the vending machine's pack prototype's `AnimationDuration`
// but we have no good way of passing that data from the server
// to the client at the moment. Rework Visualizers?
private const string DeniedAnimationKey = "deny";
private const string EjectAnimationKey = "eject";
private Animation _deniedAnimation;
private Animation _ejectAnimation;
public override void LoadData(YamlMappingNode node)
{
base.LoadData(node);
_deniedAnimation = new Animation {Length = TimeSpan.FromSeconds(1.2f)};
{
var flick = new AnimationTrackSpriteFlick();
_deniedAnimation.AnimationTracks.Add(flick);
flick.LayerKey = VendingMachineVisualLayers.Base;
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("deny", 0f));
}
_ejectAnimation = new Animation {Length = TimeSpan.FromSeconds(1.2f)};
{
var flick = new AnimationTrackSpriteFlick();
_ejectAnimation.AnimationTracks.Add(flick);
flick.LayerKey = VendingMachineVisualLayers.Base;
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("eject", 0f));
}
}
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
if (!entity.HasComponent<AnimationPlayerComponent>())
{
entity.AddComponent<AnimationPlayerComponent>();
}
}
public override void OnChangeData(AppearanceComponent component)
{
var sprite = component.Owner.GetComponent<ISpriteComponent>();
var animPlayer = component.Owner.GetComponent<AnimationPlayerComponent>();
if (!component.TryGetData(VendingMachineVisuals.VisualState, out VendingMachineVisualState state))
{
state = VendingMachineVisualState.Normal;
}
switch (state)
{
case VendingMachineVisualState.Normal:
sprite.LayerSetState(VendingMachineVisualLayers.Base, "normal");
break;
case VendingMachineVisualState.Off:
sprite.LayerSetState(VendingMachineVisualLayers.Base, "off");
break;
case VendingMachineVisualState.Broken:
sprite.LayerSetState(VendingMachineVisualLayers.Base, "broken");
break;
case VendingMachineVisualState.Deny:
if (!animPlayer.HasRunningAnimation(DeniedAnimationKey))
{
animPlayer.Play(_deniedAnimation, DeniedAnimationKey);
}
break;
case VendingMachineVisualState.Eject:
if (!animPlayer.HasRunningAnimation(EjectAnimationKey))
{
animPlayer.Play(_ejectAnimation, EjectAnimationKey);
}
break;
default:
throw new ArgumentOutOfRangeException();
}
}
public enum VendingMachineVisualLayers
{
Base,
}
}
}

View File

@@ -0,0 +1,67 @@
using Content.Shared.GameObjects.Components.Weapons.Melee;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Weapons.Melee
{
[RegisterComponent]
public sealed class MeleeWeaponArcAnimationComponent : Component
{
public override string Name => "MeleeWeaponArcAnimation";
private MeleeWeaponAnimationPrototype _meleeWeaponAnimation;
private float _timer;
private SpriteComponent _sprite;
private Angle _baseAngle;
public override void Initialize()
{
base.Initialize();
_sprite = Owner.GetComponent<SpriteComponent>();
}
public void SetData(MeleeWeaponAnimationPrototype prototype, Angle baseAngle)
{
_meleeWeaponAnimation = prototype;
_sprite.AddLayer(new RSI.StateId(prototype.State));
_baseAngle = baseAngle;
}
internal void Update(float frameTime)
{
if (_meleeWeaponAnimation == null)
{
return;
}
_timer += frameTime;
var (r, g, b, a) =
Vector4.Clamp(_meleeWeaponAnimation.Color + _meleeWeaponAnimation.ColorDelta * _timer, Vector4.Zero, Vector4.One);
_sprite.Color = new Color(r, g, b, a);
switch (_meleeWeaponAnimation.ArcType)
{
case WeaponArcType.Slash:
var angle = Angle.FromDegrees(_meleeWeaponAnimation.Width)/2;
Owner.Transform.LocalRotation =
_baseAngle + Angle.Lerp(-angle, angle, (float) (_timer / _meleeWeaponAnimation.Length.TotalSeconds));
break;
case WeaponArcType.Poke:
_sprite.Offset += (_meleeWeaponAnimation.Speed * frameTime, 0);
break;
}
if (_meleeWeaponAnimation.Length.TotalSeconds <= _timer)
{
Owner.Delete();
}
}
}
}

View File

@@ -0,0 +1,283 @@
using System;
using Content.Client.Animations;
using Content.Client.UserInterface;
using Content.Client.Utility;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Weapons.Ranged;
using Robust.Client.Animations;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Animations;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using static Content.Client.StaticIoC;
namespace Content.Client.GameObjects.Components.Weapons.Ranged
{
[RegisterComponent]
public class BallisticMagazineWeaponComponent : Component, IItemStatus
{
private static readonly Animation AlarmAnimationSmg = new Animation
{
Length = TimeSpan.FromSeconds(1.4),
AnimationTracks =
{
new AnimationTrackControlProperty
{
// These timings match the SMG audio file.
Property = nameof(Label.FontColorOverride),
InterpolationMode = AnimationInterpolationMode.Previous,
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(Color.Red, 0.1f),
new AnimationTrackProperty.KeyFrame(null, 0.3f),
new AnimationTrackProperty.KeyFrame(Color.Red, 0.2f),
new AnimationTrackProperty.KeyFrame(null, 0.3f),
new AnimationTrackProperty.KeyFrame(Color.Red, 0.2f),
new AnimationTrackProperty.KeyFrame(null, 0.3f),
}
}
}
};
private static readonly Animation AlarmAnimationLmg = new Animation
{
Length = TimeSpan.FromSeconds(0.75),
AnimationTracks =
{
new AnimationTrackControlProperty
{
// These timings match the SMG audio file.
Property = nameof(Label.FontColorOverride),
InterpolationMode = AnimationInterpolationMode.Previous,
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(Color.Red, 0.0f),
new AnimationTrackProperty.KeyFrame(null, 0.15f),
new AnimationTrackProperty.KeyFrame(Color.Red, 0.15f),
new AnimationTrackProperty.KeyFrame(null, 0.15f),
new AnimationTrackProperty.KeyFrame(Color.Red, 0.15f),
new AnimationTrackProperty.KeyFrame(null, 0.15f),
}
}
}
};
public override string Name => "BallisticMagazineWeapon";
public override uint? NetID => ContentNetIDs.BALLISTIC_MAGAZINE_WEAPON;
private StatusControl _statusControl;
/// <summary>
/// True if a bullet is chambered.
/// </summary>
[ViewVariables]
public bool Chambered { get; private set; }
/// <summary>
/// Count of bullets in the magazine.
/// </summary>
/// <remarks>
/// Null if no magazine is inserted.
/// </remarks>
[ViewVariables]
public (int count, int max)? MagazineCount { get; private set; }
[ViewVariables(VVAccess.ReadWrite)] private bool _isLmgAlarmAnimation;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _isLmgAlarmAnimation, "lmg_alarm_animation", false);
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
var cast = (BallisticMagazineWeaponComponentState) curState;
Chambered = cast.Chambered;
MagazineCount = cast.MagazineCount;
_statusControl?.Update();
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
IComponent component = null)
{
switch (message)
{
case BmwComponentAutoEjectedMessage _:
_statusControl?.PlayAlarmAnimation();
return;
}
base.HandleMessage(message, netChannel, component);
}
public Control MakeControl()
{
_statusControl = new StatusControl(this);
_statusControl.Update();
return _statusControl;
}
public void DestroyControl(Control control)
{
if (_statusControl == control)
{
_statusControl = null;
}
}
private sealed class StatusControl : Control
{
private readonly BallisticMagazineWeaponComponent _parent;
private readonly HBoxContainer _bulletsListTop;
private readonly HBoxContainer _bulletsListBottom;
private readonly TextureRect _chamberedBullet;
private readonly Label _noMagazineLabel;
public StatusControl(BallisticMagazineWeaponComponent parent)
{
_parent = parent;
SizeFlagsHorizontal = SizeFlags.FillExpand;
SizeFlagsVertical = SizeFlags.ShrinkCenter;
AddChild(new VBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsVertical = SizeFlags.ShrinkCenter,
SeparationOverride = 0,
Children =
{
(_bulletsListTop = new HBoxContainer {SeparationOverride = 0}),
new HBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
new Control
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
Children =
{
(_bulletsListBottom = new HBoxContainer
{
SizeFlagsVertical = SizeFlags.ShrinkCenter,
SeparationOverride = 0
}),
(_noMagazineLabel = new Label
{
Text = "No Magazine!",
StyleClasses = {NanoStyle.StyleClassItemStatus}
})
}
},
(_chamberedBullet = new TextureRect
{
Texture = ResC.GetTexture("/Textures/UserInterface/status/bullets/chambered.png"),
SizeFlagsVertical = SizeFlags.ShrinkCenter,
SizeFlagsHorizontal = SizeFlags.ShrinkEnd | SizeFlags.Fill,
})
}
}
}
});
}
public void Update()
{
_chamberedBullet.ModulateSelfOverride =
_parent.Chambered ? Color.FromHex("#d7df60") : Color.Black;
_bulletsListTop.RemoveAllChildren();
_bulletsListBottom.RemoveAllChildren();
if (_parent.MagazineCount == null)
{
_noMagazineLabel.Visible = true;
return;
}
var (count, capacity) = _parent.MagazineCount.Value;
_noMagazineLabel.Visible = false;
string texturePath;
if (capacity <= 20)
{
texturePath = "/Textures/UserInterface/status/bullets/normal.png";
}
else if (capacity <= 30)
{
texturePath = "/Textures/UserInterface/status/bullets/small.png";
}
else
{
texturePath = "/Textures/UserInterface/status/bullets/tiny.png";
}
var texture = ResC.GetTexture(texturePath);
const int tinyMaxRow = 60;
if (capacity > tinyMaxRow)
{
FillBulletRow(_bulletsListBottom, Math.Min(tinyMaxRow, count), tinyMaxRow, texture);
FillBulletRow(_bulletsListTop, Math.Max(0, count - tinyMaxRow), capacity - tinyMaxRow, texture);
}
else
{
FillBulletRow(_bulletsListBottom, count, capacity, texture);
}
}
private static void FillBulletRow(Control container, int count, int capacity, Texture texture)
{
var colorA = Color.FromHex("#b68f0e");
var colorB = Color.FromHex("#d7df60");
var colorGoneA = Color.FromHex("#000000");
var colorGoneB = Color.FromHex("#222222");
var altColor = false;
for (var i = count; i < capacity; i++)
{
container.AddChild(new TextureRect
{
Texture = texture,
ModulateSelfOverride = altColor ? colorGoneA : colorGoneB
});
altColor ^= true;
}
for (var i = 0; i < count; i++)
{
container.AddChild(new TextureRect
{
Texture = texture,
ModulateSelfOverride = altColor ? colorA : colorB
});
altColor ^= true;
}
}
protected override Vector2 CalculateMinimumSize()
{
return Vector2.ComponentMax((0, 15), base.CalculateMinimumSize());
}
public void PlayAlarmAnimation()
{
var animation = _parent._isLmgAlarmAnimation ? AlarmAnimationLmg : AlarmAnimationSmg;
_noMagazineLabel.PlayAnimation(animation, "alarm");
}
}
}
}

View File

@@ -37,8 +37,8 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged
return;
}
var step = ContentHelpers.RoundToLevels(current, capacity, _steps);
// capacity is - 1 as normally a bullet is chambered so max state is virtually never hit.
var step = ContentHelpers.RoundToLevels(current, capacity - 1, _steps);
sprite.LayerSetState(0, $"{_baseState}-{step}");
}
else

View File

@@ -1,8 +1,5 @@
using System;
using Content.Shared.GameObjects.Components.Weapons.Ranged;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Map;
namespace Content.Client.GameObjects.Components.Weapons.Ranged
@@ -10,20 +7,9 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged
[RegisterComponent]
public sealed class ClientRangedWeaponComponent : SharedRangedWeaponComponent
{
private TimeSpan _lastFireTime;
private int _tick;
public void TryFire(GridCoordinates worldPos)
public void SyncFirePos(GridCoordinates worldPos)
{
var curTime = IoCManager.Resolve<IGameTiming>().CurTime;
var span = curTime - _lastFireTime;
if (span.TotalSeconds < 1 / FireRate)
{
return;
}
_lastFireTime = curTime;
SendNetworkMessage(new FireMessage(worldPos, _tick++));
SendNetworkMessage(new SyncFirePosMessage(worldPos));
}
}
}

View File

@@ -2,7 +2,6 @@
using Content.Shared.Utility;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;

View File

@@ -0,0 +1,73 @@
using System;
using Content.Client.UserInterface;
using Content.Client.Utility;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components
{
[RegisterComponent]
public class WelderComponent : Component, IItemStatus
{
public override string Name => "Welder";
public override uint? NetID => ContentNetIDs.WELDER;
[ViewVariables] public float FuelCapacity { get; private set; }
[ViewVariables] public float Fuel { get; private set; }
[ViewVariables] public bool Activated { get; private set; }
[ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
var cast = (WelderComponentState) curState;
FuelCapacity = cast.FuelCapacity;
Fuel = cast.Fuel;
Activated = cast.Activated;
_uiUpdateNeeded = true;
}
public Control MakeControl() => new StatusControl(this);
private sealed class StatusControl : Control
{
private readonly WelderComponent _parent;
private readonly RichTextLabel _label;
public StatusControl(WelderComponent parent)
{
_parent = parent;
_label = new RichTextLabel {StyleClasses = {NanoStyle.StyleClassItemStatus}};
AddChild(_label);
parent._uiUpdateNeeded = true;
}
protected override void Update(FrameEventArgs args)
{
base.Update(args);
if (!_parent._uiUpdateNeeded)
{
return;
}
_parent._uiUpdateNeeded = false;
var fuelCap = _parent.FuelCapacity;
var fuel = _parent.Fuel;
_label.SetMarkup(Loc.GetString("Fuel: [color={0}]{1}/{2}[/color]",
fuel < fuelCap / 4f ? "darkorange" : "orange", Math.Round(fuel), fuelCap));
}
}
}
}

View File

@@ -1,4 +1,4 @@
using Content.Client.GameObjects.EntitySystems;
using Content.Client.GameObjects.EntitySystems;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
@@ -25,12 +25,13 @@ namespace Content.Client.GameObjects.Components
_snapGrid = Owner.GetComponent<SnapGridComponent>();
}
public override void Startup()
/// <inheritdoc />
protected override void Startup()
{
base.Startup();
_snapGrid.OnPositionChanged += SnapGridOnPositionChanged;
Owner.EntityManager.RaiseEvent(Owner, new WindowSmoothDirtyEvent());
Owner.EntityManager.EventBus.RaiseEvent(Owner, new WindowSmoothDirtyEvent());
var state0 = $"{_stateBase}0";
_sprite.LayerMapSet(CornerLayers.SE, _sprite.AddLayerState(state0));
@@ -43,7 +44,8 @@ namespace Content.Client.GameObjects.Components
_sprite.LayerSetDirOffset(CornerLayers.SW, SpriteComponent.DirectionOffset.Clockwise);
}
public override void Shutdown()
/// <inheritdoc />
protected override void Shutdown()
{
_snapGrid.OnPositionChanged -= SnapGridOnPositionChanged;
@@ -52,7 +54,7 @@ namespace Content.Client.GameObjects.Components
private void SnapGridOnPositionChanged()
{
Owner.EntityManager.RaiseEvent(Owner, new WindowSmoothDirtyEvent());
Owner.EntityManager.EventBus.RaiseEvent(Owner, new WindowSmoothDirtyEvent());
}
public void UpdateSprite()

View File

@@ -0,0 +1,41 @@
using System;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using static Content.Shared.GameObjects.Components.SharedWiresComponent;
namespace Content.Client.GameObjects.Components.Wires
{
public class WiresBoundUserInterface : BoundUserInterface
{
#pragma warning disable 649
[Dependency] private readonly ILocalizationManager _localizationManager;
#pragma warning restore 649
public WiresBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
private WiresMenu _menu;
protected override void Open()
{
base.Open();
_menu = new WiresMenu(_localizationManager) {Owner = this};
_menu.OnClose += Close;
_menu.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
_menu.Populate((WiresBoundUserInterfaceState) state);
}
public void PerformAction(Guid guid, WiresAction action)
{
SendMessage(new WiresActionMessage(guid, action));
}
}
}

View File

@@ -0,0 +1,66 @@
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using static Content.Shared.GameObjects.Components.SharedWiresComponent;
namespace Content.Client.GameObjects.Components.Wires
{
public class WiresMenu : SS14Window
{
private readonly ILocalizationManager _localizationManager;
protected override Vector2? CustomSize => (300, 150);
public WiresBoundUserInterface Owner { get; set; }
private readonly VBoxContainer _wiresContainer;
public WiresMenu(ILocalizationManager localizationManager)
{
_localizationManager = localizationManager;
Title = _localizationManager.GetString("Wires");
_wiresContainer = new VBoxContainer();
Contents.AddChild(_wiresContainer);
}
public void Populate(WiresBoundUserInterfaceState state)
{
_wiresContainer.RemoveAllChildren();
foreach (var wire in state.WiresList)
{
var container = new HBoxContainer();
var newLabel = new Label()
{
Text = $"{_localizationManager.GetString(wire.Color.Name())}: ",
FontColorOverride = wire.Color,
};
container.AddChild(newLabel);
var newButton = new Button()
{
Text = _localizationManager.GetString("Pulse"),
};
newButton.OnPressed += _ => Owner.PerformAction(wire.Guid, WiresAction.Pulse);
container.AddChild(newButton);
newButton = new Button()
{
Text = wire.IsCut ? _localizationManager.GetString("Mend") : _localizationManager.GetString("Cut"),
};
newButton.OnPressed += _ => Owner.PerformAction(wire.Guid, wire.IsCut ? WiresAction.Mend : WiresAction.Cut);
container.AddChild(newButton);
_wiresContainer.AddChild(container);
}
foreach (var status in state.Statuses)
{
var container = new HBoxContainer();
container.AddChild(new Label
{
Text = status
});
_wiresContainer.AddChild(container);
}
}
}
}

View File

@@ -0,0 +1,25 @@
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using static Content.Shared.GameObjects.Components.SharedWiresComponent;
namespace Content.Client.GameObjects.Components.Wires
{
public class WiresVisualizer2D : AppearanceVisualizer
{
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (component.TryGetData<bool>(WiresVisuals.MaintenancePanelState, out var state))
{
sprite.LayerSetVisible(WiresVisualLayers.MaintenancePanel, state);
}
}
public enum WiresVisualLayers
{
MaintenancePanel,
}
}
}

View File

@@ -0,0 +1,177 @@
using Content.Client.GameObjects.Components.Mobs;
using Content.Client.UserInterface;
using Content.Client.Utility;
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystemMessages;
using Content.Shared.Input;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Interfaces.Input;
using Robust.Client.Player;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Players;
using static Content.Client.StaticIoC;
namespace Content.Client.GameObjects.EntitySystems
{
public sealed class CombatModeSystem : EntitySystem
{
private const float AttackTimeThreshold = 0.15f;
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IInputManager _inputManager;
[Dependency] private readonly IOverlayManager _overlayManager;
#pragma warning restore 649
private InputSystem _inputSystem;
public bool UseOrAttackIsDown { get; private set; }
private float _timeHeld;
public override void Initialize()
{
base.Initialize();
_gameHud.OnCombatModeChanged = OnCombatModeChanged;
_gameHud.OnTargetingZoneChanged = OnTargetingZoneChanged;
_inputSystem = EntitySystemManager.GetEntitySystem<InputSystem>();
_inputSystem.BindMap.BindFunction(ContentKeyFunctions.UseOrAttack, new InputHandler(this));
_overlayManager.AddOverlay(new CombatModeOverlay(this));
}
public override void Shutdown()
{
base.Shutdown();
_overlayManager.RemoveOverlay(nameof(CombatModeOverlay));
}
private bool IsInCombatMode()
{
var entity = _playerManager.LocalPlayer.ControlledEntity;
if (entity == null || !entity.TryGetComponent(out CombatModeComponent combatMode))
{
return false;
}
return combatMode.IsInCombatMode;
}
private void OnTargetingZoneChanged(TargetingZone obj)
{
RaiseNetworkEvent(new CombatModeSystemMessages.SetTargetZoneMessage(obj));
}
private void OnCombatModeChanged(bool obj)
{
RaiseNetworkEvent(new CombatModeSystemMessages.SetCombatModeActiveMessage(obj));
// Just in case.
UseOrAttackIsDown = false;
}
private bool HandleInputMessage(ICommonSession session, InputCmdMessage message)
{
if (!(message is FullInputCmdMessage msg))
return false;
void SendMsg(BoundKeyFunction function, BoundKeyState state)
{
var functionId = _inputManager.NetworkBindMap.KeyFunctionID(function);
var sendMsg = new FullInputCmdMessage(msg.Tick, functionId, state,
msg.Coordinates, msg.ScreenCoordinates, msg.Uid);
_inputSystem.HandleInputCommand(session, function, sendMsg);
}
// If we are not in combat mode, relay it as a regular Use instead.
if (!IsInCombatMode())
{
SendMsg(EngineKeyFunctions.Use, msg.State);
return true;
}
if (msg.State == BoundKeyState.Down)
{
UseOrAttackIsDown = true;
_timeHeld = 0;
return true;
}
// Up.
if (UseOrAttackIsDown && _timeHeld >= AttackTimeThreshold)
{
// Attack.
SendMsg(ContentKeyFunctions.Attack, BoundKeyState.Down);
SendMsg(ContentKeyFunctions.Attack, BoundKeyState.Up);
}
else
{
// Use.
SendMsg(EngineKeyFunctions.Use, BoundKeyState.Down);
SendMsg(EngineKeyFunctions.Use, BoundKeyState.Up);
}
UseOrAttackIsDown = false;
return true;
}
public override void FrameUpdate(float frameTime)
{
if (UseOrAttackIsDown)
{
_timeHeld += frameTime;
}
}
// Custom input handler type so we get the ENTIRE InputCmdMessage.
private sealed class InputHandler : InputCmdHandler
{
private readonly CombatModeSystem _combatModeSystem;
public InputHandler(CombatModeSystem combatModeSystem)
{
_combatModeSystem = combatModeSystem;
}
public override bool HandleCmdMessage(ICommonSession session, InputCmdMessage message)
{
return _combatModeSystem.HandleInputMessage(session, message);
}
}
private sealed class CombatModeOverlay : Overlay
{
private readonly CombatModeSystem _system;
public CombatModeOverlay(CombatModeSystem system) : base(nameof(CombatModeOverlay))
{
_system = system;
}
protected override void Draw(DrawingHandleBase handle)
{
var screenHandle = (DrawingHandleScreen) handle;
var mousePos = IoCManager.Resolve<IInputManager>().MouseScreenPosition;
if (_system.UseOrAttackIsDown && _system._timeHeld > AttackTimeThreshold)
{
var tex = ResC.GetTexture($"/Textures/Objects/Tools/toolbox_r.png");
screenHandle.DrawTextureRect(tex, UIBox2.FromDimensions(mousePos, tex.Size * 2));
}
}
}
}
}

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