1106 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
Pieter-Jan Briers
ceb8cc8421 Use automatic component registration. 2019-07-31 15:07:54 +02:00
Pieter-Jan Briers
a90d7a645c Update submodule. 2019-07-31 15:04:27 +02:00
Acruid
2ea8bbf4eb Reagents & Solutions (#280)
* Added the ReagentPrototype class.

* Added the new Solution class.

* Added new shared SolutionComponent to the ECS system.

* Added some basic element and chemical reagent prototypes.

* Added a new Beaker item utilizing the SolutionComponent. This is a testing/debug entity, and should be removed or changed soon.

* Added filters for code coverage.

* Nightly work.

* Added the server SolutionComponent class.

* Added a bucket.
Verbs set up for solution interaction.

* Adds water tank entity to the game.

* Added a full water tank entity.
Solutions are properly serialized.
Solution can be poured between two containers.

* Solution class can now be enumerated.
SolutionComponent now calculates the color of the solution.

* Minor Cleanup.
2019-07-31 14:10:06 +02:00
Pieter-Jan Briers
41b72d5aa2 Remove visibility set from StorageWindow. 2019-07-31 13:48:50 +02:00
Pieter-Jan Briers
b6ab0298f4 Make sure speech bubbles stay below the HUD. 2019-07-31 13:43:59 +02:00
Pieter-Jan Briers
ec771abfaa Update submodule. 2019-07-31 13:43:19 +02:00
Pieter-Jan Briers
ad9d7573d6 Fix crash with speech bubbles maybe. 2019-07-31 13:17:06 +02:00
Pieter-Jan Briers
0086e60b6a Speech bubbles yo. 2019-07-30 23:13:05 +02:00
Pieter-Jan Briers
388cc8fdde Update submodule. 2019-07-30 23:11:54 +02:00
Pieter-Jan Briers
211dd56f94 Correctly send saying entity UID to clients. 2019-07-30 23:11:27 +02:00
Pieter-Jan Briers
1df5be6570 Lerp is a word. 2019-07-30 21:50:13 +02:00
Pieter-Jan Briers
ffee6bbd35 Make light_tube_on.ogg mono. 2019-07-30 14:00:33 +02:00
Pieter-Jan Briers
1132e5b6a7 Update submodule: light cleanup. 2019-07-30 13:31:46 +02:00
Pieter-Jan Briers
15d81c1876 Fix build scripts. 2019-07-30 12:57:08 +02:00
Pieter-Jan Briers
29b00fc633 Launcher packaged. 2019-07-30 01:08:36 +02:00
Pieter-Jan Briers
e00a737285 Animal Silence has been moved to content. 2019-07-30 01:08:22 +02:00
Pieter-Jan Briers
dc6e65559b Launching the client closes the launcher now. 2019-07-30 00:55:54 +02:00
Pieter-Jan Briers
ad695702ac Robust Lite, also known as "Qt was too hard to distribute". 2019-07-29 23:19:10 +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
Pieter-Jan Briers
930cb61af8 Flashlight improvements:
1. Sound effects
2. fixed sprite having a hole
2019-07-27 11:56:36 +02:00
ShadowCommander
d3053c3c8c Fixed HandsGui children so that HandsGui is clickable 2019-07-27 00:32:59 -07:00
Pieter-Jan Briers
2e230c089b Opening lockers or crates makes sound now. 2019-07-27 09:23:00 +02:00
Pieter-Jan Briers
aea7d01eaa More crate types. 2019-07-26 21:43:01 +02:00
Pieter-Jan Briers
448ee88357 Re-organize entity prototypes. 2019-07-26 19:43:20 +02:00
Pieter-Jan Briers
c635aeba79 Fix crash related to game HUD upon joining. 2019-07-26 17:56:07 +02:00
Pieter-Jan Briers
ff90bb4802 Update submodule. 2019-07-26 17:55:50 +02:00
Pieter-Jan Briers
d906bcda03 Eris low walls & windows.
Still needs work blocked by better entity parenting, but oh well.
2019-07-26 13:53:18 +02:00
Pieter-Jan Briers
a162564516 Update submodule. 2019-07-26 13:53:18 +02:00
Acruid
232e4951b0 Fixes InteractionSystem so that a player can pick up an item from a grid that isn't at the world origin. 2019-07-24 10:59:19 -07:00
Pieter-Jan Briers
824f5f6755 Make mob bounding boxes square. 2019-07-24 16:16:11 +02:00
Pieter-Jan Briers
248e3f686b Fix APC window. 2019-07-24 12:36:18 +02:00
Pieter-Jan Briers
69f5600a51 Update submodule 2019-07-24 09:33:09 +02:00
Pieter-Jan Briers
1fbb5915aa Better inventory window, inventory buttons on game HUD.
Part of #272
2019-07-23 23:24:47 +02:00
Pieter-Jan Briers
4d202a7678 Use new inventory icons for hands. 2019-07-20 16:02:19 +02:00
Pieter-Jan Briers
c7e9c4e857 Update submodule. 2019-07-20 16:02:19 +02:00
Pieter-Jan Briers
409a7d9e01 TG inventory icons. 2019-07-20 16:02:18 +02:00
Víctor Aguilera Puerto
5abcb680ab Ignore some server-only components. (#276) 2019-07-20 14:26:50 +02:00
Pieter-Jan Briers
c675886713 Separate inventory & character UI. 2019-07-20 14:07:11 +02:00
Pieter-Jan Briers
f5d319bc3a Adds explicit inventory window. 2019-07-20 14:07:11 +02:00
Víctor Aguilera Puerto
5f276cd39d Simple quick equip (#275)
* Quick equip

* Minor improvements.
2019-07-19 16:31:56 +02:00
Pieter-Jan Briers
b34a68a519 Remove instances of Visible = false for windows. 2019-07-19 16:12:02 +02:00
Pieter-Jan Briers
ff3c1e6e82 Update submodule. 2019-07-19 16:08:25 +02:00
Pieter-Jan Briers
18efec6472 Game HUD improvements:
Added sandbox button, needs implementation.
Tutorial bound to F1.
Improved color of buttons.
2019-07-19 14:21:36 +02:00
Pieter-Jan Briers
1ba460e1a6 Update submodule, entity spawn rewrite. 2019-07-19 12:40:28 +02:00
Pieter-Jan Briers
8cb3b9b2e6 Make respawn command work without argument. 2019-07-19 12:40:03 +02:00
Pieter-Jan Briers
c0717ed6a3 Do not show character interface for observers. 2019-07-19 11:15:33 +02:00
Pieter-Jan Briers
14d538997e Remove some redundant declarations from ChatBox.cs 2019-07-19 11:01:32 +02:00
Pieter-Jan Briers
42a41036ad Great I managed to forget about the local button too. 2019-07-19 11:00:18 +02:00
Pieter-Jan Briers
1a92d08399 Improves examine code
Examining now has larger range. Ghosts have no range limit.
Fixed some messy code and some bad netcode.
2019-07-19 10:45:55 +02:00
metalgearsloth
1f320eccd7 Add basic teleportation and portals (#269)
* Add basic teleportation and portals

* Address PJB's feedback and minor cleanup
2019-07-19 10:09:33 +02:00
Pieter-Jan Briers
52a6d9ff43 Better margin on the chat box. 2019-07-19 10:06:36 +02:00
Pieter-Jan Briers
8621d61278 Chat improvements:
Improved chat box styling.
"All" button now works as a toggle for.. all buttons!
Also improved persistence of the chat box when switching to/from lobby.
2019-07-19 01:23:16 +02:00
Pieter-Jan Briers
96516fa288 Make scrollbars less opaque. 2019-07-19 01:21:48 +02:00
Pieter-Jan Briers
90173f6147 Change lobby chat placeholder. 2019-07-19 01:21:22 +02:00
Pieter-Jan Briers
22c5b6eb31 Better join chat messages. 2019-07-19 01:21:09 +02:00
Pieter-Jan Briers
a3b0127328 Add placeholder for game chat. 2019-07-19 01:20:43 +02:00
Pieter-Jan Briers
b281cc28a1 Fix crash with ConstructorComponent. 2019-07-19 01:18:45 +02:00
Pieter-Jan Briers
40eca95661 Fix build. 2019-07-18 23:37:09 +02:00
Pieter-Jan Briers
6de6c6534c De-capitalize "Esc". 2019-07-18 23:34:41 +02:00
Pieter-Jan Briers
068c997519 Switch to Noto Sans Display in CSS. 2019-07-18 23:34:40 +02:00
Pieter-Jan Briers
c529f38517 Tutorial has a window title. 2019-07-18 23:34:40 +02:00
Pieter-Jan Briers
76c5c911d9 Noto Sans Display. 2019-07-18 23:34:40 +02:00
Víctor Aguilera Puerto
d9077bde74 Adds IThrown, ILand interfaces. Adds dice. (#273)
* Dice, IThrown, ILand

* Adds sounds to the dice using a sound collection.

* Seed random instance better.

* Missed a ")", should compile now
2019-07-18 23:33:02 +02:00
tentekal
92668432a7 Chat Filter using Flag Enums. (#270)
* Backing up local repo before upgrading OS, minor work on chat UI

* Cleaned out unnecessary modded files

* Got a working version of filter toggles based on flag enums

* Added localization to chatbox buttons

* Should actually fix modified proj files, thanks PJB

* Fixed enum operators to unset instead of toggle

* Added a local client class for storing net message details

* Reworked RepopulateChat to pull from a StoredChatMessage list

* Fixed messages dissapearing

* Re-ordered logic to be a bit more efficient with re-drawing chat
2019-07-18 23:32:48 +02:00
Pieter-Jan Briers
ce1eab9181 Update submodule, removal of window AddToScreen. 2019-07-18 22:49:49 +02:00
Pieter-Jan Briers
a9a99c1821 Glasses and gloves icon I'm no gonna immediately use but did make. 2019-07-18 22:08:13 +02:00
Pieter-Jan Briers
73511d9d8f Student cap icon for the tutorial. 2019-07-17 23:36:07 +02:00
Pieter-Jan Briers
4b9c4022b8 Adds character menu, crafting menu and tutorial to the top left. 2019-07-17 21:37:58 +02:00
Pieter-Jan Briers
69f9da944d Fix mouse input being "broken".
I accidentally made a screen-covering control so mouse input got eaten.
2019-07-17 17:08:58 +02:00
Pieter-Jan Briers
391d4a6c67 Better top menu styling. 2019-07-17 16:38:08 +02:00
Pieter-Jan Briers
73e54d201d Update submodule. 2019-07-17 14:12:59 +02:00
Pieter-Jan Briers
fb46949ea3 Update submodule 2019-07-17 00:36:13 +02:00
Pieter-Jan Briers
806e95b382 Fix connecting to a server with the lobby enabled. 2019-07-15 18:57:17 +02:00
Pieter-Jan Briers
cdcafbada6 Start working on those top left buttons in the UI draft. 2019-07-14 23:02:45 +02:00
Pieter-Jan Briers
837a906538 Make Status Effects UI better positioned. 2019-07-12 19:29:53 +02:00
Pieter-Jan Briers
4540034e26 Update submodule. 2019-07-12 19:29:37 +02:00
Pieter-Jan Briers
546770ee70 Don't try to do Discord Rich Presence in integration tests. 2019-07-08 23:17:37 +02:00
Pieter-Jan Briers
b7d5297e9f Show health status on the HUD, shoddily. 2019-07-08 21:11:42 +02:00
Pieter-Jan Briers
08d0663307 Update submodule. 2019-07-08 21:11:24 +02:00
Pieter-Jan Briers
715ddbaea2 Update submodule 2019-07-08 13:46:34 +02:00
Pieter-Jan Briers
b9215d214e Fix ContainerSlot not serializing correctly when empty. 2019-07-08 13:35:05 +02:00
Pieter-Jan Briers
0a6e5c6d01 Oops 2019-07-07 22:25:47 +02:00
Pieter-Jan Briers
558f6ab8a5 Removal of TrySpawn* methods, update submodule. 2019-07-07 22:24:44 +02:00
Pieter-Jan Briers
8ff4c22f42 Polish explosions somewhat.
Just makes them less ugly. Slightly.
2019-07-07 00:39:00 +02:00
Pieter-Jan Briers
215885a436 Disable combat mode. 2019-07-06 23:07:13 +02:00
Pieter-Jan Briers
90701573ac Increase item click box size. 2019-07-06 22:39:53 +02:00
Pieter-Jan Briers
4221fecff6 Update content repo for CLYDE 2.0. 2019-07-06 19:20:20 +02:00
Pieter-Jan Briers
c4523a956d Stupidly shoddy combat mode system.
Doesn't even work for guns, oh well.
2019-06-30 00:01:41 +02:00
Pieter-Jan Briers
338f456c50 AppVeyor attempt 5 2019-06-29 16:56:17 +02:00
Pieter-Jan Briers
b9e4410c93 AppVeyor attempt 4 2019-06-29 03:06:39 +02:00
Pieter-Jan Briers
2c7c0b905f AppVeyor attempt 3 2019-06-29 03:02:42 +02:00
Pieter-Jan Briers
7cf2239eff AppVeyor attempt 2 2019-06-29 02:59:47 +02:00
Pieter-Jan Briers
6d3613672b AppVeyor commit 1 2019-06-29 02:51:10 +02:00
Pieter-Jan Briers
f97977323a Improve integration testing 2019-06-29 01:58:16 +02:00
Pieter-Jan Briers
e8498d1bb2 Update submodule. 2019-06-29 01:58:06 +02:00
Injazz
ff4190b4e1 makes shaders round (#263) 2019-06-23 13:44:39 +02:00
Pieter-Jan Briers
f81ca7ceba Submodule update 2019-06-23 11:29:25 +02:00
Pieter-Jan Briers
955f92298d Submodule update 2019-06-23 11:08:36 +02:00
Injazz
10801af2f7 Explosions and Grenades, Triggers, OnDestroy, OnExAct, Fueltanks and destructible tables (#247)
* initial explosiveComponent

* remove garbagee

* assets

* tile mass deletion baby

* grenades

* tweaks

* Update Content.Server/GameObjects/Components/Explosion/ExplosiveComponent.cs

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

* Ex_act based on damage, fixes and tweaks

* One finishing touch

Done the most cringe way

* ex_act explosions, tables are destructible now

also adds fuel tanks

* adds ex_act to mobs
2019-06-07 13:15:20 +02:00
Pieter-Jan Briers
f1aeaaa640 Update submodule 2019-06-05 15:46:42 +02:00
Pieter-Jan Briers
f551bd32d6 Update submodule, client integration test. 2019-06-04 19:08:15 +02:00
Pieter-Jan Briers
4631e3c79a Update submodule 2019-06-03 21:30:49 +02:00
Injazz
c156af34c4 Adds security and clown outfits, and SoundEmitters (#253)
* ports clown and sec outfits

* bikehorn and soundemitters

* very smol bike horn

* working fine sounds

* Oh wow i can do it through github

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

* fixes for review

* fixes prototype

* fixing entrypoint
2019-06-03 20:16:47 +02:00
Pieter-Jan Briers
1cfdfa12a0 Run tests in travis. 2019-06-02 20:44:51 +02:00
Pieter-Jan Briers
9f58d8cc74 Update submodule 2019-06-02 20:15:54 +02:00
Pieter-Jan Briers
4a4fcad7ef Airlocks use unshaded overlays for the lights. 2019-06-02 13:40:56 +02:00
Víctor Aguilera Puerto
50bc1bff48 Fix bug where lathe databases didn't get serialized correctly. (#252) 2019-06-02 01:18:54 +02:00
Pieter-Jan Briers
dd13d969b2 Benchmarks project along with a component manager benchmark. (#251) 2019-06-02 01:17:07 +02:00
Injazz
400778eb73 improves hitscan weapons (#248)
- fixes inhand sprite visibility
- adds visible charge level
- refactor sound recourse load, now you can specify fire sound from YAML
- adds laser cannon - more powerful laser
- adds new assets for cannon
2019-06-02 01:16:55 +02:00
Pieter-Jan Briers
da35a0f3c9 Fix wall placement being off-grid. 2019-06-01 01:23:00 +02:00
Pieter-Jan Briers
1426e516b5 Update submodule 2019-06-01 00:16:53 +02:00
moneyl
e07c3c368f APC gui colored external power state (#244)
* Adds colored external power state to APC gui

* Move power state colors to StyleSheet
2019-05-29 18:07:05 +02:00
Pieter-Jan Briers
56bccdbc3e Update submodule, update NuGet dependencies. 2019-05-29 14:04:57 +02:00
moneyl
740c62330e Adds charge percentage label to APC (#243) 2019-05-28 20:50:12 +02:00
Pieter-Jan Briers
de3d306487 Update submodule. 2019-05-28 20:49:04 +02:00
Pieter-Jan Briers
32f4f99441 Update submodule. 2019-05-28 13:35:50 +02:00
Pieter-Jan Briers
8bb4f526f5 Make it possible to VV DamageableComponent's damage. 2019-05-28 13:34:11 +02:00
Pieter-Jan Briers
93a34186cc Make the camera kick when hit by a bullet. 2019-05-28 13:33:42 +02:00
Pieter-Jan Briers
e9de8ff9dd Fix projectiles not hitting mobs. 2019-05-28 13:33:23 +02:00
Pieter-Jan Briers
9c3587b00e Fix compiler warnings and remove dead code. 2019-05-28 00:30:34 +02:00
Pieter-Jan Briers
996b45a04f Project File Refactor (#241)
* Project file refactor, content edition

* Update submodule
2019-05-28 00:18:29 +02:00
moneyl
241aa353e9 Apc variable charge bar color (#240)
* Make Apc chargebar color dependent on charge amount

* Adjust orange color and increase brightness
2019-05-28 00:15:13 +02:00
Pieter-Jan Briers
2afb515a8a Update submodule 2019-05-25 14:25:10 +02:00
Pieter-Jan Briers
9ae2cf6db4 Add window title and breaker label to ApcBoundUserInterface (#238)
Add window title and breaker label to ApcBoundUserInterface
2019-05-25 01:50:19 +02:00
moneyl
ce9c8785d6 Add window title and breaker label to ApcBoundUserInterface 2019-05-24 18:24:07 -04:00
Pieter-Jan Briers
02e13d0182 Merge pull request #237 from Moneyl/fix-release-package-build-script
Fix the release package build script
2019-05-25 00:10:54 +02:00
moneyl
fe483897a6 Fixes the release package build script
Removes `copy_godot_scenes` which copies the files from Resources/Scenes into the release package folder. This function breaks builds with the latest commit #220 as it removed the remaining scene (tscn) files from the content repo and deleted the Scenes folder.
2019-05-24 17:45:41 -04:00
Pieter-Jan Briers
54449038ef Merge pull request #220 from Moneyl/no-tscn
Remove GUI dependence on tscn files
2019-05-24 23:23:58 +02:00
moneyl
b9f7c65c05 Use SetAnchorPreset instead of manually setting each anchor 2019-05-24 16:25:44 -04:00
moneyl
338a8e463e Move GUI size init into constructor
Setting the initial size in `Initialize` has no effect on the windows size so it's set in the constructor instead.
2019-05-24 16:25:44 -04:00
Pieter-Jan Briers
9c9a9db9a0 Update submodule 2019-05-24 22:00:16 +02:00
Pieter-Jan Briers
51c323963c Merge pull request #236 from Moneyl/add-missing-bbox-aabb
Adds collision to several power entities
2019-05-24 21:57:54 +02:00
Pieter-Jan Briers
7e703760c8 Merge pull request #233 from Acruid/19-05-16_ExamineImprovements
Examine Improvements
2019-05-24 21:56:40 +02:00
Pieter-Jan Briers
48b6a8c725 Merge pull request #232 from Acruid/19-05-22_PlatformErrorSpam
Fix Missing Platform Property
2019-05-24 21:51:32 +02:00
Pieter-Jan Briers
96daace170 Merge pull request #231 from Acruid/19-05-15_VerbVis
Verb Visibility
2019-05-24 21:51:06 +02:00
moneyl
196ec030f5 Add aabb values and Collidable component to power objects 2019-05-23 15:46:43 -04:00
moneyl
377bd1890e Update submodule. 2019-05-23 15:43:16 -04:00
Acruid
e40f9b20e3 Added a range check for examining entities. Now you can't examine things across the map.
Made the examine window a little transparent so that you can see things behind it. This prevents the examine popup from occluding gameplay.
Moved the ExamineEntity bind from Human to Common context so that it will always be available to clients. Ghosts can now examine things.
2019-05-22 12:59:30 -07:00
Acruid
ce163bdd80 Removes error spam about missing Platform.
Content.IntegrationTests now defaults to x64 platform.
2019-05-22 12:19:42 -07:00
Acruid
d52e5ccbfb Verbs can now set themselves as invisible. 2019-05-22 08:53:50 -07:00
moneyl
f489bee686 Merge branch 'master' into no-tscn 2019-05-19 02:14:24 -04:00
Pieter-Jan Briers
8c6f6f3fb0 Merge pull request #227 from Moneyl/construction-null-step-crash-fix
Fixes server crash due to null construction step
2019-05-18 23:24:55 +02:00
Pieter-Jan Briers
877dcc996c Merge pull request #226 from Moneyl/empty-hand-crash-fix
Empty hand crash fix
2019-05-18 23:23:56 +02:00
moneyl
7e30ffe007 Fixes server crash due to null construction step
ConstructionComponent.AttackBy tries calling TryProcessStep on the forward step of the construction stage, and if that fails it tries the backwards step. I many construction prototypes the backwards step is null for all stages and so clicking the construction ghost with an invalid step results in a crash due to the step passed to TryProcessStep being null.
2019-05-18 17:10:19 -04:00
moneyl
916b1521a0 Fixes server crash when clicking empty hand with full hand 2019-05-18 13:26:58 -04:00
moneyl
bc3942127b Fixed construction menu default size being huge 2019-05-17 10:36:29 -04:00
moneyl
4bb02d64fb Remove storage menu dependence on tscn file 2019-05-17 10:35:41 -04:00
moneyl
e40b775f7c Added properties for several ui controls to ApcWindow
By PJBs suggestion. Cleaner to do this instead of repeatedly calling GetChild. Also changed ApcBoundUserInterface._window from an SS14Window to an ApcWindow to avoid extra type casting.
2019-05-16 13:24:55 -04:00
moneyl
ddcdeca4ea Removed construction menu dependence on tscn file
One minor issue remains with this that I can't resolve. The construction window starts off much larger than the tscn dependent version on master. It can be shrunk manually and it'll look the same, but it'd be nice to keep it as it was. Tried manually changing the window size and changing the size flags but couldn't get it to work as I wanted.
2019-05-16 13:24:55 -04:00
Pieter-Jan Briers
d81254e389 Interaction rework. (#225)
Interaction rework.
2019-05-16 16:47:45 +02:00
Pieter-Jan Briers
b64643ecd6 Adds tutorial, remap some buttons to be more in line with SS13. 2019-05-16 16:25:06 +02:00
Pieter-Jan Briers
c197278c6f Interaction rework.
IActivate is now more used. IAttackHand falls back to IActivate.
2019-05-16 15:51:32 +02:00
Pieter-Jan Briers
1a5c4ad83c Give badmins access to round commands. 2019-05-16 14:41:26 +02:00
Pieter-Jan Briers
818ee83440 Remove IDisplayManager dependency from SS14Window, content changes. 2019-05-16 14:28:34 +02:00
Pieter-Jan Briers
91de6f80b1 Fix player list being blank when re-joining lobby. 2019-05-16 14:18:33 +02:00
Pieter-Jan Briers
792b219b04 Prevent server crashing without gamepreset override. 2019-05-16 14:09:11 +02:00
moneyl
2b0ca2dd8b Moved APC controls creation into the ApcWindow constructor 2019-05-15 12:31:54 -04:00
Pieter-Jan Briers
a903ffb105 Allow admins to change round preset. 2019-05-15 15:49:02 +02:00
moneyl
bec187d997 Slight chatbox changes to fit existing code style and naming 2019-05-14 23:52:00 -04:00
moneyl
2917a87ce7 Removed APC gui dependence on tscn file 2019-05-14 23:49:53 -04:00
moneyl
3fa2e0c976 Removed chatbox dependence on tscn file
This turned out to be much simpler than how I was initially making it. The important thing to note is that without setting the margin and anchor values for the chatbox itself (not it's children) it won't show up on the screen.
2019-05-14 17:43:06 -04:00
moneyl
51eae9d30c Merge pull request #3 from space-wizards/master
Pull more changes from upstream
2019-05-14 16:52:00 -04:00
Pieter-Jan Briers
e68a5c8402 Move Escape Menu to Content. 2019-05-14 15:19:41 +02:00
Pieter-Jan Briers
304b4d8542 Update submodule 2019-05-14 12:56:09 +02:00
Pieter-Jan Briers
cbca48ebbb Add online player list to lobby UI. 2019-05-14 12:54:47 +02:00
Pieter-Jan Briers
a78a07d0c4 UI styling improvements.
Especially for the lobby.

CSS really paying off here.
2019-05-14 09:57:51 +02:00
Pieter-Jan Briers
ad3b3c9f4f Add explicit placeholders to the lobby. 2019-05-14 08:38:49 +02:00
Pieter-Jan Briers
96fbde3413 Adds a UI element to act as placeholder. 2019-05-14 01:19:25 +02:00
Pieter-Jan Briers
88c29abe5e Transition lobby from tscn to C#, localization support for lobby. 2019-05-14 00:23:58 +02:00
Pieter-Jan Briers
331cfaa1c5 Update submodule. 2019-05-14 00:23:02 +02:00
Pieter-Jan Briers
9a1f37d476 Update submodule, switch to .NET Framework 4.7.2 2019-05-11 16:10:09 +02:00
Pieter-Jan Briers
57bcb2a16d Make content UI scaling aware. 2019-05-11 16:05:41 +02:00
Pieter-Jan Briers
fa8fbc49f5 Update submodule. 2019-05-11 16:05:28 +02:00
Pieter-Jan Briers
c0edf2aeed Clean up NanoStyle:
1. Run code formatting.
2. Condense some rules down.
2019-05-08 23:19:17 +02:00
moneyl
e0e5f6355c Merge pull request #2 from space-wizards/master
pull from head
2019-05-08 16:18:02 -04:00
Pieter-Jan Briers
eacfa34c99 Fix parallax generation.
It broke when IoC got made thread local,
since it is done in the thread pool.
2019-05-08 22:01:14 +02:00
moneyl
296288e99d Merge pull request #1 from space-wizards/master
Match fork with origin
2019-05-08 10:54:50 -04:00
Pieter-Jan Briers
0ccefebc52 Entering text into lobby chat box does not release focus. 2019-05-08 16:49:59 +02:00
Pieter-Jan Briers
29304a7714 Handle existing players on restartround correctly. 2019-05-08 16:42:36 +02:00
Pieter-Jan Briers
d15998ed16 Make sure SpeciesUI defaults to having a correct icon. 2019-05-08 15:32:30 +02:00
Pieter-Jan Briers
90620db5f8 Adds shoddy death match system.
It barely even works but oh well.
2019-05-08 09:55:36 +02:00
Pieter-Jan Briers
a014b40972 Update submodule 2019-05-08 09:55:00 +02:00
Pieter-Jan Briers
41b5c4dba2 Hide spam from storage component. 2019-05-06 19:31:04 +02:00
Pieter-Jan Briers
1fa63179d1 Fix character UI being broken.
Also transition everything away from tscn files.
2019-05-05 23:14:40 +02:00
Pieter-Jan Briers
e35d5390db Storage system refactor & map init.
* Demonstrated map init working with guns, toolboxes, tool lockers.
* Refactored EntityStorage and ServerStorage to have a common interface.
* EntityStorage no longer uses ServerStorage PURELY for visuals.
  Use an appearance visualizer instead.
2019-05-05 18:52:06 +02:00
Pieter-Jan Briers
030f1f2a57 Commit new map file. 2019-05-05 15:45:16 +02:00
Pieter-Jan Briers
61eb340945 Update submodule 2019-05-05 15:45:05 +02:00
Pieter-Jan Briers
b7d30f0870 Remove manual container ejection checking code.
This fixes map load when something is in a storage component.
2019-05-05 13:09:21 +02:00
Pieter-Jan Briers
23d8b92f94 Implement updates to containers for ContainerSlot. 2019-05-05 13:08:56 +02:00
Pieter-Jan Briers
5a5e201c7c Update submodule. 2019-05-05 13:08:38 +02:00
Pieter-Jan Briers
1d3f61de3c Update submodule, remove Godot references. 2019-05-05 02:03:40 +02:00
Pieter-Jan Briers
c8cdde5d40 Remove Godot support. (#215)
Remove Godot support.
2019-05-05 02:01:19 +02:00
Pieter-Jan Briers
deaae06787 Remove Godot support. 2019-05-05 01:54:28 +02:00
Pieter-Jan Briers
ff0f309f31 Merge branch 'master' of github.com:space-wizards/space-station-14 2019-05-04 23:29:21 +02:00
Pieter-Jan Briers
89b77bbed8 Content-side integration tests. (#214)
Content-side integration tests.
2019-05-04 23:29:11 +02:00
Pieter-Jan Briers
ec1a4c6601 Use Unicode ³ for lathes. 2019-05-04 22:35:49 +02:00
Pieter-Jan Briers
71e2e2c768 Update submodule 2019-05-04 22:19:36 +02:00
Pieter-Jan Briers
dc2d3f94cb Fix constructionmenu layout. 2019-05-04 19:18:24 +02:00
Pieter-Jan Briers
ea38075feb Update submodule 2019-05-04 17:51:49 +02:00
Pieter-Jan Briers
e00d4f7b26 Fix macOS app bundles failing to start. 2019-05-03 14:05:21 +02:00
Pieter-Jan Briers
cb7e5d4214 Content-side integration tests.
Currently only tests that the server starts.
2019-05-03 13:34:49 +02:00
Pieter-Jan Briers
186138e9b5 Merge pull request #213 from Pireax/master
Fix locker colliding with items
2019-05-03 13:32:29 +02:00
Pireax
50780f5cf0 Fix locker colliding with items 2019-04-30 18:23:24 +02:00
Pieter-Jan Briers
f249c19eaf Fix a couple warnings. 2019-04-29 13:12:50 +02:00
Pieter-Jan Briers
77d92675e2 Fix player attachment events. 2019-04-29 12:53:09 +02:00
Pieter-Jan Briers
02e35502a1 Changed some function names on IMapGrid. (#212)
UnitTesting project is now filtered out of the code coverage.
2019-04-29 12:52:47 +02:00
Pieter-Jan Briers
dc3369d198 Update submodule 2019-04-29 12:51:24 +02:00
Acruid
25b50ebb27 Changed some function names on IMapGrid.
UnitTesting project is now filtered out of the code coverage.
2019-04-28 22:08:27 -07:00
Pieter-Jan Briers
6e5680b3c2 Engineering lockers. (#210)
* Import Eris engineering locker sprites.

* Allow customizing ClientStorageComponent open/close states better.

* EntityStorage does not blow up if Storage is also defined in prototype.

* Engineering styled lockers.
2019-04-26 23:44:26 +02:00
Víctor Aguilera Puerto
fe0414eda7 Lathes (#207)
* Recipe stuff.

* Lathe GUI and stuff

* god dammit

* Lathe menu works, yay.

* EventArgs henk

* Some work

* SS14 -> Robust

* More SS14 -> Robust

* Lathe materials

* Lathe works, Lathe GUI, Queue GUI, etc

too many changes to name them here

* Remove materials button, add ViewVariables and update lathe on connect

* Add Autolathe RSI

* Adds new recipes, fixes a few bugs.

* Remove unused ScrollContainers

* Use same delegate for spawn.

* Removes client-side LatheComponent in favor of BoundUserInterface

* Remove GetMaterial and TryGetMaterial

* Use auto-properties in a few places.

* Adds LatheDatabase, and a bunch of other changes

* Remove useless log.

* Remove lathetype from prototypes.

* Turns Storage, Lathe and Database into autoproperties

* Remove Hacked property from LatheRecipePrototype

* Remove unneeded dependency injection from components

* Refactors LatheDatabaseComponent to use ComponentState

* Refactors MaterialStorageComponent to use ComponentState

* Oopsie

* Another oopsie

* Last oopsie, I hope

* Fix missing Close call.
2019-04-26 15:51:05 +02:00
Pieter-Jan Briers
092539ae59 Update submodule 2019-04-26 15:50:07 +02:00
DamianX
b16768fd0b Moved dropping items over PlaceableSurfaces from AfterAttack to AttackBy (#209)
Added ClickLocation variable to AttackByEventArgs
2019-04-25 23:22:51 +02:00
Pieter-Jan Briers
8655dcaaf6 Fix typo in welder description. 2019-04-22 16:55:27 +02:00
Pieter-Jan Briers
66621de270 Fix being unable to focus chat with hotkey. 2019-04-22 16:52:08 +02:00
Pieter-Jan Briers
a75ec704e6 Fix being unable to fire guns. 2019-04-22 16:51:58 +02:00
Pieter-Jan Briers
5f00394f4f Fix macOS launcher script 2019-04-22 00:33:32 +02:00
Acruid
d3daa83b82 Map System Code Refactor (#204)
* Removes static `IoCManager` service locator calls from `Robust.Shared.Map` namespace.
* Misc code cleanup and filling out doc comments for Map classes.
* Added Union and Intersect methods to Box2.
* Any touched component was converted from static IoC calls to field injection.

Sibling PR to https://github.com/space-wizards/RobustToolbox/pull/796.
2019-04-21 01:20:18 +02:00
Acruid
50f42d71a2 Interaction System Messages (#202)
* Adds entity messages to the InteractionSystem.

* Changed Handled check formatting to match the rest of the codebase.
2019-04-21 01:18:16 +02:00
Pieter-Jan Briers
88d099a571 Update submodule 2019-04-21 01:17:38 +02:00
Pieter-Jan Briers
3e1f7d6d92 Fix compiler warnings. 2019-04-19 23:44:28 +02:00
Pieter-Jan Briers
4c08c31149 Update submodule. 2019-04-19 23:44:18 +02:00
Víctor Aguilera Puerto
29caf4cf53 Fix the thing (#203) 2019-04-19 12:30:31 +02:00
Pieter-Jan Briers
0598578ed9 Fix Jenkins. 2019-04-17 23:34:57 +02:00
Pieter-Jan Briers
747cb15888 OOC <-> Discord link. (#201) 2019-04-17 23:31:43 +02:00
PrPleGoo
903961771b Actual lockers (#195)
Adds storing entities into lockers the way we all know and love.
Relies on an implementation of ITileDefinition in https://github.com/space-wizards/space-station-14/pull/193 (just like origin/master)
#191
2019-04-17 23:26:00 +02:00
Pieter-Jan Briers
1fe24eeb12 Fix some errors due to engine rename. 2019-04-16 13:22:18 +02:00
Silver
8cd7e6e141 update client bat 2019-04-15 21:34:02 -06:00
Silver
95c44d2a35 Fix explicit paths to types in stationstation.yml 2019-04-15 21:33:53 -06:00
Silver
f0fd3186cd Update Scripts 2019-04-15 21:12:00 -06:00
Silver
a18692bc46 Rename toolbox references from ss14 to robust 2019-04-15 21:11:38 -06:00
Silver
b879418fa3 Update Submodule to 25926a1 2019-04-15 20:37:09 -06:00
Pieter-Jan Briers
9b6acf6202 Update submodule 2019-04-13 21:07:47 +02:00
Acruid
b1c81ed6e6 Entity Interpolation (#197)
* Frame interpolation kinda works.

* Added null CurState check because it can be null now.

* Merge remote-tracking branch 'upstream/master' into dev-GameState
2019-04-13 09:46:27 +02:00
Pieter-Jan Briers
52af7d27da Re-implement chat in content. (#198)
* OOC is a word.

* Re-implement chat in content.
2019-04-13 09:45:09 +02:00
Pieter-Jan Briers
51caae7ebe Update submodule 2019-04-13 09:42:12 +02:00
Acruid
9d7345892f Fixes Breaking Change with UI windows. (#192)
* Custom UI window constructors were changed.

* Update Submodule.
2019-04-12 21:37:09 -07:00
Pieter-Jan Briers
6f032f678a Merge pull request #193 from PrPleGoo/ThingDoer
Locker shoving
2019-04-12 22:44:11 +02:00
PrPleGoo
d86020ceae Merge branch 'master' into ThingDoer 2019-04-11 18:00:56 +02:00
PrPleGoo
2c7a66d8ec Merge remote-tracking branch 'upstream/master' 2019-04-11 18:00:12 +02:00
PrPleGoo
4eef8b9bd8 Changed human mass to 85. 2019-04-11 17:59:14 +02:00
PrPleGoo
5251b30591 Shoving lockers, content side 2019-04-11 17:04:48 +02:00
Pieter-Jan Briers
c283634efb Make examine use tooltips. (#189)
FANCY.
2019-04-09 17:33:53 +02:00
Pieter-Jan Briers
e4676395c0 Update submodule. 2019-04-09 14:41:56 +02:00
Pieter-Jan Briers
8c7e7e5510 Update submodule, implement style for Tree. 2019-04-08 18:58:16 +02:00
PrPleGoo
69c1eff34e Update RobustToolbox 2019-04-08 18:35:38 +02:00
PrPleGoo
c3192e187c Revert "Update RobustToolbox"
This reverts commit abd2c7642e.
2019-04-08 18:35:27 +02:00
PrPleGoo
abd2c7642e Update RobustToolbox 2019-04-08 18:30:55 +02:00
Pieter-Jan Briers
49845d736b Update submodule 2019-04-08 12:22:14 +02:00
PrPleGoo
35f3cbe3f9 Engineer's helmet (#188)
* refacting some sprite things

* fix sprites

* Netcode for sending a new icon state to the ClientComponent

* Fixed broken torches.

* Fix dirty calls.

* ClothingComponentState now also includes EquippedPrefix

* Inherritance ClothingComponent : ItemComponent

* Added parameter to ItemComponentState constructor.

* Update RobustToolbox

* Revert "Update RobustToolbox"

This reverts commit 82c7e98ff3853b64698d5e80a45cd7a3758618e0.

Undo weird commit to toolbox?
2019-04-08 12:18:27 +02:00
Pieter-Jan Briers
50433c7ab6 As you can clearly see, I am good at programming. 2019-04-07 12:36:15 +02:00
Pieter-Jan Briers
9f44870eb9 Move keybinds INTO content. 2019-04-06 18:07:43 +02:00
PrPleGoo
77753debeb Leather gloves and an LED tube light. (#187)
Added BurnTemperature to bulbs.
Added HeatResistance to clothing and species.
Added HeatResistanceComponent which resolves armor vs skin.
Made the hand burn on lamps only happen when heat resistance is too poor.
2019-04-06 17:11:51 +02:00
Víctor Aguilera Puerto
9f1dd9f876 ItemList style. (#183) 2019-04-06 17:07:43 +02:00
Pieter-Jan Briers
73ab6716a5 Update submodule 2019-04-06 17:04:52 +02:00
Víctor Aguilera Puerto
0deaa483dd Ignore server-side components. (#185) 2019-04-06 13:41:09 +02:00
Pieter-Jan Briers
f6af1a4ad5 Merge pull request #184 from PrPleGoo/master
Make Attackby (etc) parameters use event args.
2019-04-06 13:40:46 +02:00
PrPleGoo
43cef42fba Fixed module 2019-04-05 19:54:24 +02:00
PrPleGoo
66344c3ac7 Activate with an EventArg object for a parameter 2019-04-05 19:44:32 +02:00
PrPleGoo
c90d54664b UseEntity with an EventArg object for a parameter 2019-04-05 19:42:49 +02:00
PrPleGoo
7d85141c9b AfterAttack with an EventArg object for a parameter 2019-04-05 19:40:46 +02:00
PrPleGoo
9b2be2ba50 RangedAttackBy with an EventArg object for a parameter 2019-04-05 19:32:18 +02:00
PrPleGoo
ddde077d16 AttackByEventArgs inherits from EventArgs again 2019-04-05 19:29:16 +02:00
PrPleGoo
495315565a AttackHand with an EventArg object for a parameter 2019-04-05 19:27:39 +02:00
PrPleGoo
ee7a29326d AttackBy with an EventArg object for a parameter 2019-04-05 19:22:38 +02:00
PrPleGoo
6f298cab62 Update .gitmodules 2019-04-05 18:12:01 +02:00
Pieter-Jan Briers
f5bb790edb Footstep sounds. (#182) 2019-04-05 02:04:34 +02:00
Pieter-Jan Briers
85241a7dce Update submodule. Switch to EncodingHelpers.UTF8 2019-04-05 02:04:09 +02:00
Pieter-Jan Briers
f08d4bf7eb Update submodule. 2019-04-05 00:19:42 +02:00
Pieter-Jan Briers
8ff58821ac Update submodule. 2019-04-04 19:44:45 +02:00
Pieter-Jan Briers
ea581e67c8 Correctly implement movement blocking and undo that appearance mess. 2019-04-04 19:43:01 +02:00
Pieter-Jan Briers
0fe1407214 Move movement to client. 2019-04-04 16:18:43 +02:00
Pieter-Jan Briers
b8e7ae925f Update submodule 2019-04-04 15:14:43 +02:00
Pieter-Jan Briers
bdb6bf35bf Update submodule 2019-04-04 15:09:45 +02:00
Pieter-Jan Briers
9f3f09871e Wires are now actually hidden by floor tiles. (#181) 2019-04-04 15:09:06 +02:00
Injazz
3b0ec7f695 Adds header to readme.md (#179)
* Adds header

* updates header link

Github hosted image now

* use commit instead of master
2019-04-04 15:06:39 +02:00
Pieter-Jan Briers
fe01a7ccfc Update submodule 2019-04-04 00:39:10 +02:00
Pieter-Jan Briers
bd5f920151 Content changes for tile ID refactor. 2019-04-03 21:20:26 +02:00
Pieter-Jan Briers
b52b8d495e Update submodule. 2019-04-01 23:44:59 +02:00
ScumbagDog
1af1ee2ad4 Made a fancier lasergun (#174)
Laserguns now have an internal capacitor that can be recharged by using it with a power cell

Makes the final fix for #138
2019-04-01 20:06:43 +02:00
Pieter-Jan Briers
b94e015314 Style UI tooltips. 2019-04-01 20:05:45 +02:00
Pieter-Jan Briers
04942442aa Update submodule. 2019-04-01 20:05:39 +02:00
Pieter-Jan Briers
eef0db6001 Change main menu button font. 2019-04-01 15:53:50 +02:00
Pieter-Jan Briers
0e1b3607a4 Update submodule. 2019-04-01 15:53:44 +02:00
Pieter-Jan Briers
89f175c73b Maybe fix macOS app bundles. 2019-04-01 13:40:10 +02:00
Pieter-Jan Briers
25f680267c Update submodule 2019-03-31 16:24:22 +02:00
Pieter-Jan Briers
c06c9e92a5 Update readme to show build status again 2019-03-31 15:21:44 +02:00
Pieter-Jan Briers
d4fb4e8822 Update submodule 2019-03-31 14:54:58 +02:00
Silver
b23f3a119e Update submodule
Fixed pathhelper localpath issue where special characters fragmented path.
2019-03-30 18:22:57 -06:00
Pieter-Jan Briers
1da3e89c7b Update submodule 2019-03-31 00:51:55 +01:00
clusterfack
bc98d3cef8 Fix examine going to all clients (#175)
It was using the incorrect overload
2019-03-30 23:46:54 +01:00
Pieter-Jan Briers
2bfb03cd29 Update submodule, unfuck scripts, clean up solution file. 2019-03-30 23:20:50 +01:00
clusterfack
2dbbd72a22 Updates .bat startup files (#177)
Makes it easier for newbs to start up server/client using the batch files
2019-03-30 22:53:24 +01:00
clusterfack
5391221391 Silver fucked up 2019-03-30 16:17:35 -05:00
Silver
1ad7b03023 Update submodule, Fix localpath 2019-03-30 13:51:25 -06:00
Silver
66cec2a4bf Update submodule 2019-03-30 13:49:43 -06:00
clusterfack
a73c76cd02 Fix being able to store larger or the same size storage containers within another (#172) 2019-03-30 13:56:29 +01:00
clusterfack
e381920e86 Fix Species UI Overlay Removal (#173)
Soooo nameof turns a variable into a string of that variable. I'm not sure where I copied that from but ID is the proper value.
2019-03-30 12:40:15 +01:00
Silver
5eefc7fd45 Update Submodule again 2019-03-30 01:16:18 -06:00
Silver
8792c06d87 Updated submodule RobustToolbox 2019-03-30 01:05:01 -06:00
Silver
0869b05ffd Update Projects and References 2019-03-30 00:31:45 -06:00
Silver
717cc575d1 Update Travis and Jenkins 2019-03-29 22:12:10 -06:00
Silver
ffd346af4b Update gitmodules 2019-03-29 22:11:40 -06:00
Silver
13274d63b0 Update README.md 2019-03-29 15:48:29 -06:00
Silver
72c856722b Update README.md 2019-03-29 15:47:46 -06:00
Silver
b69db1ccbf Update .gitmodules 2019-03-29 15:47:00 -06:00
Víctor Aguilera Puerto
d2f01004a2 Updates submodule to use new naming. (#171) 2019-03-29 13:55:35 -06:00
Ivan
41357d44c1 Update README.md 2019-03-29 13:36:45 -06:00
Ivan
56a7b9a305 Update README.md 2019-03-29 13:35:55 -06:00
Pieter-Jan Briers
12e4cdc43b Update submodule 2019-03-28 20:45:35 +01:00
Víctor Aguilera Puerto
d090e98bd4 [Ready] SoundComponent (#164)
Requires https://github.com/space-wizards/space-station-14/pull/768

- [x] Play sounds
- [x] SoundSchedules actually work
- [x] Send sound to specific users
- [x] Make existing components use SoundComponent
- [x] Add ScheduledSounds from prototypes
- [x] Add Play methods equivalent to those of AudioSystem.
- [x] Document most code.
2019-03-28 14:31:49 +01:00
Pieter-Jan Briers
8926669f3a Better feedback when failing to insert into storage. 2019-03-28 11:48:18 +01:00
Pieter-Jan Briers
3fb42ca4ee Make ClothingComponent Storeable.
This fixes being unable to store clothing into lockers.
2019-03-28 11:47:45 +01:00
Pieter-Jan Briers
880d4b7249 Improved icon smoothing to use ECS properly, cables smooth now. 2019-03-28 11:14:03 +01:00
Pieter-Jan Briers
00c59290fc Update submodule 2019-03-28 11:13:37 +01:00
Pieter-Jan Briers
b152832bf6 Fix directions of walls on the map. 2019-03-27 22:50:40 +01:00
Pieter-Jan Briers
cd85e486e3 Update submodule 2019-03-27 18:32:11 +01:00
Pieter-Jan Briers
575b12dcdc Use SharedSpeciesComponent on the client. 2019-03-27 17:12:43 +01:00
Remie Richards
c0caaaa8e5 Fix pickup verb. (#165) 2019-03-27 13:29:43 +01:00
Injazz
c5e077efc1 mob dead state tweaks (#162)
~~well, it SHOULD make mob rotate on death, but it doesn't, i have no idea why~~

- Blocks character movement in crit/death
- rotates character ~~360noscope~~ 90 degrees to convince you it's lying down when dead

resolves #145
resolves #158
related to #115
2019-03-27 13:29:06 +01:00
Pieter-Jan Briers
842cb44cd2 New station station and new floors. 2019-03-26 22:21:20 +01:00
Pieter-Jan Briers
de26699cfe Pick up verb. 2019-03-26 13:46:07 +01:00
Pieter-Jan Briers
38d11d0684 Prevent verbs from same component appearing multiple times. 2019-03-26 13:45:16 +01:00
Pieter-Jan Briers
adf1c4d79e Adds underplating tile. 2019-03-26 13:44:54 +01:00
Pieter-Jan Briers
5b95aacb45 Update submodule. 2019-03-26 11:45:42 +01:00
Pieter-Jan Briers
7be6a61b75 Update submodule 2019-03-26 11:37:58 +01:00
Pieter-Jan Briers
0577c90f81 Fix bad table layers. 2019-03-26 11:37:12 +01:00
Pieter-Jan Briers
995bb2f067 Catwalks from Eris. 2019-03-26 11:37:01 +01:00
Pieter-Jan Briers
feccff9036 Aaand I forgot to commit this.
I blame Rider for not updating correctly.
2019-03-26 09:36:28 +01:00
Pieter-Jan Briers
3b83ba9f65 ContainerSlot deletes contained entity on deletion. 2019-03-26 09:35:51 +01:00
Pieter-Jan Briers
068e8949c2 Fancy tables from Eris. 2019-03-26 09:35:27 +01:00
Pieter-Jan Briers
93eaff3d15 Add plating tile type 2019-03-25 22:49:03 +01:00
Pieter-Jan Briers
e3a39a21a2 Prevent epilepsy seizures from lights running out of power. 2019-03-25 22:47:54 +01:00
Pieter-Jan Briers
a41bd181e1 Update submodule 2019-03-25 22:44:50 +01:00
Injazz
4a5168f14c Destructible component rework (#157)
* Walls are destructible now
* Added girders that spawn after wall’s destruction
* Girders drop metal sheet on destruction
2019-03-25 22:27:03 +01:00
Víctor Aguilera Puerto
9a224398d3 Fixes some bugs relating to having manually edited the map. (#161)
Resolves #159
2019-03-25 14:20:21 +01:00
Pieter-Jan Briers
e39ca8d8d2 Update submodule 2019-03-25 13:01:37 +01:00
Pieter-Jan Briers
b9e18b43e7 Prevent thunk sound spam. 2019-03-25 13:01:03 +01:00
Pieter-Jan Briers
6d0c2ed362 Magazines now correctly get saved to the map. 2019-03-24 23:37:28 +01:00
Víctor Aguilera Puerto
d9ff72c907 Adds small lights and small light bulbs. (#156)
In the future, someone might want to edit the sprites so the bulb is white and not yellow.
Here's some screenshots:
![image](https://user-images.githubusercontent.com/6766154/54878560-d9242e80-4e2e-11e9-8fde-39ae082f34b0.png)
![image](https://user-images.githubusercontent.com/6766154/54878572-f3f6a300-4e2e-11e9-95a1-253a5504922f.png)
2019-03-24 18:05:32 +01:00
Víctor Aguilera Puerto
03856b79b4 Fix the sound bug when clicking on lights. (#155)
It was updating lights when there was nothing to be updated, which caused the sound to play.
2019-03-23 22:42:41 +01:00
Pieter-Jan Briers
dcd74e8305 Update submodule 2019-03-23 22:34:04 +01:00
Víctor Aguilera Puerto
4f2ae14b3f Light bulbs can now be colored! (#154)
-Also fixes a bug where bulb type didn't matter when inserting a new bulb into a light fixture.
-And a bug where bulb state changing didn't change the light itself.

P.S. Also note I didn't add any colored lightbulb prototypes, as I don't think they're necessary right now. To see the changes, please use VV! In the future, it might be possible to change the lightbulb's color with a multitool or something in-game.
2019-03-23 22:30:05 +01:00
Pieter-Jan Briers
5c234c1de4 Update submodule 2019-03-23 15:04:45 +01:00
Pieter-Jan Briers
0882435293 Fancy guns. (#152) 2019-03-23 15:04:14 +01:00
Pieter-Jan Briers
f0aec83be4 Update submodule 2019-03-23 00:00:17 +01:00
Víctor Aguilera Puerto
b3aa1f6dcd Wall lights now require light bulbs. (#151)
* Adds light_tube.rsi

Sprites taken from CEV Eris!

* Adds LightBulbComponent

* Wall lights now use light bulbs!

* Light bulb now updates its sprite when it changes.

* Comments the code.

* Adds license and copyright to new sprites
2019-03-22 23:59:13 +01:00
Pieter-Jan Briers
58e8aef5d8 Fix removing items from inventory dropping them too. 2019-03-22 23:53:43 +01:00
Pieter-Jan Briers
5b0f49bab7 Reset relative of entities to 0 on pickup.
This mostly helps with akwardness related to sound positioning.
2019-03-22 22:18:17 +01:00
Pieter-Jan Briers
c27be2bf95 Fix mousedown on handsgui. 2019-03-22 21:46:51 +01:00
Víctor Aguilera Puerto
62eb7db0c7 Adds Ointment and Brutepack. (#150)
* Add Ointment and Brutepack to StackType

* Add Ointment and Brutepack sprites

* HealingComponent now works correctly with StackComponent

* Adds Ointment and Brutepack prototypes

* Adds Ointment and Brutepack to stationstation
2019-03-22 15:03:29 +01:00
Pieter-Jan Briers
1cfed8b6c7 Fix package release build path. 2019-03-22 02:13:19 +01:00
Pieter-Jan Briers
1a33bb2d69 Revert "Remove Jenkinsfile."
This reverts commit e2576e1770.
2019-03-22 01:54:50 +01:00
Pieter-Jan Briers
e2576e1770 Remove Jenkinsfile.
I decided to put this in the job on Jenkins instead.
Also this is kinda an excuse to do a push to see if the webhook works.
2019-03-22 01:29:20 +01:00
Pieter-Jan Briers
40ce38770a BYOND is a word. 2019-03-21 23:33:49 +01:00
Pieter-Jan Briers
1d0ed83967 package_release_build.py for Clyde. 2019-03-21 21:22:58 +01:00
Víctor Aguilera Puerto
6649d06fd8 HealingComponent can now heal different types of damage (#146)
```
  - type: Healing
    heal: 100
    damage: Heat
```
2019-03-21 19:34:03 +01:00
Pieter-Jan Briers
4a40b5112c Update submodule & add nuget.config for package directory. 2019-03-21 18:49:45 +01:00
Pieter-Jan Briers
250a9646da Update submodule. 2019-03-21 18:03:12 +01:00
Pieter-Jan Briers
4ba8848383 Add notify debug command. 2019-03-21 18:03:05 +01:00
Pieter-Jan Briers
45b803ce41 Simplify Healing Component deletion.
I just fixed the bug causing it to need explicit dropping so this hack
can go.
2019-03-21 17:51:25 +01:00
Pieter-Jan Briers
e1ca54fc2e Fix crash when deleting held entity. 2019-03-21 17:49:21 +01:00
Injazz
3b40b4aafa Healing component and Medkit (#143)
Medkit to heal yourself and your buddy
known issues:
- [ ] it doesn't restore screen effects that happens when health status go into crit and dead
2019-03-21 16:55:16 +01:00
Pieter-Jan Briers
96868625cb Update submodule. 2019-03-20 23:25:58 +01:00
Pieter-Jan Briers
5a9cc75ab9 Fix typo in lights icon path. 2019-03-20 23:25:22 +01:00
Pieter-Jan Briers
fad429c922 Fancier light sprites. 2019-03-20 15:07:10 +01:00
Pieter-Jan Briers
8b694b1899 Bullets no longer open doors. 2019-03-20 15:02:19 +01:00
Pieter-Jan Briers
75614db5ce Make bullet hitbox smaller. 2019-03-20 14:35:53 +01:00
Pieter-Jan Briers
34f4c12e00 APCs now go click when breaker is switched. 2019-03-20 14:11:03 +01:00
Pieter-Jan Briers
a7e63329b4 I forgot to commit light clunk. 2019-03-20 11:37:22 +01:00
Pieter-Jan Briers
c17bc612ff Lights now go CLUNK when turned on. 2019-03-20 01:12:38 +01:00
Pieter-Jan Briers
d5559e44bb Airlocks get opening sounds now. 2019-03-20 00:42:52 +01:00
Pieter-Jan Briers
f83876e8f8 Update submodule. 2019-03-20 00:42:28 +01:00
Pieter-Jan Briers
5797c6cb66 PCM is an acronym. 2019-03-19 22:44:10 +01:00
Pieter-Jan Briers
0a605781e9 Convert audio effects to mono channel.
We that for some reason, a lot of SS13's audio effects sound *bad* in stereo mode. BYOND seems to convert them to Mono internally while playing them which circumvents this.
This merges the audio tracks into one, saving some space and making them sound correct on Godot.

Also, OpenAL doesn't like playing Stereo audio positionally, and I'm not gonna write a channel merger in C# for this to work around this.
2019-03-19 22:43:50 +01:00
Pieter-Jan Briers
d7ef38701b Actually position gun sounds. 2019-03-19 22:41:19 +01:00
Pieter-Jan Briers
5e7b03d251 Make content use SWSL shaders. 2019-03-18 23:54:58 +01:00
Pieter-Jan Briers
33a915fc67 Update submodule. 2019-03-18 23:54:29 +01:00
Pieter-Jan Briers
83147922f4 Remove gun debug logging. 2019-03-17 16:08:14 +01:00
Pieter-Jan Briers
c182ea944f Add spawn points to stationstation. 2019-03-17 16:01:56 +01:00
Pieter-Jan Briers
e1f6a2bbd5 Spawn point system.
Hey we can place spawn points on the map now instead of hardcoding them!
Spawn points are simply invisible bare bones entities.
2019-03-17 15:52:27 +01:00
Pieter-Jan Briers
8aefe6c615 update submodule. 2019-03-17 15:48:20 +01:00
Pieter-Jan Briers
8e6c9e7c47 Give admins spawn and delete command access. 2019-03-17 15:36:49 +01:00
Pieter-Jan Briers
f0176c2801 Update submodule. 2019-03-17 15:36:36 +01:00
Pieter-Jan Briers
9c4832bfbe Prevent storage buttons from vertically expanding. 2019-03-17 15:35:15 +01:00
Pieter-Jan Briers
6c66817502 Fix EntityButton not inheriting correct type. 2019-03-17 14:37:18 +01:00
Pieter-Jan Briers
694d6e5b33 Fix crash when putting gun into backpack. 2019-03-17 14:37:05 +01:00
Pieter-Jan Briers
f779755c11 Doors are now automated. 2019-03-17 13:24:26 +01:00
Pieter-Jan Briers
53b59bd6f3 Update submodule. 2019-03-17 13:24:11 +01:00
Pieter-Jan Briers
2c0d61111f Update submodule. 2019-03-16 21:25:52 +01:00
Pieter-Jan Briers
0e686285f8 Engineering and external airlocks. 2019-03-16 21:25:42 +01:00
Pieter-Jan Briers
23975c100c Add airlocks on stationstation. 2019-03-16 20:50:41 +01:00
Pieter-Jan Briers
84aa369809 Airlocks rewritten to use appearances, use less ugly sprite from Eris. 2019-03-16 20:40:07 +01:00
Pieter-Jan Briers
1b3d3debc1 Fix client inventory not inheriting the correct controls.
Fixes it on Clyde.
2019-03-16 18:14:23 +01:00
Pieter-Jan Briers
c1c4728961 Fix character window being anchored left wide. 2019-03-16 18:14:09 +01:00
Pieter-Jan Briers
7606c656a0 Update submodule. 2019-03-16 18:13:47 +01:00
Pieter-Jan Briers
da3a7e47e3 Make HandsGui work on Clyde. 2019-03-15 19:07:29 +01:00
Pieter-Jan Briers
43c0b512bd CheckBox styling. 2019-03-11 11:55:29 +01:00
Pieter-Jan Briers
11eadd91af Update submodule. 2019-03-11 11:55:19 +01:00
Pieter-Jan Briers
2234641e4a Update submodule and styling 2019-03-11 09:07:56 +01:00
Pieter-Jan Briers
ccaa995702 Checkbox SVGs and 96 DPI exports 2019-03-11 09:07:36 +01:00
Pieter-Jan Briers
0bcb6adf6a New styling for various new controls. 2019-03-05 09:39:20 +01:00
Pieter-Jan Briers
0030da78a6 Update submodule. 2019-03-05 09:38:48 +01:00
Pieter-Jan Briers
5436d6831e Change theming now that the font system isn't unstable. 2019-02-24 16:14:36 +01:00
Pieter-Jan Briers
7664fcf1f4 Add helpers to load textures and fonts. 2019-02-24 16:14:19 +01:00
Pieter-Jan Briers
dfcfd9b04f Noto is a word, Rider. 2019-02-24 16:14:00 +01:00
Pieter-Jan Briers
9a938185d9 Update submodule. 2019-02-24 15:55:30 +01:00
Pieter-Jan Briers
1ee556091c Make default font Noto Sans 12. 2019-02-24 15:55:23 +01:00
Pieter-Jan Briers
eee4b64c64 Make LineEdit not ugly as hell. 2019-02-23 16:35:51 +01:00
Pieter-Jan Briers
89a4b60f7e I apparently forgot to commit this 2019-02-22 16:59:20 +01:00
Pieter-Jan Briers
20846ef446 Fix various compiler warnings. 2019-02-21 22:01:13 +01:00
Pieter-Jan Briers
fd9db21ad7 Update submodule. 2019-02-21 18:13:37 +01:00
Pieter-Jan Briers
3516392f06 NanoStyle v1 2019-02-18 09:16:06 +01:00
Pieter-Jan Briers
54e0818fef Add more acronyms to Rider's acronym list. 2019-02-11 19:14:53 +01:00
Pieter-Jan Briers
f628fb3fd2 Give Parallax texture a name. 2019-02-11 19:14:37 +01:00
Pieter-Jan Briers
1116a0a647 Update submodule and ImageSharp. 2019-02-11 19:14:29 +01:00
Pieter-Jan Briers
f7be2c16c0 Fix bad scene files in content. (#141) 2019-01-23 15:32:19 +01:00
Pieter-Jan Briers
54cc252bd7 Remove direct references to IOverlay.
I've removed it in the OpenGL branch due to being kinda a bad idea.
Overlay as an abstract is a much better one.
2019-01-23 15:31:18 +01:00
Pieter-Jan Briers
69ee137cea Add .directory to gitignore 2019-01-23 15:29:39 +01:00
Pieter-Jan Briers
415b7e96fd Transform refactor. (#139)
space-wizards/space-station-14#725
2019-01-18 11:40:30 +01:00
Pieter-Jan Briers
e8e1c9dd1f Try to fix travis 2019-01-18 11:39:21 +01:00
Pieter-Jan Briers
474c4910e2 Fix bad file resource reference in inventory scene. 2019-01-17 21:52:26 +01:00
Pieter-Jan Briers
0e51d98c85 Fix PowerCell component not being ignored 2019-01-17 20:53:14 +01:00
Pieter-Jan Briers
7ca90d11b3 Gun stuff (#132)
* Guns can now be fully automatic.

Take that BYOND.

* Improve delay handling

* Bullet spread

* Fix firing guns on pickup
2018-12-13 14:49:57 +01:00
Acruid
69946c79d8 Fixes inventories and throwing. (#133) 2018-12-13 14:49:05 +01:00
clusterfack
37df61113e Species Component (#130)
* Fix this
fug
oksure
Creates the initial species component, damage states, and threshold templates and hooks them into the damageable component

* More rebase fixes

* test

* Pre future rebase

* Please

* Lol

* Lol2

* SHADERS

* Update Engine

* yml file

* Fix an initialization issue, injects dependencies

* Fix error in loading shaders

* Makes a master character ui controller component added upon client attachment to entity and remove upon client detachment from entity

* Fixes just about everytrhing

* Address PJB's comments

* geeze

* Make overlays work in worldspace instead of screen space and not cover user interfaces

* update submodule
2018-12-13 14:47:18 +01:00
Pieter-Jan Briers
b8becf4a56 Remove use of CRCs to bypass bullshit. 2018-12-03 16:55:00 +01:00
Pieter-Jan Briers
5b7ff8ddee Update submodule 2018-12-02 11:05:15 +01:00
Pieter-Jan Briers
ef3007a603 Parallax (#131) 2018-11-30 21:54:30 +01:00
Pieter-Jan Briers
0230323563 Implement server status content side. 2018-11-26 10:02:47 +01:00
Pieter-Jan Briers
845d0f9182 We have a lobby! (#127)
It's shoddy as hell but it works for our purposes.
2018-11-25 19:04:49 +01:00
Pieter-Jan Briers
f887d22a16 Make WipeMind() not crash if there is no mind to be wiped. 2018-11-25 12:52:31 +01:00
Pieter-Jan Briers
9780cf9062 Allow changing the owner of a mind. 2018-11-24 19:12:22 +01:00
Pieter-Jan Briers
10120bb0b6 Engine InLobby removal update. 2018-11-22 23:07:27 +01:00
Pieter-Jan Briers
574512f1bf Extremely basic game ticker. (#126)
You can theoretically restart the round (resetting the map) now, but it breaks very badly if a client is connected due to various edge cases/race conditions.
2018-11-22 10:37:58 +01:00
Pieter-Jan Briers
744af3be3e Remove even more join delay 2018-11-21 21:25:21 +01:00
Pieter-Jan Briers
2d07d89a73 Content changes for ServerRunLevel removal 2018-11-21 21:21:29 +01:00
Pieter-Jan Briers
f91488fa27 Popup message notifications. (#125)
* Popup message system v1

* Networking for the popup notifications.

Ship it.
2018-11-21 21:11:30 +01:00
Pieter-Jan Briers
b0f212bad5 Verbs (#124)
Right click verbs work.
2018-11-21 20:58:11 +01:00
Acruid
8038ebe37d Fix Hands Crash (#122)
* Fixed sprite issues with construction system (Thanks PJB!).

* Storage and Hands Systems now subscribe to Transform Parent changes, and will keep their containers in sync.

* Add check in Interaction System to prevent processing client-side entities on the server.
2018-11-11 20:32:05 +01:00
Pieter-Jan Briers
4720182cf4 Flashlights now run off a power cell. (#121)
* Flashlight runs off a power cell now.

* Cleanup
2018-11-11 20:05:09 +01:00
Pieter-Jan Briers
51bc17f76d Fix map serialization of storage component. (#111)
Items were stored since the engine can serialize containers,
but the used storage volume wasn't.
2018-11-11 20:04:52 +01:00
Acruid
d186f18c20 Thrown Item Physics (#120)
* Thrown object speed is now based on force.
Fixed BoundingBox namespace.

* 100% more throwing.
CollisionGroup enum.

* Update Engine.
2018-10-30 09:13:10 +01:00
Acruid
fc7493e149 Namespace Compatibility changes. (#114) 2018-10-25 17:40:48 -07:00
Acruid
3dc3031054 World Storage (#112)
* Added generic crate assets.
Added open flag to StorageComponent.

* StorageComponent door toggling works.

* Door now updates based on if someone is subscribed to the Storage.

* Added License to crate.rsi
Fixed Icon of crate.

* Added basic Locker.

* Added Fire Extinguisher.

* Added ability to close the storage UI by the server.
Notify the server that the UI is closed, so that the player is unsubscribed from the storage.
Unsubscribe the player from a storage if the player entity moves out of range.

* Add check to make sure entity is on the same map as the storage.

* Update Engine module.

* Update Engine.
2018-10-25 17:35:34 -07:00
Acruid
6cc88115b5 Rename ICollisionManager to IPhysicsManager (#113)
* Renamed ICollisionManager to IPhysicsManager.

* Added sanitization and logging for clients sending invalid coordinates to the interaction system.

* Update Engine.
2018-10-25 17:29:33 -07:00
Pieter-Jan Briers
c33daddda2 Power cells 2018-09-21 08:21:40 +02:00
Pieter-Jan Briers
952c58c226 Eris power cell sprites. 2018-09-21 08:20:25 +02:00
Pieter-Jan Briers
49ea084336 Fix map light rotation. 2018-09-20 19:42:06 +02:00
Pieter-Jan Briers
1fe7e90da8 Apparently I forgot to push c20r and laser sprites. 2018-09-20 19:12:31 +02:00
PJB3005
b92a9b6d1a Admin Ghosting 2018-09-20 18:19:04 +02:00
Pieter-Jan Briers
43d5be40f0 Fix incorrect black gloves name. 2018-09-19 20:43:11 +02:00
Pieter-Jan Briers
2b026daf67 Make character inventory slightly nicer. 2018-09-19 19:06:30 +02:00
Pieter-Jan Briers
74541e23a4 Equipment & inhands. (#110)
* Equipment WiP

* Equipment's starting to work.

* Equipment works properly 100% now.

* Inhands work.

Also more clothes.
2018-09-19 18:54:04 +02:00
Pieter-Jan Briers
c612806ef1 Improve APC bounding box. 2018-09-17 21:19:55 +02:00
Pieter-Jan Briers
097882f658 Improve mob hitbox. 2018-09-17 21:10:01 +02:00
Pieter-Jan Briers
24c9cf54af Zoom in 2x.
Things are tiny because we're using SS13's sprites.
Zoom in like SS13.
2018-09-17 20:05:47 +02:00
Pieter-Jan Briers
73712b4e39 Update submodule.
Guns work again.
2018-09-17 12:36:09 +02:00
Pieter-Jan Briers
2dd3f24fb2 Make Y+ up. (#109) 2018-09-17 10:46:38 +02:00
Pieter-Jan Briers
7a91fb7512 Project file maintenance, C# 7.2 (#108)
1. Updates all the NuGet packages (except CommandLineParser because they ruined their API)
2. Makes all projects use C# 7.2 explicitly. (not latest)
3. Use some C# 7.2 features like readonly structs and default literals.
2018-09-13 20:09:53 +02:00
PJB3005
1dabe49234 VV support. 2018-09-09 15:34:43 +02:00
PJB3005
06ea07eca5 Use Godot-accessible Resources folder.
Fixes #62
2018-08-31 10:49:48 +02:00
Pieter-Jan Briers
d414ea55f5 APC GUI. (#107) 2018-08-31 08:52:48 +02:00
PJB3005
bb5a278fdb Fix compiler warning. 2018-08-30 12:53:23 +02:00
Centronias
ef506006b3 Toggleable flashlight (#106)
Fixes second half of #92 

Regarding the first half, Digitalis explained in discord that when an entity is picked up, visible components are made invisible by making the entire Godot scene node for the entity (based on the GodotTransformComponent) not visible. It's probably possible to exempt the pointlight from that invisibility, but that seems hacky and it seems like something that should be covered by a generic "items equipped in hands are visible" design.

Adds:
- Handheld light component for toggling light activation
- Lantern sprite with on and off layers
- Lantern prototype updates

Known issues:
- When light is on and on the ground, hovering over it with the cursor does not produce the outline effect. I'm not sure, but I think this is caused by the way I implemented the illuminated layer as an entire sprite rather than just the illuminated part. The outline only works on the first layer maybe? I checked it against the welder in its on state and it doesn't seem to outline the flame.
- Illuminated sprite (layer 1) is an entire flashlight, so to make it look okay, the whole first layer is turned off. Would be better / more correct to follow the example of the welder and just create an illuminated "cap" to overlay on the dark extinguished layer. I'd whip up some coder art myself, but I don't have the right tools to handle transparency.
- Illuminated sprite is slightly different from the extinguished sprite, so turning on the light makes it a little bit shorter.
2018-08-28 17:39:20 +02:00
Acruid
7b7b5cc59f Math Assembly (#102)
Included new Math assembly in projects.
2018-08-27 10:23:42 +02:00
PJB3005
5ce370f18b Update submodule 2018-08-27 09:59:10 +02:00
Pieter-Jan Briers
73f4442410 Engine Culling (#104)
* Move groups.yml to content.

* Add unpowered light prototype from engine.

* Move prototypes and textures over.

* Update submodule
2018-08-24 13:14:38 +02:00
Acruid
ed39649721 Interaction Features (#101)
Various minor interaction features.

There is no concept of gravity in the game yet, so items drift through space or the hallways of a station until they are stopped. Thrown items do not collide with walls because making them collidable makes them collide with EVERYTHING, including the player. We need collision groups in the physics system.

Because of the previous problems the velocity things are throw at is set quite low, it can be tweaked after the other mentioned issues are resolved.
2018-08-22 10:19:47 +02:00
Pieter-Jan Briers
cd4442b81e Antag Datums. Oh also minds. (#100)
AKA: Minds and there's a stupid basic roles framework.
2018-08-20 15:59:04 -07:00
Pieter-Jan Briers
e8c4dc9a34 Rejoin into the same body. (#96)
Depends on https://github.com/space-wizards/space-station-14/pull/659
2018-08-20 11:47:00 +02:00
Acruid
edb3eb5b2e HandsSystem now uses InputSystem. (#95)
Fixed TemperatureSystem namespace.
2018-08-18 15:28:02 -07:00
Acruid
ce5760d46c Click Migration (#94)
* Migrated Interaction system to the new Input system.

* Contexts are now set up in content.
2018-08-16 15:57:11 -07:00
Pieter-Jan Briers
189a52c6d7 Use Sprite proxies for GUIs. (#93)
AKA "welders actually light up in the hands GUI".
2018-08-16 22:43:04 +02:00
PJB3005
fe1252cca7 Why did I write sbyte there..? 2018-08-12 12:01:44 +02:00
Pieter-Jan Briers
7b3fcb1d54 Fix missing component errors on the client. 2018-08-09 22:50:05 +02:00
Pieter-Jan Briers
fa594fb3e9 Smoothwalling. (#91) 2018-08-09 20:22:54 +02:00
Acruid
830159390b Input Handling (#90)
* Migrates the Examine system to the new Input system.

* Update Engine.
2018-08-08 11:43:49 -07:00
Pieter-Jan Briers
b0a3f294c5 Remove now-redundant Transform component specifiers. 2018-08-02 10:17:30 +02:00
Pieter-Jan Briers
40ed16e118 Fix build harder. 2018-08-02 08:55:49 +02:00
Pieter-Jan Briers
6c5a0786c2 Fix build. 2018-08-02 08:45:23 +02:00
Pieter-Jan Briers
adda9136d0 Update submodule to fix CI. 2018-08-02 08:38:04 +02:00
Pieter-Jan Briers
cb224a973d Update Submodule. 2018-08-02 08:30:25 +02:00
Pieter-Jan Briers
d7074bf74f Construction System. (#87)
* Construction WiP

* Construction kinda works!

* Lots more construction work.

* It mostly works!
2018-08-02 08:29:55 +02:00
Víctor Aguilera Puerto
f051078c79 Remove all references to OpenTK (#85)
Resolves #80
2018-07-29 23:58:19 +02:00
Pieter-Jan Briers
74b38c49fb Materials system. (#84) 2018-07-27 17:11:58 +02:00
Pieter-Jan Briers
cb439ba39a Remove compiler warnings. 2018-07-26 23:55:34 +02:00
Pieter-Jan Briers
b34591ab59 Data Rework Content Edition (#82)
* WiP Data Rework.

* Convert stationstation to new map format.

* stationstation.yml v2

* Update submodule
2018-07-26 23:38:16 +02:00
Acruid
8c874c76dc Base Update() method was removed from components in engine, added some basic ECS systems to update the components. (#79) 2018-07-26 23:26:19 +02:00
Pieter-Jan Briers
18ed1041ba Update submodule 2018-07-26 23:26:03 +02:00
Pieter-Jan Briers
ad5c82fec9 APC & SMES appearances. (#78)
* CEV-Eris SMES sprite as RSI.

Has been modified so that the light overlay states use alpha blending.

* Add tiny glow to SMES display sprite.

* Appearances work v2

* More WiP

* RoundToLevels works correctly on even level counts now.

* SMES -> Smes because MS guidelines.

* CEV-Eris APC sprite.

* APC visuals.

* Reduce SMES scale again to normal levels.

* Update submodule
2018-07-17 11:39:55 +02:00
Pieter-Jan Briers
7629d447aa Update submodule 2018-07-14 13:36:32 +02:00
Pieter-Jan Briers
806addd3ae I am a certified genius. 2018-06-23 22:03:46 +02:00
Pieter-Jan Briers
ad0c547f08 Update Jenkinsfile. 2018-06-23 22:02:40 +02:00
Pieter-Jan Briers
d92d4c2fa2 Merge branch 'master' of github.com:space-wizards/space-station-14-content 2018-06-23 21:58:48 +02:00
Pieter-Jan Briers
f163602942 Linux exports LAD 2018-06-23 21:58:39 +02:00
indeano
2d23c6a45e Some work on directing storage component's client updates (#75)
* adds storage system, primitive client-unsubscribing of storage updates

* strips out currently unneeded system

* channel to actor

* closestorageui todo added

* thorough logging, actor to session, validates session before sending packets

* offloads session status check to an event handler
2018-06-20 15:49:13 -05:00
PJB3005
86e600bec7 Merge remote-tracking branch 'origin/18-06-09-gridentities' 2018-06-13 04:26:04 +02:00
Pieter-Jan Briers
e4c378cb8f Update submodule & clean up code with Timer.Spawn(). 2018-06-11 20:30:52 +02:00
Pieter-Jan Briers
10999dc905 Grid map detaching: content edition. 2018-06-10 01:03:49 +02:00
Pieter-Jan Briers
9b251d70e2 Readme & submodule update. 2018-06-09 15:48:54 +02:00
Pieter-Jan Briers
df538aa4d6 Update submodule. 2018-06-08 15:16:20 +02:00
Pieter-Jan Briers
cd1d731b73 Don't let me write sh code. 2018-06-08 14:56:59 +02:00
Pieter-Jan Briers
c355031a2c Fix broken travis.yml 2018-06-08 12:46:11 +02:00
Pieter-Jan Briers
bd529a0478 Fix texture case sensitivity issues. 2018-06-08 12:37:34 +02:00
Pieter-Jan Briers
7fcd424c71 Proper drawdepths for items & clothing. 2018-06-03 16:29:56 +02:00
Pieter-Jan Briers
439b8d772f Update submodule. 2018-06-03 16:23:38 +02:00
Pieter-Jan Briers
d8540cdbd9 Comments are important. 2018-05-30 17:05:06 +02:00
Pieter-Jan Briers
0dad2c863b Fix powernet spreading edge cases. 2018-05-30 16:44:35 +02:00
Pieter-Jan Briers
179b2534d2 Fix load & supply desync upon merging powernets. 2018-05-30 16:22:51 +02:00
Pieter-Jan Briers
ff0b1bfd05 Fix power warnings & sort errors. 2018-05-30 16:10:36 +02:00
Pieter-Jan Briers
097c876578 More data in power debug tool. 2018-05-30 14:23:08 +02:00
Pieter-Jan Briers
d5ce4c6b7f Sprite drawdepth fixes. 2018-05-30 14:07:49 +02:00
Pieter-Jan Briers
7e5c764b50 Make power node not die on map load. 2018-05-27 23:39:14 +02:00
Pieter-Jan Briers
77222824c3 New map! 2018-05-27 23:27:32 +02:00
Pieter-Jan Briers
e36033a3ac Power improvements:
* crashes fixed
* made lights an RSI.
2018-05-27 21:45:31 +02:00
Pieter-Jan Briers
89d8208abd Update submodule 2018-05-27 19:33:53 +02:00
Pieter-Jan Briers
ab91d358ef Power is now frame time aware and actually works correctly. 2018-05-27 19:33:27 +02:00
Pieter-Jan Briers
147aad5064 Some work on the mess that is this power code.
Jesus.

Tons of fixes, refactors and other things.
The powernet's code is still awful though.
2018-05-27 16:44:50 +02:00
Pieter-Jan Briers
f1ec10e3e1 Input Refactor (#72) 2018-05-27 10:13:33 +02:00
Pieter-Jan Briers
b13d100107 Fixing issues sonar pointed out, content changes. (#71)
* Fixing issues sonar pointed out, content changes.

* Update submodule.
2018-05-26 18:28:37 +02:00
Pieter-Jan Briers
13611e04ff Update submodule 2018-05-13 15:54:51 +02:00
Pieter-Jan Briers
92b7386e57 Third time's the charm. 2018-05-13 14:49:50 +02:00
Pieter-Jan Briers
74367b1b74 Hey maybe don't forget to commit this. 2018-05-13 14:40:30 +02:00
Pieter-Jan Briers
dee2b12955 macOS export attempts.
Pray for it!
2018-05-13 14:37:16 +02:00
Pieter-Jan Briers
f6b4f23362 Copy scene files from content -> engine during release builds. (#70) 2018-05-13 12:34:56 +02:00
Pieter-Jan Briers
85b4be3ff4 Updates for the netcode refactor. (#69)
* Updates for the netcode refactor.

* Change HandsGui to control.

Why? No idea but drawing of it works again.

* Fix hand change sync.

* Update submodule
2018-05-13 11:54:21 +02:00
Pieter-Jan Briers
20cf3a6a9b Rename IEntity.GetComponents to GetAllComponents. (#68)
* Rename IEntity.GetComponents to GetAllComponents.

This is to avoid confusion.
A single s to distinguish it from GetComponent() is quite hard to miss.

* Update submodule.
2018-05-10 19:21:15 +02:00
Pieter-Jan Briers
205a4fc530 Fixes server shutdown crash & client error log spam. (#67) 2018-05-10 18:52:44 +02:00
Pieter-Jan Briers
2d9285f790 Fixes description on prototypes 2018-05-09 17:14:19 +02:00
clusterfack
3915b735ae Examine System (#65)
* Examine System

* Adds some relevant comments
2018-05-09 16:34:26 +02:00
Pieter-Jan Briers
61a1e769d7 Sprite refactor (#63)
* Sprite refactor compatibility.

* Sprite-level rotation.

* Dude it works.

Welder now has an unshaded flame toggle!

Door component no longer on client!

* Remove debug text.

* Update.
2018-05-08 01:20:15 -05:00
indeano
2ba705ffe9 Fixes server crashing when attempting to drop an empty hand's contained entity. (#66)
* fixes server crash on empty hand drop

Fixes issue with server crashing when client requests for an empty hand to drop its entity.

* fixes drop crash by checking for null in container contains

* removes old change - time to learn to reset properly
2018-05-07 04:05:27 -05:00
clusterfack
78fa747973 Adds click parsing entity system (#64)
* Adds click parsing entity system

Instead of every entity system receiving the message and deciding if it individually wants the message, and verifying player information. The click parser system receives the message and parses which system to send it to based on clicktype and sends it.

* Submodule update
2018-05-01 02:52:37 -05:00
clusterfack
c33c227d95 Inventories (#61)
* Bully the fuck out of inventory shit
Inventory Stuff
Inventories technically work
It works technicaly speaking
Yeah this part too
Lets do it!
Inventories completed
Motherfucker

* Remove unnecessary usings and fix one thing

* Submodule update

* Adds a bunch of various clothing prototypes for each current inventory slot
2018-04-25 13:42:35 +02:00
clusterfack
ea05c593aa Storage Component (toolboxes, backpacks, etc) (#57)
* Fix
Finish and update submodule
Comments code, changes network messages
FIXES THE USE INTERACTION SO YOU CAN ACTUALLY ACTIVATE THINGS NOW
Need engine commits
We'll figure out what to do about this shit later eh mates? Maybe have a script in the build process that automatically moves them over to godot/scenes
Changes some prototypes and fixes stuff
Fixes some more bugs, makes storage values show up properly
Part 3
Part 2
Storage Take 1

* Doneso
2018-04-22 13:11:38 +02:00
Pieter-Jan Briers
74193d1182 Compatability with sawmills. (#60) 2018-04-19 20:23:49 +02:00
Pieter-Jan Briers
41bda76980 Minor stuff: (#58)
* Minor stuff:

* Making a file called DISABLE_SUBMODULE_AUTOUPDATE in BuildChecker/ makes it not always run git submodule update.
* Added .mypy_cache to .gitignore.
* Made default platforms x64 (from Any CPU).

* Make git hooks +x.
2018-04-19 20:15:36 +02:00
Pieter-Jan Briers
ed411fa944 Travis CI. (#59)
* Travis CI attempt #1

* It is scientifically impossible to get Travis right the first commit.
2018-04-19 19:51:38 +02:00
PJB3005
d6661e4558 Update submodule due to BFG 2018-04-13 02:50:50 +02:00
Pieter-Jan Briers
9989d6db75 Updates submodule (#56)
* Adds audio to the content repo.

* Content repo updates

* I forgot this AGAIN.
2018-04-12 23:56:43 +02:00
Remie Richards
4a5a2ceb31 Turns out this is important! (#55)
RUN_THIS.py won't actually do anything with this commented out.
2018-04-07 22:39:38 +02:00
Pieter-Jan Briers
6704245a41 The Wait Is Over. (#49)
* Move submodule to Godot

* Update submodule AGAIN.

* Update project files.

* Remove WearableAnimatedSprite from prototypes.

* Remove content repo resource copier.

* Update submodule.

* Fix resource handling.

* Content.Client compiles by commenting out hands GUI.

* Update submodule.

* Fix prototype textures and update submodule.

* Update Submodule.

* Update submodule SOME MORE!

* Random WiP shit I guess

* Make omnisharp not choke on buildchecker.

* Update submodule.

* Highly WiP broken HandsGui code.

* Ok maybe let's not insult omnisharp.

* Fix annoying Omnisharp warning.

* Update submodule.

* Update submodule.

* Hey I forgot to push this but it didn't conflict!

* Fix hands GUI on godot.

* Update submodule.

* Obligatory submodule update.

* Work on exports.

* Work on exports.

* Update submodule.

* Update submodule.

* Fix dumb case mismatch between content and engine

* work pls.

* This maybe.

* Now!

* Update submodule.

* update submodule.

* Some WiP work on exporting aaah.

* OK READY FOR THE BUILDS SERVER.

* Probably should've made those commits in a different order.

* DO THE THING

* update submodule.

* Updates for effects system.

* Update submodule.

* Make file/line numbers show up on Windows Godot.

* Set submodule to master.
2018-04-07 15:31:38 +02:00
clusterfack
128728bfcb Adds weapons (#48)
* Adds weapons

- Adds melee weapons
- Adds projectile weapons
- Adds hitscan weapons (like lasers)

* Adds a separate sprite for projectile weapons
2018-04-06 00:32:51 +02:00
DamianX
63b5d83728 Fixed crash when pressing BACK in lobby (#46)
* Fixed crash when pressing BACK in lobby

* that's true
2018-03-24 16:54:13 +01:00
DamianX
bddd31b1b0 Copied run{client,server}.bat to the content repo (#45) 2018-03-22 12:34:32 -05:00
Acruid
071ed3f1ed Turret Entities (#38)
* Spotlight Turret.

* Used new turret sprite, credit: @Lobstrex (Lob)#5692

* Moved AimShootLifeProcessor.cs to content.

* Update Submodule Try 2.
2018-03-15 20:41:31 +01:00
clusterfack
98bd1552b9 Fixes power components (#39)
Fixes the power components not having node set if they get a node added as a component in OnAdd()
2018-03-09 17:59:19 +01:00
clusterfack
7554d51b0a Full interaction system (#43)
* Full interaction system

Has support for ranged interactions, and afterattacks, no longer relies on the clickable comopnent and you can click on no component and it will still function

* yes
2018-03-09 17:59:03 +01:00
Acruid
3f89f3f0f7 InputSystem Messages (#37)
* Misc Cleanup.

* Removed BoundKeyChangeEventArgs.

* Nothing uses EntityEventArgs now.

* Rebase fixes.

* Updated Engine.
Fixed .gitmodules file.
2018-03-04 03:07:09 +01:00
Acruid
b005d661f8 Component Messaging Rework (#36)
* Remove DiscoBall.

* Changes `IEntity.AddComponent(IComponent)` to `IEntity.AddComponent<T>`.

* Pulled ComponentManager registration out of Component into Entity.

* Killed component message params.

* Updated engine submodule.
2018-02-24 11:48:23 -08:00
clusterfack
05d4b2793b Adds wirecutter, powernet wire interaction (#34)
So it begins, the first of many interactions. Wirecutters cut powernet wires.
2018-02-13 00:07:50 -06:00
clusterfack
cf29e4b7bd Update Submodule (#33) 2018-02-06 22:46:48 -06:00
Acruid
3dfce70fdc Map Blueprint Save/Loading (#32)
* Change entry point to new system.

* Map saving/loading works.

* Cleans up old code.

* Saving and Loading of blueprints works.

* Saving and Loading of blueprints works.

* Updated the nuget 'YamlDotNet' package to 4.3.0.

* Submodules are trash
2018-02-06 21:34:36 -06:00
clusterfack
059832d324 Adds Basic Tools (#27)
* Basic Tools

* Adds tool prototypes
2018-02-06 19:03:36 -06:00
clusterfack
1452502fbf Interaction Entity System (#26)
* Interaction Entity System

* ye

* Update submodule

* Requires engine update to function, but doesn't use shitcode

* Fix conflicts

* Fix conflicts but for real

* Update submodule
2018-02-05 12:57:26 -07:00
clusterfack
1f22f8ab6a Power System (Complete) (#25)
* Power Commit 1

* Commit 2

* Powernet Part2, All components essentially complete

* Commit 4

* Commit 5

* Commit 6

Creates prototypes

* Finishes powernet code alpha

Adds prototypes and logic for powernet updating and regeneration

* Adds onremove functionality to all components

Without these bits of logic nothing makes any sense!

* And this

* Fixes a lot of bugs

* Fix powernet thinking devices are duplicates

* Fix bug and add comments

* woop woop thats the sound of the police
2018-02-03 21:35:42 -07:00
clusterfack
2eb30c9ba9 Update submodule (#28)
* Update submodule

* Fix disco ball
2018-02-02 19:33:50 -06:00
Acruid
988b2b2bc5 EntityUid Type (#23)
* Wraps Entity Uids in their own type.

* Update submodule I think.
2018-01-20 23:46:31 +01:00
clusterfack
5ee56a4cd3 Updates submodule (#22)
* Updates submodule

Fix connect not entering server (with correct toml config) and compile error, updates submodule
Server still has an issue with toml file pointing towards sandbox instead of content and roundstart object initializations

* Updates more stuff

Needs my most recent PR on ss14 to function

* Submodule updated for real this time
2018-01-18 20:00:35 +01:00
BobdaBiscuit
37091d1254 Adds a basic lantern item prototype (#15) 2017-11-16 17:21:33 -07:00
Pieter-Jan Briers
68f4fd995e Update submodule, fix HandsGui.
HandsGui now works with the new system. The port was sloppy though so
somebody might want to come and refactor this so that it doesn't force
its own position and size.
2017-11-13 19:48:21 +01:00
Pieter-Jan Briers
ba152fc61d Make zip files compressed using DEFLATE 2017-11-02 21:34:47 +01:00
Pieter-Jan Briers
074e6d8b62 Fix code to work with engine update 2017-10-29 21:43:42 +01:00
Pieter-Jan Briers
283ed302db Update submodule 2017-10-29 20:24:51 +01:00
Pieter-Jan Briers
4b345c0ea6 Add --arch=64 to MacOS start script. 2017-10-26 17:58:48 +02:00
Pieter-Jan Briers
fd4584519e Fix naming on MacOS artifacts.
They said x86, which is wrong. They're called x64 now.
2017-10-25 20:11:40 +02:00
Pieter-Jan Briers
de19cf652a Enable MacOS builds! 2017-10-25 19:05:55 +02:00
Pieter-Jan Briers
7c840d71e2 Resource pack becomes Jenkins build artifact. 2017-10-25 18:12:53 +02:00
PJB3005
0d0e6b2feb Update submodule. Kinda test Jenkins auto-build. 2017-10-24 17:31:35 +02:00
Pieter-Jan Briers
984617eed8 Update submodule 2017-10-24 00:40:40 +02:00
Pieter-Jan Briers
2d16205091 Jenkins setup. (#13)
* Adding test Jenkinsfile

* Will it be this simple?

* Maybe if I don't typo?

* Nuget restore needed.

* Work around NuGet bug hopefully.

The first person to run NuGet gets to own the /tmp/NuGetScratch folder, so jenkins breaks because I already ran NuGet personally.

* I'm a genius
2017-10-24 00:11:08 +02:00
Serkket
c41b867c7f A QOL Thing (#11) 2017-10-22 23:57:08 +02:00
Pieter-Jan Briers
7f196fc415 Doors! (#12)
* Doors WiP

* Kinda seem to work now?

* Finished

* Oh yeah maybe enable that.

* It works except it doesn't

* Undo formatting changes

* BuildChecker too
2017-10-22 23:48:01 +02:00
Pieter-Jan Briers
ec3e7968a6 Update submodule 2017-10-22 23:47:41 +02:00
Pieter-Jan Briers
82fbec02f0 Update submodule 2017-10-22 20:49:55 +02:00
Pieter-Jan Briers
9bd6200625 Update submodule 2017-10-22 19:37:24 +02:00
Pieter-Jan Briers
dac4f9cb3f Update submodule 2017-10-22 18:01:08 +02:00
Pieter-Jan Briers
8a11d2c649 Update submodule 2017-10-21 23:00:50 +02:00
Pieter-Jan Briers
2c99c8cb86 Fix release builds 2017-10-21 22:33:39 +02:00
Pieter-Jan Briers
b6eb825c5c Fix clothing.yml prototypes. 2017-10-21 19:40:26 +02:00
Pieter-Jan Briers
3601245d6f Make platform control better 2017-10-21 15:32:22 +02:00
Silver
de46359742 Update Rejected Coders
memes
2017-10-20 13:38:48 -06:00
PJB3005
de4583d14b Fix compile from removal of SFML. 2017-10-20 20:55:59 +02:00
PJB3005
60f975f0dc Update submodule 2017-10-20 20:49:05 +02:00
PJB3005
d38cc0ee82 Some item stuff I had laying around. 2017-10-20 20:46:36 +02:00
PJB3005
36141f3b1a Update submodule 2017-10-09 18:49:00 +02:00
Pieter-Jan Briers
6f89d0672d Urist's damage and health thing. (#10)
* Add prototype for temperature testing entity.

* Add Damageable, Destructible, Temperature. Add a prototype based on those.

* Works and cleaned up.

* Nerf
2017-10-07 15:15:29 +02:00
Pieter-Jan Briers
7597cd9172 Interactable component system. (#9)
* InteractableComponent v1. Broken edition

* It works!
2017-10-06 21:05:21 +02:00
Silver
8c1fa84c6e Merge pull request #8 from space-wizards/17_09_24-inventories
Inventories!
2017-09-30 09:32:45 -06:00
PJB3005
7bc132bc0d Merge branch 'master' into 17_09_24-inventories 2017-09-30 16:57:23 +02:00
PJB3005
ec4c864354 update submodule 2017-09-30 16:57:06 +02:00
PJB3005
26e9c37be1 Functional GUI! 2017-09-30 16:56:19 +02:00
PJB3005
4c4687eb8e Merge branch 'master' into 17_09_24-inventories 2017-09-30 12:22:35 +02:00
PJB3005
b8e01208af Update submodule 2017-09-30 12:22:05 +02:00
PJB3005
c7de994183 Item pickup seems to work, crash due to netcode. 2017-09-30 12:17:45 +02:00
PJB3005
b0b220f085 Hand switching works! 2017-09-29 18:38:27 +02:00
PJB3005
5fd0e31920 Merge branch 'master' into 17_09_24-inventories 2017-09-29 18:30:32 +02:00
PJB3005
bf10d34a8d Update submodule 2017-09-29 18:30:20 +02:00
PJB3005
1b44e84ef3 Merge branch 'master' into 17_09_24-inventories 2017-09-26 23:28:20 +02:00
PJB3005
66ec5864d0 Update submodule 2017-09-26 23:24:02 +02:00
PJB3005
f40cffa746 Fix init ordering issue with hands. 2017-09-26 23:10:10 +02:00
PJB3005
4caa13f1c0 Merge branch 'master' into 17_09_24-inventories 2017-09-26 21:59:21 +02:00
PJB3005
6864a457fe Update submodule 2017-09-26 21:59:06 +02:00
PJB3005
d345316db8 Make NetID use 1000+ range 2017-09-26 21:58:45 +02:00
PJB3005
a2399f8842 Merge branch 'master' into 17_09_24-inventories 2017-09-26 21:29:00 +02:00
PJB3005
92947a507c Update submodule 2017-09-26 21:28:32 +02:00
PJB3005
4f2d059de4 Simple network replication for hands, untested. 2017-09-26 21:27:48 +02:00
PJB3005
a464acf354 Ignore item and inventory client side. 2017-09-26 19:59:31 +02:00
PJB3005
c85436118a Basic example prototype that manages not to crash. 2017-09-25 21:00:53 +02:00
PJB3005
fc1c29d14f Merge branch 'master' into 17_09_24-inventories 2017-09-25 20:55:24 +02:00
PJB3005
17ea432604 Update submodule 2017-09-25 20:54:16 +02:00
PJB3005
3e0bcddd4d Done I hope. Needs testing and type checker updates.
waiting on https://github.com/space-wizards/space-station-14/pull/422
2017-09-25 20:52:39 +02:00
PJB3005
73ae408e20 More inventory work. APIs implemented.
Still need to do prototype loading and test cases.
2017-09-24 23:19:47 +02:00
Pieter-Jan Briers
66483bdd72 Can* API functions by Yota's request. 2017-09-24 22:24:54 +02:00
PJB3005
f6e265bccb Inventories WiP 2017-09-24 21:09:26 +02:00
PJB3005
60ff292f14 Fix content assemblies not being loaded.
They were put in the wrong directory.
2017-09-24 16:41:15 +02:00
Pieter-Jan Briers
b2806dcb75 Update submodule 2017-09-23 22:12:28 +02:00
Pieter-Jan Briers
55b9e367b3 x64 build platform. 2017-09-23 00:35:08 +02:00
Pieter-Jan Briers
2d91344a5b Update submodule to 6e535df92627ba4f07685baa4ef6c6abb39e372f 2017-09-23 00:05:54 +02:00
clusterfack
eee05e1451 Adds basic wire object (#6)
* Adds basic wire object

* Tests out the entity parenting system
2017-09-14 23:46:34 -05:00
clusterfack
6d28ce7641 Update submodule (#7) 2017-09-14 23:45:49 -05:00
PJB3005
4e48f2d581 Update submodule again. 2017-08-23 11:09:01 +02:00
PJB3005
6d4e532ebe Update submodule. 2017-08-23 08:01:20 +02:00
PJB3005
73c02f74f2 Disco ball small edition
Example of inheritance in action.
2017-08-22 23:54:48 +02:00
PJB3005
7add727db0 Disco Ball prototype 2017-08-22 23:48:51 +02:00
PJB3005
44cdd5ed55 Update submodule 2017-08-22 23:19:36 +02:00
PJB3005
7fda197391 Remove SFML from shared and server. 2017-08-21 23:51:13 +02:00
PJB3005
8ca10e4f0b Update submodule. 2017-08-21 23:41:05 +02:00
PJB3005
8a43a7022d Remove x64 platform target. 2017-08-19 11:27:51 +02:00
PJB3005
e46e4a7993 Fix SFML references. 2017-08-19 11:24:49 +02:00
PJB3005
3097106fe4 Update submodule 2017-08-19 11:20:51 +02:00
Pieter-Jan Briers
50cb6b4e30 Update submodule 2017-08-17 13:11:48 +02:00
Pieter-Jan Briers
e569eb276e package_release_build mac support. 2017-08-14 15:05:39 +02:00
PJB3005
17cd8c1d61 Remove dead components from content prototype test. 2017-08-13 14:59:00 +02:00
PJB3005
090fb0f417 Give content YamlDotNet, OpenTK and SFML references. 2017-08-13 14:58:31 +02:00
PJB3005
e0941e2f45 Update submodule 2017-08-13 14:35:05 +02:00
PJB3005
b593220805 Update submodule to d8b3864 2017-08-11 10:23:34 +02:00
PJB3005
2b8d2d9c83 Prototype PoC 2017-08-09 13:54:13 +02:00
PJB3005
780fecff3b Clean up release folder too. 2017-08-09 13:43:14 +02:00
PJB3005
23381d936f Fix package build script deleting build output. 2017-08-09 13:18:44 +02:00
PJB3005
a55ab49b6d Add Linux builds to build packager. 2017-08-09 11:13:04 +02:00
7428 changed files with 103536 additions and 523 deletions

23
.appveyor.yml Normal file
View File

@@ -0,0 +1,23 @@
version: 1.0.{build}
image: Visual Studio 2019
platform: x64
configuration: Debug
build:
project: SpaceStation.sln
parallel: false
verbosity: minimal
before_build:
- cmd: py -3 -m pip install --user requests
- cmd: py -3 RUN_THIS.py --no-prompt
build_script:
- ps: dotnet build SpaceStation14.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /p:AppVeyor=yes
test:
assemblies:
only:
- bin/Content.IntegrationTests/Content.IntegrationTests.dll
- bin/Content.Tests/Content.Tests.dll

2
.github/CODEOWNERS vendored
View File

@@ -1 +1 @@
* @PJB3005 * @PJB3005 @Silvertorch5

20
.gitignore vendored
View File

@@ -261,4 +261,22 @@ __pycache__/
*.pyc *.pyc
# Visual Studio Code workspace settings. # Visual Studio Code workspace settings.
.vscode/ .vscode/*
!.vscode/extensions.json
# Release package files go here:
release/
# Apple please go.
.DS_Store
# KDE, come in.
.directory
BuildFiles/Mac/Space Station 14.app/Contents/MacOS/Godot
BuildFiles/Mac/Space Station 14.app/Contents/MacOS/GodotSharpTools.dll
BuildFiles/Mac/Space Station 14.app/Contents/MacOS/mscorlib.dll
BuildFiles/Mac/Space Station 14.app/Contents/MacOS/libmonosgen-2.0.dylib
BuildFiles/Windows/Godot/*
# Working on the tools scripts is getting annoying okay?
.mypy_cache/

7
.gitmodules vendored
View File

@@ -1,3 +1,4 @@
[submodule "engine"] [submodule "RobustToolbox"]
path = engine path = RobustToolbox
url = https://github.com/space-wizards/space-station-14.git url = https://github.com/space-wizards/RobustToolbox.git
branch = master

40
.travis.yml Normal file
View File

@@ -0,0 +1,40 @@
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
#- osx
addons:
apt:
#sources:
#- deadsnakes
packages:
- python3.5
- python3-pip
cache:
directories:
- packages/
- RobustToolbox/Dependencies/
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
before_script:
#- "if [ $TRAVIS_OS_NAME = linux ]; then pyenv shell 3.6; fi"
- "python3.5 -m pip install --user requests"
- "python3.5 RUN_THIS.py --no-prompt"
script:
- "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 +1,5 @@
INSTALLED_HOOKS_VERSION INSTALLED_HOOKS_VERSION
DISABLE_SUBMODULE_AUTOUPDATE
*.nuget*
project.assets.json
project.packagespec.json

View File

@@ -16,12 +16,27 @@ https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
<PropertyGroup> <PropertyGroup>
<Python>python3</Python> <Python>python3</Python>
<Python Condition="'$(OS)'=='Windows_NT' Or '$(OS)'=='Windows'">py -3</Python> <Python Condition="'$(OS)'=='Windows_NT' Or '$(OS)'=='Windows'">py -3</Python>
<ProjectGuid>{C899FCA4-7037-4E49-ABC2-44DE72487110}</ProjectGuid>
<TargetFrameworkMoniker>.NETFramework, Version=v4.7.2</TargetFrameworkMoniker>
</PropertyGroup>
<PropertyGroup>
<OutputType>Library</OutputType>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup> </PropertyGroup>
<Target Name="Build"> <Target Name="Build">
<Exec Command="$(Python) git_helper.py" CustomErrorRegularExpression="^Error"/> <Exec Command="$(Python) git_helper.py" CustomErrorRegularExpression="^Error" />
</Target> </Target>
<Target Name="Rebuild" DependsOnTargets="Build" /> <Target Name="Rebuild" DependsOnTargets="Build" />
<Target Name="Clean"> <Target Name="Clean">
<Message Importance="low" Text="Ignoring 'Clean' target." /> <Message Importance="low" Text="Ignoring 'Clean' target." />
</Target> </Target>
<Target Name="Compile">
</Target>
<Target Name="CoreCompile">
</Target>
</Project> </Project>

View File

@@ -8,10 +8,12 @@ import shutil
from pathlib import Path from pathlib import Path
from typing import List from typing import List
SOLUTION_PATH = Path("..") / "SpaceStation14Content.sln" SOLUTION_PATH = Path("..") / "SpaceStation14.sln"
CURRENT_HOOKS_VERSION = "1" # If this doesn't match the saved version we overwrite them all. # If this doesn't match the saved version we overwrite them all.
CURRENT_HOOKS_VERSION = "2"
QUIET = len(sys.argv) == 2 and sys.argv[1] == "--quiet" QUIET = len(sys.argv) == 2 and sys.argv[1] == "--quiet"
def run_command(command: List[str], capture: bool = False) -> subprocess.CompletedProcess: def run_command(command: List[str], capture: bool = False) -> subprocess.CompletedProcess:
""" """
Runs a command with pretty output. Runs a command with pretty output.
@@ -40,6 +42,9 @@ def update_submodules():
Updates all submodules. Updates all submodules.
""" """
if os.path.isfile("DISABLE_SUBMODULE_AUTOUPDATE"):
return
# If the status doesn't match, force VS to reload the solution. # If the status doesn't match, force VS to reload the solution.
# status = run_command(["git", "submodule", "status"], capture=True) # status = run_command(["git", "submodule", "status"], capture=True)
run_command(["git", "submodule", "update", "--init", "--recursive"]) run_command(["git", "submodule", "update", "--init", "--recursive"])
@@ -50,6 +55,7 @@ def update_submodules():
# print("Git submodules changed. Reloading solution.") # print("Git submodules changed. Reloading solution.")
# reset_solution() # reset_solution()
def install_hooks(): def install_hooks():
""" """
Installs the necessary git hooks into .git/hooks. Installs the necessary git hooks into .git/hooks.
@@ -77,7 +83,8 @@ def install_hooks():
for filename in os.listdir(str(hooks_source_dir)): for filename in os.listdir(str(hooks_source_dir)):
print("Copying hook {}".format(filename)) print("Copying hook {}".format(filename))
shutil.copyfile(str(hooks_source_dir/filename), str(hooks_target_dir/filename)) shutil.copy2(str(hooks_source_dir/filename),
str(hooks_target_dir/filename))
def reset_solution(): def reset_solution():
@@ -91,6 +98,7 @@ def reset_solution():
with SOLUTION_PATH.open("w") as f: with SOLUTION_PATH.open("w") as f:
f.write(content) f.write(content)
if __name__ == '__main__': if __name__ == '__main__':
install_hooks() install_hooks()
update_submodules() update_submodules()

0
BuildChecker/hooks/post-checkout Normal file → Executable file
View File

0
BuildChecker/hooks/post-merge Normal file → Executable file
View File

View File

@@ -0,0 +1,20 @@
<?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>SS14</string>
<key>CFBundleDisplayName</key>
<string>Space Station 14</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

@@ -0,0 +1,8 @@
#!/bin/sh
# cd to file containing script or something?
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
cd "$BASEDIR"
exec ../Resources/Robust.Client "$@"

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

@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using Moq;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
namespace Content.Benchmarks
{
public class ComponentManagerGetAllComponents
{
private readonly List<IEntity> _entities = new List<IEntity>();
private IComponentManager _componentManager;
[Params(500, 1000, 5000)] public int N { get; set; }
[GlobalSetup]
public void Setup()
{
// Initialize component manager.
IoCManager.InitThread();
IoCManager.Register<IComponentManager, ComponentManager>();
var dummyReg = new Mock<IComponentRegistration>();
dummyReg.SetupGet(p => p.Name).Returns("Dummy");
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 [] {typeof(DummyComponent)});
var componentFactory = new Mock<IComponentFactory>();
componentFactory.Setup(p => p.GetComponent<DummyComponent>()).Returns(new DummyComponent());
componentFactory.Setup(p => p.GetRegistration(It.IsAny<DummyComponent>())).Returns(dummyReg.Object);
IoCManager.RegisterInstance<IComponentFactory>(componentFactory.Object);
IoCManager.BuildGraph();
_componentManager = IoCManager.Resolve<IComponentManager>();
// Initialize N entities with one component.
for (var i = 0; i < N; i++)
{
var entity = new Entity();
entity.SetUid(new EntityUid(i + 1));
_entities.Add(entity);
_componentManager.AddComponent<DummyComponent>(entity);
}
}
[Benchmark]
public int Run()
{
var count = 0;
foreach (var _ in _componentManager.GetAllComponents<DummyComponent>())
{
count += 1;
}
return count;
}
private class DummyComponent : Component
{
public override string Name => "Dummy";
}
}
}

View File

@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
<PropertyGroup>
<!-- 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>x64</Platforms>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>8</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Content.Client\Content.Client.csproj" />
<ProjectReference Include="..\Content.Server\Content.Server.csproj" />
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
<ProjectReference Include="..\Content.Tests\Content.Tests.csproj" />
<ProjectReference Include="..\Content.IntegrationTests\Content.IntegrationTests.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Server\Robust.Server.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.UnitTesting\Robust.UnitTesting.csproj" />
</ItemGroup>
</Project>

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

@@ -0,0 +1,12 @@
using BenchmarkDotNet.Running;
namespace Content.Benchmarks
{
internal static class Program
{
public static void Main(string[] args)
{
BenchmarkRunner.Run<ComponentManagerGetAllComponents>();
}
}
}

View File

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

View File

@@ -0,0 +1,223 @@
using System.Collections.Generic;
using Content.Shared.Chat;
using Robust.Client.Graphics.Drawing;
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;
namespace Content.Client.Chat
{
public class ChatBox : MarginContainer
{
public delegate void TextSubmitHandler(ChatBox chatBox, string text);
public delegate void FilterToggledHandler(ChatBox chatBox, BaseButton.ButtonToggledEventArgs e);
private const int MaxLinePixelLength = 500;
private readonly IList<string> _inputHistory = new List<string>();
private readonly ILocalizationManager localize = IoCManager.Resolve<ILocalizationManager>();
public LineEdit Input { get; private set; }
public OutputPanel Contents { get; }
// Buttons for filtering
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.
/// </summary>
private int _inputIndex = -1;
/// <summary>
/// Message that WAS being input before going through history began.
/// </summary>
private string _inputTemp;
/// <summary>
/// Default formatting string for the ClientChatConsole.
/// </summary>
public string DefaultChatFormat { get; set; }
public bool ReleaseFocusOnEnter { get; set; } = true;
public ChatBox()
{
/*MarginLeft = -475.0f;
MarginTop = 10.0f;
MarginRight = -10.0f;
MarginBottom = 235.0f;
AnchorLeft = 1.0f;
AnchorRight = 1.0f;*/
var outerVBox = new VBoxContainer();
var panelContainer = new PanelContainer
{
PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#25252aaa")},
SizeFlagsVertical = SizeFlags.FillExpand
};
var vBox = new VBoxContainer();
panelContainer.AddChild(vBox);
var hBox = new HBoxContainer();
outerVBox.AddChild(panelContainer);
outerVBox.AddChild(hBox);
var contentMargin = new MarginContainer
{
MarginLeftOverride = 4, MarginRightOverride = 4,
SizeFlagsVertical = SizeFlags.FillExpand
};
Contents = new OutputPanel();
contentMargin.AddChild(Contents);
vBox.AddChild(contentMargin);
Input = new LineEdit();
Input.OnKeyBindDown += InputKeyBindDown;
Input.OnTextEntered += Input_OnTextEntered;
vBox.AddChild(Input);
AllButton = new Button
{
Text = localize.GetString("All"),
Name = "ALL",
SizeFlagsHorizontal = SizeFlags.ShrinkEnd | SizeFlags.Expand,
ToggleMode = true,
};
LocalButton = new Button
{
Text = localize.GetString("Local"),
Name = "Local",
ToggleMode = true,
};
OOCButton = new Button
{
Text = localize.GetString("OOC"),
Name = "OOC",
ToggleMode = true,
};
AllButton.OnToggled += OnFilterToggled;
LocalButton.OnToggled += OnFilterToggled;
OOCButton.OnToggled += OnFilterToggled;
hBox.AddChild(AllButton);
hBox.AddChild(LocalButton);
hBox.AddChild(OOCButton);
AddChild(outerVBox);
}
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
{
base.KeyBindDown(args);
if (!args.CanFocus)
{
return;
}
Input.GrabKeyboardFocus();
}
private void InputKeyBindDown(GUIBoundKeyEventArgs args)
{
if (args.Function == EngineKeyFunctions.TextReleaseFocus)
{
Input.ReleaseKeyboardFocus();
args.Handle();
return;
}
else if (args.Function == EngineKeyFunctions.TextHistoryPrev)
{
if (_inputIndex == -1 && _inputHistory.Count != 0)
{
_inputTemp = Input.Text;
_inputIndex++;
}
else if (_inputIndex + 1 < _inputHistory.Count)
{
_inputIndex++;
}
if (_inputIndex != -1)
{
Input.Text = _inputHistory[_inputIndex];
}
Input.CursorPos = Input.Text.Length;
args.Handle();
return;
}
else if (args.Function == EngineKeyFunctions.TextHistoryNext)
{
if (_inputIndex == 0)
{
Input.Text = _inputTemp;
_inputTemp = "";
_inputIndex--;
}
else if (_inputIndex != -1)
{
_inputIndex--;
Input.Text = _inputHistory[_inputIndex];
}
Input.CursorPos = Input.Text.Length;
args.Handle();
}
}
public event TextSubmitHandler TextSubmitted;
public event FilterToggledHandler FilterToggled;
public void AddLine(string message, ChatChannel channel, Color color)
{
if (Disposed)
{
return;
}
var formatted = new FormattedMessage(3);
formatted.PushColor(color);
formatted.AddText(message);
formatted.Pop();
Contents.AddMessage(formatted);
}
private void Input_OnTextEntered(LineEdit.LineEditEventArgs args)
{
if (!string.IsNullOrWhiteSpace(args.Text))
{
TextSubmitted?.Invoke(this, args.Text);
_inputHistory.Insert(0, args.Text);
}
_inputIndex = -1;
Input.Clear();
if (ReleaseFocusOnEnter)
{
Input.ReleaseKeyboardFocus();
}
}
private void OnFilterToggled(BaseButton.ButtonToggledEventArgs args)
{
FilterToggled?.Invoke(this, args);
}
}
}

View File

@@ -0,0 +1,411 @@
using System.Collections.Generic;
using Content.Client.Interfaces.Chat;
using Content.Shared.Chat;
using Robust.Client.Console;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.UserInterface;
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;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Client.Chat
{
internal sealed class ChatManager : IChatManager
{
/// <summary>
/// The max amount of chars allowed to fit in a single speech bubble.
/// </summary>
private const int SingleBubbleCharLimit = 100;
/// <summary>
/// Base queue delay each speech bubble has.
/// </summary>
private const float BubbleDelayBase = 0.2f;
/// <summary>
/// Factor multiplied by speech bubble char length to add to delay.
/// </summary>
private const float BubbleDelayFactor = 0.8f / SingleBubbleCharLimit;
/// <summary>
/// The max amount of speech bubbles over a single entity at once.
/// </summary>
private const int SpeechBubbleCap = 4;
private const char ConCmdSlash = '/';
private const char OOCAlias = '[';
private const char MeAlias = '@';
private readonly List<StoredChatMessage> filteredHistory = new List<StoredChatMessage>();
// Filter Button States
private bool _allState;
private bool _localState;
private bool _oocState;
// Flag Enums for holding filtered channels
private ChatChannel _filteredChannels;
#pragma warning disable 649
[Dependency] private readonly IClientNetManager _netManager;
[Dependency] private readonly IClientConsole _console;
[Dependency] private readonly IEntityManager _entityManager;
[Dependency] private readonly IEyeManager _eyeManager;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
#pragma warning restore 649
private ChatBox _currentChatBox;
private Control _speechBubbleRoot;
/// <summary>
/// Speech bubbles that are currently visible on screen.
/// We track them to push them up when new ones get added.
/// </summary>
private readonly Dictionary<EntityUid, List<SpeechBubble>> _activeSpeechBubbles =
new Dictionary<EntityUid, List<SpeechBubble>>();
/// <summary>
/// Speech bubbles that are to-be-sent because of the "rate limit" they have.
/// </summary>
private readonly Dictionary<EntityUid, SpeechBubbleQueueData> _queuedSpeechBubbles
= new Dictionary<EntityUid, SpeechBubbleQueueData>();
public void Initialize()
{
_netManager.RegisterNetMessage<MsgChatMessage>(MsgChatMessage.NAME, _onChatMessage);
_speechBubbleRoot = new LayoutContainer
{
MouseFilter = Control.MouseFilterMode.Ignore
};
LayoutContainer.SetAnchorPreset(_speechBubbleRoot, LayoutContainer.LayoutPreset.Wide);
_userInterfaceManager.StateRoot.AddChild(_speechBubbleRoot);
_speechBubbleRoot.SetPositionFirst();
}
public void FrameUpdate(FrameEventArgs delta)
{
// Update queued speech bubbles.
if (_queuedSpeechBubbles.Count == 0)
{
return;
}
foreach (var (entityUid, queueData) in _queuedSpeechBubbles.ShallowClone())
{
if (!_entityManager.TryGetEntity(entityUid, out var entity))
{
_queuedSpeechBubbles.Remove(entityUid);
continue;
}
queueData.TimeLeft -= delta.DeltaSeconds;
if (queueData.TimeLeft > 0)
{
continue;
}
if (queueData.MessageQueue.Count == 0)
{
_queuedSpeechBubbles.Remove(entityUid);
continue;
}
var msg = queueData.MessageQueue.Dequeue();
queueData.TimeLeft += BubbleDelayBase + msg.Length * BubbleDelayFactor;
// We keep the queue around while it has 0 items. This allows us to keep the timer.
// When the timer hits 0 and there's no messages left, THEN we can clear it up.
CreateSpeechBubble(entity, msg);
}
}
public void SetChatBox(ChatBox chatBox)
{
if (_currentChatBox != null)
{
_currentChatBox.TextSubmitted -= _onChatBoxTextSubmitted;
_currentChatBox.FilterToggled -= _onFilterButtonToggled;
}
_currentChatBox = chatBox;
if (_currentChatBox != null)
{
_currentChatBox.TextSubmitted += _onChatBoxTextSubmitted;
_currentChatBox.FilterToggled += _onFilterButtonToggled;
}
RepopulateChat(filteredHistory);
_currentChatBox.AllButton.Pressed = !_allState;
_currentChatBox.LocalButton.Pressed = !_localState;
_currentChatBox.OOCButton.Pressed = !_oocState;
}
public void RemoveSpeechBubble(EntityUid entityUid, SpeechBubble bubble)
{
bubble.Dispose();
var list = _activeSpeechBubbles[entityUid];
list.Remove(bubble);
if (list.Count == 0)
{
_activeSpeechBubbles.Remove(entityUid);
}
}
private void WriteChatMessage(StoredChatMessage message)
{
Logger.Debug($"{message.Channel}: {message.Message}");
if (IsFiltered(message.Channel))
{
Logger.Debug($"Message filtered: {message.Channel}: {message.Message}");
return;
}
var color = Color.DarkGray;
var messageText = message.Message;
if (!string.IsNullOrEmpty(message.MessageWrap))
{
messageText = string.Format(message.MessageWrap, messageText);
}
switch (message.Channel)
{
case ChatChannel.Server:
color = Color.Orange;
break;
case ChatChannel.OOC:
color = Color.LightSkyBlue;
break;
}
_currentChatBox?.AddLine(messageText, message.Channel, color);
}
private void _onChatBoxTextSubmitted(ChatBox chatBox, string text)
{
DebugTools.Assert(chatBox == _currentChatBox);
if (string.IsNullOrWhiteSpace(text))
return;
switch (text[0])
{
case ConCmdSlash:
{
// run locally
var conInput = text.Substring(1);
_console.ProcessCommand(conInput);
break;
}
case OOCAlias:
{
var conInput = text.Substring(1);
_console.ProcessCommand($"ooc \"{conInput}\"");
break;
}
case MeAlias:
{
var conInput = text.Substring(1);
_console.ProcessCommand($"me \"{conInput}\"");
break;
}
default:
{
var conInput = _currentChatBox.DefaultChatFormat != null
? string.Format(_currentChatBox.DefaultChatFormat, text)
: text;
_console.ProcessCommand(conInput);
break;
}
}
}
private void _onFilterButtonToggled(ChatBox chatBox, BaseButton.ButtonToggledEventArgs e)
{
switch (e.Button.Name)
{
case "Local":
_localState = !_localState;
if (_localState)
{
_filteredChannels |= ChatChannel.Local;
break;
}
else
{
_filteredChannels &= ~ChatChannel.Local;
break;
}
case "OOC":
_oocState = !_oocState;
if (_oocState)
{
_filteredChannels |= ChatChannel.OOC;
break;
}
else
{
_filteredChannels &= ~ChatChannel.OOC;
break;
}
case "ALL":
chatBox.LocalButton.Pressed ^= true;
chatBox.OOCButton.Pressed ^= true;
_allState = !_allState;
break;
}
RepopulateChat(filteredHistory);
}
private void RepopulateChat(IEnumerable<StoredChatMessage> filteredMessages)
{
_currentChatBox.Contents.Clear();
foreach (var msg in filteredMessages)
{
WriteChatMessage(msg);
}
}
private void _onChatMessage(MsgChatMessage msg)
{
Logger.Debug($"{msg.Channel}: {msg.Message}");
// Log all incoming chat to repopulate when filter is un-toggled
var storedMessage = new StoredChatMessage(msg);
filteredHistory.Add(storedMessage);
WriteChatMessage(storedMessage);
// Local messages that have an entity attached get a speech bubble.
if (msg.Channel == ChatChannel.Local && msg.SenderEntity != default)
{
AddSpeechBubble(msg);
}
}
private void AddSpeechBubble(MsgChatMessage msg)
{
if (!_entityManager.TryGetEntity(msg.SenderEntity, out var entity))
{
Logger.WarningS("chat", "Got local chat message with invalid sender entity: {0}", msg.SenderEntity);
return;
}
// Split message into words separated by spaces.
var words = msg.Message.Split(' ');
var messages = new List<string>();
var currentBuffer = new List<string>();
// Really shoddy way to approximate word length.
// Yes, I am aware of all the crimes here.
// TODO: Improve this to use actual glyph width etc..
var currentWordLength = 0;
foreach (var word in words)
{
// +1 for the space.
currentWordLength += word.Length + 1;
if (currentWordLength > SingleBubbleCharLimit)
{
// Too long for the current speech bubble, flush it.
messages.Add(string.Join(" ", currentBuffer));
currentBuffer.Clear();
currentWordLength = word.Length;
if (currentWordLength > SingleBubbleCharLimit)
{
// Word is STILL too long.
// Truncate it with an ellipse.
messages.Add($"{word.Substring(0, SingleBubbleCharLimit - 3)}...");
currentWordLength = 0;
continue;
}
}
currentBuffer.Add(word);
}
if (currentBuffer.Count != 0)
{
// Don't forget the last bubble.
messages.Add(string.Join(" ", currentBuffer));
}
foreach (var message in messages)
{
EnqueueSpeechBubble(entity, message);
}
}
private void EnqueueSpeechBubble(IEntity entity, string contents)
{
if (!_queuedSpeechBubbles.TryGetValue(entity.Uid, out var queueData))
{
queueData = new SpeechBubbleQueueData();
_queuedSpeechBubbles.Add(entity.Uid, queueData);
}
queueData.MessageQueue.Enqueue(contents);
}
private void CreateSpeechBubble(IEntity entity, string contents)
{
var bubble = new SpeechBubble(contents, entity, _eyeManager, this);
if (_activeSpeechBubbles.TryGetValue(entity.Uid, out var existing))
{
// Push up existing bubbles above the mob's head.
foreach (var existingBubble in existing)
{
existingBubble.VerticalOffset += bubble.ContentHeight;
}
}
else
{
existing = new List<SpeechBubble>();
_activeSpeechBubbles.Add(entity.Uid, existing);
}
existing.Add(bubble);
_speechBubbleRoot.AddChild(bubble);
if (existing.Count > SpeechBubbleCap)
{
// Get the oldest to start fading fast.
var last = existing[0];
last.FadeNow();
}
}
private bool IsFiltered(ChatChannel channel)
{
// _allState works as inverter.
return _allState ^ _filteredChannels.HasFlag(channel);
}
private sealed class SpeechBubbleQueueData
{
/// <summary>
/// Time left until the next speech bubble can appear.
/// </summary>
public float TimeLeft { get; set; }
public Queue<string> MessageQueue { get; } = new Queue<string>();
}
}
}

View File

@@ -0,0 +1,140 @@
using Content.Client.Interfaces.Chat;
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
{
public class SpeechBubble : Control
{
/// <summary>
/// The total time a speech bubble stays on screen.
/// </summary>
private const float TotalTime = 4;
/// <summary>
/// The amount of time at the end of the bubble's life at which it starts fading.
/// </summary>
private const float FadeTime = 0.25f;
/// <summary>
/// The distance in world space to offset the speech bubble from the center of the entity.
/// i.e. greater -> higher above the mob's head.
/// </summary>
private const float EntityVerticalOffset = 0.5f;
private readonly IEyeManager _eyeManager;
private readonly IEntity _senderEntity;
private readonly IChatManager _chatManager;
private float _timeLeft = TotalTime;
public float VerticalOffset { get; set; }
private float _verticalOffsetAchieved;
public float ContentHeight { get; private set; }
public SpeechBubble(string text, IEntity senderEntity, IEyeManager eyeManager, IChatManager chatManager)
{
_chatManager = chatManager;
_senderEntity = senderEntity;
_eyeManager = eyeManager;
MouseFilter = MouseFilterMode.Ignore;
// Use text clipping so new messages don't overlap old ones being pushed up.
RectClipContent = true;
var label = new RichTextLabel
{
MaxWidth = 256,
MouseFilter = MouseFilterMode.Ignore
};
label.SetMessage(text);
var panel = new PanelContainer
{
StyleClasses = { "tooltipBox" },
Children = { label },
MouseFilter = MouseFilterMode.Ignore,
ModulateSelfOverride = Color.White.WithAlpha(0.75f)
};
AddChild(panel);
ForceRunStyleUpdate();
ContentHeight = panel.CombinedMinimumSize.Y;
_verticalOffsetAchieved = -ContentHeight;
}
protected override Vector2 CalculateMinimumSize()
{
return (base.CalculateMinimumSize().X, 0);
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
_timeLeft -= args.DeltaSeconds;
if (_timeLeft <= FadeTime)
{
// Update alpha if we're fading.
Modulate = Color.White.WithAlpha(_timeLeft / FadeTime);
}
if (_senderEntity.Deleted || _timeLeft <= 0)
{
// Timer spawn to prevent concurrent modification exception.
Timer.Spawn(0, Die);
return;
}
// Lerp to our new vertical offset if it's been modified.
if (FloatMath.CloseTo(_verticalOffsetAchieved - VerticalOffset, 0, 0.1))
{
_verticalOffsetAchieved = VerticalOffset;
}
else
{
_verticalOffsetAchieved = FloatMath.Lerp(_verticalOffsetAchieved, VerticalOffset, 10 * args.DeltaSeconds);
}
var worldPos = _senderEntity.Transform.WorldPosition;
worldPos += (0, EntityVerticalOffset);
var lowerCenter = _eyeManager.WorldToScreen(worldPos) / UIScale;
var screenPos = lowerCenter - (Width / 2, ContentHeight + _verticalOffsetAchieved);
LayoutContainer.SetPosition(this, screenPos);
var height = (lowerCenter.Y - screenPos.Y).Clamp(0, ContentHeight);
LayoutContainer.SetSize(this, (Size.X, height));
}
private void Die()
{
if (Disposed)
{
return;
}
_chatManager.RemoveSpeechBubble(_senderEntity.Uid, this);
}
/// <summary>
/// Causes the speech bubble to start fading IMMEDIATELY.
/// </summary>
public void FadeNow()
{
if (_timeLeft > FadeTime)
{
_timeLeft = FadeTime;
}
}
}
}

View File

@@ -0,0 +1,38 @@
using Content.Shared.Chat;
namespace Content.Client.Chat
{
public class StoredChatMessage
{
// TODO Make me reflected with respect to MsgChatMessage
/// <summary>
/// Client's own copies of chat messages used in filtering locally
/// </summary>
/// <summary>
/// Actual Message contents, i.e. words
/// </summary>
public string Message { get; set; }
/// <summary>
/// Message channel, used for filtering
/// </summary>
public ChatChannel Channel { get; set; }
/// <summary>
/// What to "wrap" the message contents with. Example is stuff like 'Joe says: "{0}"'
/// </summary>
public string MessageWrap { get; set; }
/// <summary>
/// Constructor to copy a net message into stored client variety
/// </summary>
public StoredChatMessage(MsgChatMessage netMsg)
{
Message = netMsg.Message;
Channel = netMsg.Channel;
MessageWrap = netMsg.MessageWrap;
}
}
}

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

@@ -0,0 +1,10 @@
using System;
using Content.Shared;
namespace Content.Client
{
public sealed class ClientModuleTestingCallbacks : SharedModuleTestingCallbacks
{
public Action ClientBeforeIoC { get; set; }
}
}

View File

@@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using Content.Client.Interfaces;
using Content.Shared;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Interfaces.GameObjects;
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
{
public class ClientNotifyManager : SharedNotifyManager, IClientNotifyManager
{
#pragma warning disable 649
[Dependency] private IPlayerManager _playerManager;
[Dependency] private IUserInterfaceManager _userInterfaceManager;
[Dependency] private IInputManager _inputManager;
[Dependency] private IEyeManager _eyeManager;
[Dependency] private IClientNetManager _netManager;
#pragma warning restore 649
private readonly List<PopupLabel> _aliveLabels = new List<PopupLabel>();
private bool _initialized;
public void Initialize()
{
DebugTools.Assert(!_initialized);
_netManager.RegisterNetMessage<MsgDoNotify>(nameof(MsgDoNotify), DoNotifyMessage);
_initialized = true;
}
private void DoNotifyMessage(MsgDoNotify message)
{
PopupMessage(_eyeManager.WorldToScreen(message.Coordinates), message.Message);
}
public override void PopupMessage(GridCoordinates coordinates, IEntity viewer, string message)
{
if (viewer != _playerManager.LocalPlayer.ControlledEntity)
{
return;
}
PopupMessage(_eyeManager.WorldToScreen(coordinates), message);
}
public void PopupMessage(ScreenCoordinates coordinates, string message)
{
var label = new PopupLabel {Text = message};
var minimumSize = label.CombinedMinimumSize;
LayoutContainer.SetPosition(label, label.InitialPos = coordinates.Position - minimumSize / 2);
_userInterfaceManager.PopupRoot.AddChild(label);
_aliveLabels.Add(label);
}
public void PopupMessage(string message)
{
PopupMessage(new ScreenCoordinates(_inputManager.MouseScreenPosition), message);
}
public void FrameUpdate(FrameEventArgs eventArgs)
{
_aliveLabels.ForEach(l =>
{
if (l.TimeLeft > 3f)
{
l.Dispose();
}
});
_aliveLabels.RemoveAll(l => l.Disposed);
}
private class PopupLabel : Label
{
public float TimeLeft { get; private set; }
public Vector2 InitialPos { get; set; }
public PopupLabel()
{
ShadowOffsetXOverride = 1;
ShadowOffsetYOverride = 1;
FontColorShadowOverride = Color.Black;
}
protected override void Update(FrameEventArgs eventArgs)
{
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));
}
}
}
}
public class PopupMessageCommand : IConsoleCommand
{
public string Command => "popupmsg";
public string Description => "";
public string Help => "";
public bool Execute(IDebugConsole console, params string[] args)
{
var arg = args[0];
var mgr = IoCManager.Resolve<IClientNotifyManager>();
mgr.PopupMessage(arg);
return false;
}
}
}

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

@@ -0,0 +1,57 @@
using Content.Client.Interfaces;
using Content.Shared.GameObjects.Components.Markers;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
namespace Content.Client.Commands
{
internal sealed class ShowMarkersCommand : IConsoleCommand
{
// ReSharper disable once StringLiteralTypo
public string Command => "togglemarkers";
public string Description => "Toggles visibility of markers such as spawn points.";
public string Help => "";
public bool Execute(IDebugConsole console, params string[] args)
{
bool? whichToSet = null;
foreach (var entity in IoCManager.Resolve<IEntityManager>()
.GetEntities(new TypeEntityQuery(typeof(SharedSpawnPointComponent))))
{
if (!entity.TryGetComponent(out ISpriteComponent sprite))
{
continue;
}
if (!whichToSet.HasValue)
{
whichToSet = !sprite.Visible;
}
sprite.Visible = whichToSet.Value;
}
return false;
}
}
internal sealed class NotifyCommand : IConsoleCommand
{
public string Command => "notify";
public string Description => "Send a notify client side.";
public string Help => "notify <message>";
public bool Execute(IDebugConsole console, params string[] args)
{
var message = args[0];
var notifyManager = IoCManager.Resolve<IClientNotifyManager>();
notifyManager.PopupMessage(message);
return false;
}
}
}

View File

@@ -0,0 +1,393 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Client.GameObjects.Components.Construction;
using Content.Shared.Construction;
using Robust.Client.Graphics;
using Robust.Client.Interfaces.Placement;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Placement;
using Robust.Client.ResourceManagement;
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.Maths;
using Robust.Shared.Prototypes;
namespace Content.Client.Construction
{
public class ConstructionMenu : SS14Window
{
#pragma warning disable CS0649
[Dependency] readonly IPrototypeManager PrototypeManager;
[Dependency] readonly IResourceCache ResourceCache;
#pragma warning restore
public ConstructorComponent Owner { get; set; }
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;
// This list is flattened in such a way that the top most deepest category is first.
private List<CategoryNode> FlattenedCategories;
private readonly PlacementManager Placement;
protected override Vector2? CustomSize => (500, 350);
public ConstructionMenu()
{
IoCManager.InjectDependencies(this);
Placement = (PlacementManager) IoCManager.Resolve<IPlacementManager>();
Placement.PlacementCanceled += OnPlacementCanceled;
Title = "Construction";
var hSplitContainer = new HSplitContainer();
// Left side
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();
var info = new HBoxContainer();
InfoIcon = new TextureRect();
InfoLabel = new Label
{
SizeFlagsHorizontal = SizeFlags.FillExpand, SizeFlagsVertical = SizeFlags.ShrinkCenter
};
info.AddChild(InfoIcon);
info.AddChild(InfoLabel);
guide.AddChild(info);
var stepsLabel = new Label
{
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
SizeFlagsVertical = SizeFlags.ShrinkCenter,
Text = "Steps"
};
guide.AddChild(stepsLabel);
StepList = new ItemList
{
SizeFlagsVertical = SizeFlags.FillExpand, SelectMode = ItemList.ItemListSelectMode.None
};
guide.AddChild(StepList);
var buttonsContainer = new HBoxContainer();
BuildButton = new Button
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
TextAlign = Button.AlignMode.Center,
Text = "Build!",
Disabled = true,
ToggleMode = false
};
EraseButton = new Button
{
TextAlign = Button.AlignMode.Center, Text = "Clear Ghosts", ToggleMode = true
};
buttonsContainer.AddChild(BuildButton);
buttonsContainer.AddChild(EraseButton);
guide.AddChild(buttonsContainer);
hSplitContainer.AddChild(guide);
Contents.AddChild(hSplitContainer);
BuildButton.OnPressed += OnBuildPressed;
EraseButton.OnToggled += OnEraseToggled;
SearchBar.OnTextChanged += OnTextEntered;
RecipeList.OnItemSelected += OnItemSelected;
PopulatePrototypeList();
PopulateTree();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
Placement.PlacementCanceled -= OnPlacementCanceled;
}
}
void OnItemSelected()
{
var prototype = (ConstructionPrototype) RecipeList.Selected.Metadata;
if (prototype == null)
{
InfoLabel.Text = "";
InfoIcon.Texture = null;
StepList.Clear();
BuildButton.Disabled = true;
}
else
{
BuildButton.Disabled = false;
InfoLabel.Text = prototype.Description;
InfoIcon.Texture = prototype.Icon.Frame0();
StepList.Clear();
foreach (var forward in prototype.Stages.Select(a => a.Forward))
{
if (forward == null)
{
continue;
}
Texture icon;
string text;
switch (forward)
{
case ConstructionStepMaterial mat:
switch (mat.Material)
{
case ConstructionStepMaterial.MaterialType.Metal:
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/Materials/sheet_glass.png");
text = $"Glass x{mat.Amount}";
break;
case ConstructionStepMaterial.MaterialType.Cable:
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/Tools/wrench.png");
text = "Wrench";
break;
case ConstructionStepTool.ToolType.Crowbar:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/Tools/crowbar.png");
text = "Crowbar";
break;
case ConstructionStepTool.ToolType.Screwdriver:
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;
text = $"Welding tool ({tool.Amount} fuel)";
break;
case ConstructionStepTool.ToolType.Wirecutters:
icon = ResourceCache.GetResource<TextureResource>(
"/Textures/Objects/Tools/wirecutter.png");
text = "Wirecutters";
break;
default:
throw new NotImplementedException();
}
break;
default:
throw new NotImplementedException();
}
StepList.AddItem(text, icon, false);
}
}
}
void OnTextEntered(LineEdit.LineEditEventArgs args)
{
var str = args.Text;
PopulateTree(string.IsNullOrWhiteSpace(str) ? null : str.ToLowerInvariant());
}
void OnBuildPressed(BaseButton.ButtonEventArgs args)
{
var prototype = (ConstructionPrototype) RecipeList.Selected.Metadata;
if (prototype == null)
{
return;
}
if (prototype.Type != ConstructionType.Structure)
{
// In-hand attackby doesn't exist so this is the best alternative.
var loc = Owner.Owner.GetComponent<ITransformComponent>().GridPosition;
Owner.SpawnGhost(prototype, loc, Direction.North);
return;
}
var hijack = new ConstructionPlacementHijack(prototype, Owner);
var info = new PlacementInformation
{
IsTile = false,
PlacementOption = prototype.PlacementMode,
};
Placement.BeginHijackedPlacing(info, hijack);
}
private void OnEraseToggled(BaseButton.ButtonToggledEventArgs args)
{
var hijack = new ConstructionPlacementHijack(null, Owner);
Placement.ToggleEraserHijacked(hijack);
}
void PopulatePrototypeList()
{
RootCategory = new CategoryNode("", null);
int count = 1;
foreach (var prototype in PrototypeManager.EnumeratePrototypes<ConstructionPrototype>())
{
var currentNode = RootCategory;
foreach (var category in prototype.CategorySegments)
{
if (!currentNode.ChildCategories.TryGetValue(category, out var subNode))
{
count++;
subNode = new CategoryNode(category, currentNode);
currentNode.ChildCategories.Add(category, subNode);
}
currentNode = subNode;
}
currentNode.Prototypes.Add(prototype);
}
// Do a pass to sort the prototype lists and flatten the hierarchy.
void Recurse(CategoryNode node)
{
// I give up we're using recursion to flatten this.
// There probably IS a way to do it.
// I'm too stupid to think of what that way is.
foreach (var child in node.ChildCategories.Values)
{
Recurse(child);
}
node.Prototypes.Sort(ComparePrototype);
FlattenedCategories.Add(node);
node.FlattenedIndex = FlattenedCategories.Count - 1;
}
FlattenedCategories = new List<CategoryNode>(count);
Recurse(RootCategory);
}
void PopulateTree(string searchTerm = null)
{
RecipeList.Clear();
var categoryItems = new Tree.Item[FlattenedCategories.Count];
categoryItems[RootCategory.FlattenedIndex] = RecipeList.CreateItem();
// Yay more recursion.
Tree.Item ItemForNode(CategoryNode node)
{
if (categoryItems[node.FlattenedIndex] != null)
{
return categoryItems[node.FlattenedIndex];
}
var item = RecipeList.CreateItem(ItemForNode(node.Parent));
item.Text = node.Name;
item.Selectable = false;
categoryItems[node.FlattenedIndex] = item;
return item;
}
foreach (var node in FlattenedCategories)
{
foreach (var prototype in node.Prototypes)
{
if (searchTerm != null)
{
var found = false;
// TODO: don't run ToLowerInvariant() constantly.
if (prototype.Name.ToLowerInvariant().IndexOf(searchTerm, StringComparison.Ordinal) != -1)
{
found = true;
}
else
{
foreach (var keyw in prototype.Keywords.Concat(prototype.CategorySegments))
{
// TODO: don't run ToLowerInvariant() constantly.
if (keyw.ToLowerInvariant().IndexOf(searchTerm, StringComparison.Ordinal) != -1)
{
found = true;
break;
}
}
}
if (!found)
{
continue;
}
}
var subItem = RecipeList.CreateItem(ItemForNode(node));
subItem.Text = prototype.Name;
subItem.Metadata = prototype;
}
}
}
private void OnPlacementCanceled(object sender, EventArgs e)
{
EraseButton.Pressed = false;
}
private static int ComparePrototype(ConstructionPrototype x, ConstructionPrototype y)
{
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 List<ConstructionPrototype> Prototypes = new List<ConstructionPrototype>();
public int FlattenedIndex = -1;
public CategoryNode(string name, CategoryNode parent)
{
Name = name;
Parent = parent;
}
}
}
}

View File

@@ -0,0 +1,49 @@
using Content.Client.GameObjects.Components.Construction;
using Content.Shared.Construction;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Placement;
using Robust.Client.Utility;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
namespace Content.Client.Construction
{
public class ConstructionPlacementHijack : PlacementHijack
{
private readonly ConstructionPrototype Prototype;
private readonly ConstructorComponent Owner;
public ConstructionPlacementHijack(ConstructionPrototype prototype, ConstructorComponent owner)
{
Prototype = prototype;
Owner = owner;
}
public override bool HijackPlacementRequest(GridCoordinates coords)
{
if (Prototype != null)
{
var dir = Manager.Direction;
Owner.SpawnGhost(Prototype, coords, dir);
}
return true;
}
public override bool HijackDeletion(IEntity entity)
{
if (entity.TryGetComponent(out ConstructionGhostComponent ghost))
{
Owner.ClearGhost(ghost.GhostID);
}
return true;
}
public override void StartHijack(PlacementManager manager)
{
base.StartHijack(manager);
manager.CurrentBaseSprite = Prototype.Icon.DirFrame0();
}
}
}

View File

@@ -1,87 +1,31 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <TargetFramework>$(TargetFramework)</TargetFramework>
<ProjectGuid>{A2E5F175-78AF-4DDD-8F97-E2D2552372ED}</ProjectGuid> <LangVersion>8</LangVersion>
<OutputType>Library</OutputType> <IsPackable>false</IsPackable>
<AppDesignerFolder>Properties</AppDesignerFolder> <Platforms>x64</Platforms>
<RootNamespace>Content.Client</RootNamespace> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AssemblyName>Content.Client</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ContentAssemblyTarget>..\bin\Client\Assemblies\</ContentAssemblyTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<!--
This copies all dependencies,
but on the plus side it's automatically located in the right place.
-->
<OutputPath>..\bin\Content.Client\</OutputPath> <OutputPath>..\bin\Content.Client\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants> <OutputType Condition="'$(FullRelease)' != 'True'">Exe</OutputType>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<!--
This copies all dependencies,
but on the plus side it's automatically located in the right place.
-->
<OutputPath>..\bin\Content.Client\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<Import Project="..\RobustToolbox\MSBuild\Robust.DefineConstants.targets" />
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <PackageReference Include="Nett" Version="0.13.0" />
<Reference Include="System.Core" /> <PackageReference Include="SixLabors.Core" Version="1.0.0-beta0007" />
<Reference Include="System.Xml.Linq" /> <PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-beta0006" />
<Reference Include="System.Data.DataSetExtensions" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
<Reference Include="Microsoft.CSharp" /> <PackageReference Include="YamlDotNet" Version="8.1.0" />
<Reference Include="System.Data" /> <PackageReference Include="SharpZipLib" Version="1.2.0" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="EntryPoint.cs" /> <ProjectReference Include="..\RobustToolbox\Lidgren.Network\Lidgren.Network.csproj" />
<Compile Include="Properties\AssemblyInfo.cs" /> <ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
</ItemGroup> <ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
<ItemGroup> <ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj"> <ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
<Project>{26aeebb3-dde7-443a-9f43-7bc7f4acf6b5}</Project>
<Name>Content.Shared</Name>
</ProjectReference>
<ProjectReference Include="..\engine\Lidgren.Network\Lidgren.Network.csproj">
<Project>{59250baf-0000-0000-0000-000000000000}</Project>
<Name>Lidgren.Network</Name>
</ProjectReference>
<ProjectReference Include="..\engine\SS14.Client.Graphics\SS14.Client.Graphics.csproj">
<Project>{302b877e-0000-0000-0000-000000000000}</Project>
<Name>SS14.Client.Graphics</Name>
</ProjectReference>
<ProjectReference Include="..\engine\SS14.Client\SS14.Client.csproj">
<Project>{0c31dfdf-0000-0000-0000-000000000000}</Project>
<Name>SS14.Client</Name>
</ProjectReference>
<ProjectReference Include="..\engine\SS14.Shared\SS14.Shared.csproj">
<Project>{0529f740-0000-0000-0000-000000000000}</Project>
<Name>SS14.Shared</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\SS14.Content.targets" />
<Target Name="AfterBuild" DependsOnTargets="CopyContentAssemblies" />
<ItemGroup>
<!-- Files to be copied into Client/Assemblies -->
<ContentAssemblies Include="$(OutputPath)Content.Client.dll" />
<ContentAssemblies Include="$(OutputPath)Content.Shared.dll" />
<ContentAssemblies Include="$(OutputPath)Content.Client.pdb" Condition="'$(Configuration)' == 'Debug'" />
<ContentAssemblies Include="$(OutputPath)Content.Shared.pdb" Condition="'$(Configuration)' == 'Debug'" />
</ItemGroup> </ItemGroup>
<Import Project="..\RobustToolbox\MSBuild\Robust.Engine.targets" />
<Target Name="ContentAfterBuild" DependsOnTargets="ClientAfterBuild" AfterTargets="Build" />
</Project> </Project>

View File

@@ -1,12 +1,246 @@
using SS14.Shared.ContentPack; using System;
using Content.Client.GameObjects.Components.Actor;
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.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 namespace Content.Client
{ {
public class EntryPoint : GameClient public class EntryPoint : GameClient
{ {
#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
public override void Init() public override void Init()
{ {
// TODO: Anything at all. var factory = IoCManager.Resolve<IComponentFactory>();
var prototypes = IoCManager.Resolve<IPrototypeManager>();
factory.DoAutoRegistrations();
var registerIgnore = new[]
{
"Wrenchable",
"AmmoBox",
"Breakable",
"Pickaxe",
"Interactable",
"Destructible",
"Temperature",
"PowerTransfer",
"PowerNode",
"PowerProvider",
"PowerDevice",
"PowerStorage",
"PowerGenerator",
"Explosive",
"OnUseTimerTrigger",
"ToolboxElectricalFill",
"ToolLockerFill",
"EmitSoundOnUse",
"FootstepModifier",
"HeatResistance",
"Teleportable",
"ItemTeleporter",
"Portal",
"EntityStorage",
"PlaceableSurface",
"Wirecutter",
"Screwdriver",
"Multitool",
"Wrench",
"Crowbar",
"HitscanWeapon",
"ProjectileWeapon",
"Projectile",
"MeleeWeapon",
"Storeable",
"Dice",
"Construction",
"Apc",
"Door",
"PoweredLight",
"Smes",
"Powercell",
"LightBulb",
"Healing",
"Catwalk",
"BallisticMagazine",
"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)
{
factory.RegisterIgnore(ignoreName);
}
factory.Register<SharedResearchConsoleComponent>();
factory.Register<SharedLatheComponent>();
factory.Register<SharedSpawnPointComponent>();
factory.Register<SolutionComponent>();
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();
if (TestingCallbacks != null)
{
var cast = (ClientModuleTestingCallbacks) TestingCallbacks;
cast.ClientBeforeIoC?.Invoke();
}
IoCManager.BuildGraph();
IoCManager.Resolve<IParallaxManager>().LoadParallax();
IoCManager.Resolve<IBaseClient>().PlayerJoinedServer += SubscribePlayerAttachmentEvents;
var stylesheet = new NanoStyle();
IoCManager.Resolve<IUserInterfaceManager>().Stylesheet = stylesheet.Stylesheet;
IoCManager.Resolve<IUserInterfaceManager>().Stylesheet = stylesheet.Stylesheet;
IoCManager.InjectDependencies(this);
_escapeMenuOwner.Initialize();
_baseClient.PlayerJoinedGame += (sender, args) =>
{
_stateManager.RequestStateChange<GameScreen>();
};
_baseClient.PlayerJoinedServer += (sender, args) =>
{
IoCManager.Resolve<IMapManager>().CreateNewMapEntity(MapId.Nullspace);
};
}
/// <summary>
/// Subscribe events to the player manager after the player manager is set up
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void SubscribePlayerAttachmentEvents(object sender, EventArgs args)
{
_playerManager.LocalPlayer.EntityAttached += AttachPlayerToEntity;
_playerManager.LocalPlayer.EntityDetached += DetachPlayerFromEntity;
}
/// <summary>
/// Add the character interface master which combines all character interfaces into one window
/// </summary>
public static void AttachPlayerToEntity(EntityAttachedEventArgs eventArgs)
{
eventArgs.NewEntity.AddComponent<CharacterInterface>();
}
/// <summary>
/// Remove the character interface master from this entity now that we have detached ourselves from it
/// </summary>
public static void DetachPlayerFromEntity(EntityDetachedEventArgs eventArgs)
{
eventArgs.OldEntity.RemoveComponent<CharacterInterface>();
}
public override void PostInit()
{
base.PostInit();
// Setup key contexts
var inputMan = IoCManager.Resolve<IInputManager>();
ContentContexts.SetupContexts(inputMan.Contexts);
IoCManager.Resolve<IGameHud>().Initialize();
IoCManager.Resolve<IClientNotifyManager>().Initialize();
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, FrameEventArgs frameEventArgs)
{
base.Update(level, frameEventArgs);
switch (level)
{
case ModUpdateLevel.FramePreEngine:
IoCManager.Resolve<IClientNotifyManager>().FrameUpdate(frameEventArgs);
IoCManager.Resolve<IClientGameTicker>().FrameUpdate(frameEventArgs);
IoCManager.Resolve<IChatManager>().FrameUpdate(frameEventArgs);
break;
}
} }
} }
} }

View File

@@ -0,0 +1,103 @@
using Content.Client.State;
using Content.Client.UserInterface;
using Robust.Client.Console;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.Placement;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.State;
using Robust.Client.State.States;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
namespace Content.Client
{
internal sealed class EscapeMenuOwner : IEscapeMenuOwner
{
#pragma warning disable 649
[Dependency] private readonly IClientConsole _clientConsole;
[Dependency] private readonly IConfigurationManager _configurationManager;
[Dependency] private readonly IInputManager _inputManager;
[Dependency] private readonly IPlacementManager _placementManager;
[Dependency] private readonly IPrototypeManager _prototypeManager;
[Dependency] private readonly IResourceCache _resourceCache;
[Dependency] private readonly IStateManager _stateManager;
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
[Dependency] private readonly IGameHud _gameHud;
[Dependency] private readonly ILocalizationManager _localizationManager;
#pragma warning restore 649
private EscapeMenu _escapeMenu;
public void Initialize()
{
_stateManager.OnStateChanged += StateManagerOnOnStateChanged;
_gameHud.EscapeButtonToggled += _setOpenValue;
}
private void StateManagerOnOnStateChanged(StateChangedEventArgs obj)
{
if (obj.NewState is GameScreen)
{
// Switched TO GameScreen.
_escapeMenu = new EscapeMenu(_clientConsole, _tileDefinitionManager, _placementManager,
_prototypeManager, _resourceCache, _configurationManager, _localizationManager);
_escapeMenu.OnClose += () => _gameHud.EscapeButtonDown = false;
_inputManager.SetInputCommand(EngineKeyFunctions.EscapeMenu,
InputCmdHandler.FromDelegate(s => Enabled()));
}
else if (obj.OldState is GameScreen)
{
// Switched FROM GameScreen.
_escapeMenu.Dispose();
_escapeMenu = null;
_inputManager.SetInputCommand(EngineKeyFunctions.EscapeMenu, null);
}
}
private void Enabled()
{
if (_escapeMenu.IsOpen)
{
if (_escapeMenu.IsAtFront())
{
_setOpenValue(false);
}
else
{
_escapeMenu.MoveToFront();
}
}
else
{
_setOpenValue(true);
}
}
private void _setOpenValue(bool value)
{
if (value)
{
_gameHud.EscapeButtonDown = true;
_escapeMenu.OpenCentered();
}
else
{
_gameHud.EscapeButtonDown = false;
_escapeMenu.Close();
}
}
}
public interface IEscapeMenuOwner
{
void Initialize();
}
}

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

@@ -0,0 +1,95 @@
using Content.Client.GameObjects.Components.Mobs;
using Content.Client.UserInterface;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
namespace Content.Client.GameObjects.Components.Actor
{
[RegisterComponent]
public sealed class CharacterInfoComponent : Component, ICharacterUI
{
private CharacterInfoControl _control;
#pragma warning disable 649
[Dependency] private readonly ILocalizationManager _loc;
[Dependency] private readonly IResourceCache _resourceCache;
#pragma warning restore 649
public override string Name => "CharacterInfo";
public Control Scene { get; private set; }
public UIPriority Priority => UIPriority.Info;
public override void OnAdd()
{
base.OnAdd();
Scene = _control = new CharacterInfoControl(_resourceCache, _loc);
}
public override void Initialize()
{
base.Initialize();
if (Owner.TryGetComponent(out ISpriteComponent spriteComponent))
{
_control.SpriteView.Sprite = spriteComponent;
}
_control.NameLabel.Text = Owner.Name;
// ReSharper disable once StringLiteralTypo
_control.SubText.Text = _loc.GetString("Professional Greyshirt");
}
private sealed class CharacterInfoControl : VBoxContainer
{
public SpriteView SpriteView { get; }
public Label NameLabel { get; }
public Label SubText { get; }
public CharacterInfoControl(IResourceCache resourceCache, ILocalizationManager loc)
{
AddChild(new HBoxContainer
{
Children =
{
(SpriteView = new SpriteView { Scale = (2, 2)}),
new VBoxContainer
{
SizeFlagsVertical = SizeFlags.None,
Children =
{
(NameLabel = new Label()),
(SubText = new Label
{
SizeFlagsVertical = SizeFlags.None,
StyleClasses = {NanoStyle.StyleClassLabelSubText}
})
}
}
}
});
AddChild(new Placeholder(resourceCache)
{
PlaceholderText = loc.GetString("Health & status effects")
});
AddChild(new Placeholder(resourceCache)
{
PlaceholderText = loc.GetString("Objectives")
});
AddChild(new Placeholder(resourceCache)
{
PlaceholderText = loc.GetString("Antagonist Roles")
});
}
}
}
}

View File

@@ -0,0 +1,151 @@
using System.Collections.Generic;
using System.Linq;
using Content.Client.GameObjects.Components.Mobs;
using Content.Client.UserInterface;
using Content.Shared.Input;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.Input;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
namespace Content.Client.GameObjects.Components.Actor
{
/// <summary>
/// A semi-abstract component which gets added to entities upon attachment and collects all character
/// user interfaces into a single window and keybind for the user
/// </summary>
[RegisterComponent]
public class CharacterInterface : Component
{
public override string Name => "Character Interface Component";
[Dependency]
#pragma warning disable 649
private readonly IGameHud _gameHud;
#pragma warning restore 649
/// <summary>
/// Window to hold each of the character interfaces
/// </summary>
/// <remarks>
/// Null if it would otherwise be empty.
/// </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>
public override void Initialize()
{
base.Initialize();
//Use all the character ui interfaced components to create the character window
_uiComponents = Owner.GetAllComponents<ICharacterUI>().ToList();
if (_uiComponents.Count == 0)
{
return;
}
Window = new CharacterWindow(_uiComponents);
Window.OnClose += () => _gameHud.CharacterButtonDown = false;
}
/// <summary>
/// Dispose of window and the keypress binding
/// </summary>
public override void OnRemove()
{
base.OnRemove();
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>();
inputMgr.SetInputCommand(ContentKeyFunctions.OpenCharacterMenu, null);
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case PlayerAttachedMsg _:
if (Window != null)
{
_gameHud.CharacterButtonVisible = true;
_gameHud.CharacterButtonToggled = b =>
{
if (b)
{
Window.Open();
}
else
{
Window.Close();
}
};
}
break;
case PlayerDetachedMsg _:
if (Window != null)
{
_gameHud.CharacterButtonVisible = false;
Window.Close();
}
break;
}
}
/// <summary>
/// A window that collects and shows all the individual character user interfaces
/// </summary>
public class CharacterWindow : SS14Window
{
private readonly VBoxContainer _contentsVBox;
public CharacterWindow(List<ICharacterUI> windowComponents)
{
Title = "Character";
_contentsVBox = new VBoxContainer();
Contents.AddChild(_contentsVBox);
windowComponents.Sort((a, b) => ((int) a.Priority).CompareTo((int) b.Priority));
foreach (var element in windowComponents)
{
_contentsVBox.AddChild(element.Scene);
}
}
}
}
/// <summary>
/// Determines ordering of the character user interface, small values come sooner
/// </summary>
public enum UIPriority
{
First = 0,
Info = 5,
Species = 100,
Last = 99999
}
}

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

@@ -0,0 +1,71 @@
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
{
[RegisterComponent]
[ComponentReference(typeof(ItemComponent))]
public class ClothingComponent : ItemComponent
{
private FemaleClothingMask _femaleMask;
public override string Name => "Clothing";
public override uint? NetID => ContentNetIDs.CLOTHING;
[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)
{
return null;
}
var rsi = GetRSI();
var prefix = ClothingEquippedPrefix ?? EquippedPrefix;
var stateId = prefix != null ? $"{prefix}-equipped-{slot}" : $"equipped-{slot}";
if (rsi.TryGetState(stateId, out _))
{
return (rsi, stateId);
}
return null;
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
if (curState == null)
return;
var clothingComponentState = (ClothingComponentState)curState;
ClothingEquippedPrefix = clothingComponentState.ClothingEquippedPrefix;
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

@@ -0,0 +1,16 @@
using Content.Shared.Construction;
using Robust.Shared.GameObjects;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.Construction
{
[RegisterComponent]
public class ConstructionGhostComponent : Component
{
public override string Name => "ConstructionGhost";
[ViewVariables] public ConstructionPrototype Prototype { get; set; }
[ViewVariables] public ConstructorComponent Master { get; set; }
[ViewVariables] public int GhostID { get; set; }
}
}

View File

@@ -0,0 +1,111 @@
using System.Collections.Generic;
using Content.Client.Construction;
using Content.Client.UserInterface;
using Content.Shared.Construction;
using Content.Shared.GameObjects.Components.Construction;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Construction
{
[RegisterComponent]
public class ConstructorComponent : SharedConstructorComponent
{
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
#pragma warning restore 649
private int nextId;
private readonly Dictionary<int, ConstructionGhostComponent> Ghosts = new Dictionary<int, ConstructionGhostComponent>();
public ConstructionMenu ConstructionMenu { get; private set; }
public override void Initialize()
{
base.Initialize();
Owner.GetComponent<ITransformComponent>();
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case PlayerAttachedMsg _:
if (ConstructionMenu == null)
{
ConstructionMenu = new ConstructionMenu {Owner = this};
ConstructionMenu.OnClose += () => _gameHud.CraftingButtonDown = false;
}
_gameHud.CraftingButtonVisible = true;
_gameHud.CraftingButtonToggled = b =>
{
if (b)
{
ConstructionMenu.Open();
}
else
{
ConstructionMenu.Close();
}
};
break;
case PlayerDetachedMsg _:
_gameHud.CraftingButtonVisible = false;
break;
case AckStructureConstructionMessage ackMsg:
ClearGhost(ackMsg.Ack);
break;
}
}
public override void OnRemove()
{
ConstructionMenu?.Dispose();
}
public void SpawnGhost(ConstructionPrototype prototype, GridCoordinates loc, Direction dir)
{
var entMgr = IoCManager.Resolve<IClientEntityManager>();
var ghost = entMgr.SpawnEntity("constructionghost", loc);
var comp = ghost.GetComponent<ConstructionGhostComponent>();
comp.Prototype = prototype;
comp.Master = this;
comp.GhostID = nextId++;
ghost.GetComponent<ITransformComponent>().LocalRotation = dir.ToAngle();
var sprite = ghost.GetComponent<SpriteComponent>();
sprite.LayerSetSprite(0, prototype.Icon);
sprite.LayerSetVisible(0, true);
Ghosts.Add(comp.GhostID, comp);
}
public void TryStartConstruction(int ghostId)
{
var ghost = Ghosts[ghostId];
var transform = ghost.Owner.GetComponent<ITransformComponent>();
var msg = new TryStartStructureConstructionMessage(transform.GridPosition, ghost.Prototype.ID, transform.LocalRotation, ghostId);
SendNetworkMessage(msg);
}
public void ClearGhost(int ghostId)
{
if (Ghosts.TryGetValue(ghostId, out var ghost))
{
ghost.Owner.Delete();
Ghosts.Remove(ghostId);
}
}
}
}

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using Content.Shared.GameObjects;
using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects
{
/// <summary>
/// Fuck I really hate doing this
/// TODO: make sure the client only gets damageable component on the clientside entity for its player mob
/// </summary>
[RegisterComponent]
public class DamageableComponent : SharedDamageableComponent
{
/// <inheritdoc />
public override string Name => "Damageable";
public Dictionary<DamageType, int> CurrentDamage = new Dictionary<DamageType, int>();
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if(curState is DamageComponentState damagestate)
{
CurrentDamage = damagestate.CurrentDamage;
}
}
}
}

View File

@@ -0,0 +1,154 @@
using System;
using Content.Client.GameObjects.Components.Wires;
using Content.Shared.GameObjects.Components.Doors;
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 Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Content.Client.GameObjects.Components.Doors
{
public class AirlockVisualizer2D : AppearanceVisualizer
{
private const string AnimationKey = "airlock_animation";
private Animation CloseAnimation;
private Animation OpenAnimation;
private Animation DenyAnimation;
public override void LoadData(YamlMappingNode node)
{
base.LoadData(node);
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)};
{
var flick = new AnimationTrackSpriteFlick();
CloseAnimation.AnimationTracks.Add(flick);
flick.LayerKey = DoorVisualLayers.Base;
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("closing", 0f));
var flickUnlit = new AnimationTrackSpriteFlick();
CloseAnimation.AnimationTracks.Add(flickUnlit);
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));
}
OpenAnimation = new Animation {Length = TimeSpan.FromSeconds(1.2f)};
{
var flick = new AnimationTrackSpriteFlick();
OpenAnimation.AnimationTracks.Add(flick);
flick.LayerKey = DoorVisualLayers.Base;
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("opening", 0f));
var flickUnlit = new AnimationTrackSpriteFlick();
OpenAnimation.AnimationTracks.Add(flickUnlit);
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)
{
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(DoorVisuals.VisualState, out DoorVisualState state))
{
state = DoorVisualState.Closed;
}
var unlitVisible = true;
switch (state)
{
case DoorVisualState.Closed:
sprite.LayerSetState(DoorVisualLayers.Base, "closed");
sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "closed_unlit");
sprite.LayerSetState(WiresVisualizer2D.WiresVisualLayers.MaintenancePanel, "panel_open");
break;
case DoorVisualState.Closing:
if (!animPlayer.HasRunningAnimation(AnimationKey))
{
animPlayer.Play(CloseAnimation, AnimationKey);
}
break;
case DoorVisualState.Opening:
if (!animPlayer.HasRunningAnimation(AnimationKey))
{
animPlayer.Play(OpenAnimation, AnimationKey);
}
break;
case DoorVisualState.Open:
sprite.LayerSetState(DoorVisualLayers.Base, "open");
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);
}
}
public enum DoorVisualLayers
{
Base,
BaseUnlit
}
}

View File

@@ -0,0 +1,208 @@
// 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.ViewVariables;
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
using static Content.Shared.GameObjects.SharedInventoryComponent.ClientInventoryMessage;
namespace Content.Client.GameObjects
{
/// <summary>
/// A character UI which shows items the user has equipped within his inventory
/// </summary>
[RegisterComponent]
public class ClientInventoryComponent : SharedInventoryComponent
{
private readonly Dictionary<Slots, IEntity> _slots = new Dictionary<Slots, IEntity>();
[ViewVariables]
public InventoryInterfaceController InterfaceController { get; private set; }
private ISpriteComponent _sprite;
private bool _playerAttached = false;
public override void OnRemove()
{
base.OnRemove();
if (_playerAttached)
{
InterfaceController?.PlayerDetached();
}
InterfaceController?.Dispose();
}
public override void Initialize()
{
base.Initialize();
var controllerType = ReflectionManager.LooseGetType(InventoryInstance.InterfaceControllerTypeName);
var args = new object[] {this};
InterfaceController = DynamicTypeFactory.CreateInstance<InventoryInterfaceController>(controllerType, args);
InterfaceController.Initialize();
if (Owner.TryGetComponent(out _sprite))
{
foreach (var mask in InventoryInstance.SlotMasks.OrderBy(s => InventoryInstance.SlotDrawingOrder(s)))
{
if (mask == Slots.NONE)
{
continue;
}
_sprite.LayerMapReserveBlank(mask);
}
}
// Component state already came in but we couldn't set anything visually because, well, we didn't initialize yet.
foreach (var (slot, entity) in _slots)
{
_setSlot(slot, entity);
}
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if (curState == null)
return;
var cast = (InventoryComponentState) curState;
var doneSlots = new HashSet<Slots>();
foreach (var (slot, entityUid) in cast.Entities)
{
var entity = Owner.EntityManager.GetEntity(entityUid);
if (!_slots.ContainsKey(slot) || _slots[slot] != entity)
{
_slots[slot] = entity;
_setSlot(slot, entity);
}
doneSlots.Add(slot);
}
foreach (var slot in _slots.Keys.ToList())
{
if (!doneSlots.Contains(slot))
{
_clearSlot(slot);
_slots.Remove(slot);
}
}
}
private void _setSlot(Slots slot, IEntity entity)
{
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)
{
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;
}
}
_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)
{
InterfaceController?.RemoveFromSlot(slot);
_sprite?.LayerSetVisible(slot, false);
}
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)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
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

@@ -0,0 +1,221 @@
// Only unused on .NET Core due to KeyValuePair.Deconstruct
// ReSharper disable once RedundantUsingDirective
using Content.Client.Utility;
using JetBrains.Annotations;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
using Content.Client.UserInterface;
using System.Collections.Generic;
namespace Content.Client.GameObjects
{
// Dynamically instantiated by ClientInventoryComponent.
[UsedImplicitly]
public class HumanInventoryInterfaceController : InventoryInterfaceController
{
#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<ItemSlotButton>> _inventoryButtons
= new Dictionary<Slots, List<ItemSlotButton>>();
private ItemSlotButton _hudButtonPocket1;
private ItemSlotButton _hudButtonPocket2;
private ItemSlotButton _hudButtonBelt;
private ItemSlotButton _hudButtonBack;
private ItemSlotButton _hudButtonId;
private Control _quickButtonsContainer;
public HumanInventoryInterfaceController(ClientInventoryComponent owner) : base(owner)
{
}
public override void Initialize()
{
base.Initialize();
_window = new HumanInventoryWindow(_loc, _resourceCache);
_window.OnClose += () => _gameHud.InventoryButtonDown = false;
foreach (var (slot, button) in _window.Buttons)
{
button.OnPressed = (e) => AddToInventory(e, slot);
button.OnStoragePressed = (e) => OpenStorage(e, slot);
_inventoryButtons.Add(slot, new List<ItemSlotButton> {button});
}
void AddButton(out ItemSlotButton variable, Slots slot, string textureName)
{
var texture = _resourceCache.GetTexture($"/Textures/UserInterface/Inventory/{textureName}.png");
var storageTexture = _resourceCache.GetTexture("/Textures/UserInterface/Inventory/back.png");
variable = new ItemSlotButton(texture, storageTexture)
{
OnPressed = (e) => AddToInventory(e, slot),
OnStoragePressed = (e) => OpenStorage(e, slot)
};
_inventoryButtons[slot].Add(variable);
}
AddButton(out _hudButtonPocket1, Slots.POCKET1, "pocket");
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,
_hudButtonPocket2,
}
};
}
public override SS14Window Window => _window;
private HumanInventoryWindow _window;
public override void AddToSlot(Slots slot, IEntity entity)
{
base.AddToSlot(slot, entity);
if (!_inventoryButtons.TryGetValue(slot, out var buttons))
return;
foreach (var button in buttons)
{
_itemSlotManager.SetItemSlot(button, entity);
button.OnPressed = (e) => HandleInventoryKeybind(e, slot);
}
}
public override void RemoveFromSlot(Slots slot)
{
base.RemoveFromSlot(slot);
if (!_inventoryButtons.TryGetValue(slot, out var buttons))
{
return;
}
foreach (var button in buttons)
{
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);
}
public override void PlayerDetached()
{
base.PlayerDetached();
_gameHud.InventoryQuickButtonContainer.RemoveChild(_quickButtonsContainer);
foreach (var (slot, list) in _inventoryButtons)
{
foreach (var button in list)
{
ClearButton(button, slot);
}
}
}
private class HumanInventoryWindow : SS14Window
{
private const int ButtonSize = 64;
private const int ButtonSeparation = 2;
private const int RightSeparation = 2;
public IReadOnlyDictionary<Slots, ItemSlotButton> Buttons { get; }
public HumanInventoryWindow(ILocalizationManager loc, IResourceCache resourceCache)
{
Title = loc.GetString("Your Inventory");
Resizable = false;
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 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 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);
}
const int size = ButtonSize;
const int sep = ButtonSeparation;
const int rSep = RightSeparation;
// Left column.
AddButton(Slots.EYES, "glasses", (0, size + sep));
AddButton(Slots.INNERCLOTHING, "uniform", (0, 2 * (size + sep)));
AddButton(Slots.EXOSUITSLOT1, "suit_storage", (0, 3 * (size + sep)));
// Middle column.
AddButton(Slots.HEAD, "head", (size + sep, 0));
AddButton(Slots.MASK, "mask", (size + sep, size + sep));
AddButton(Slots.OUTERCLOTHING, "suit", (size + sep, 2 * (size + sep)));
AddButton(Slots.SHOES, "shoes", (size + sep, 3 * (size + sep)));
// Right column
AddButton(Slots.EARS, "ears", (2 * (size + sep), 0));
AddButton(Slots.IDCARD, "id", (2 * (size + sep), size + sep));
AddButton(Slots.GLOVES, "gloves", (2 * (size + sep), 2 * (size + sep)));
// Far right column.
AddButton(Slots.BACKPACK, "back", (rSep + 3 * (size + sep), 0));
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)));
}
}
}
}

View File

@@ -0,0 +1,102 @@
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;
using Robust.Shared.IoC;
namespace Content.Client.GameObjects
{
public abstract class InventoryInterfaceController : IDisposable
{
#pragma warning disable 649
[Dependency] protected readonly IGameHud _gameHud;
#pragma warning restore 649
protected InventoryInterfaceController(ClientInventoryComponent owner)
{
Owner = owner;
}
public virtual void Initialize()
{
}
public abstract SS14Window Window { get; }
protected ClientInventoryComponent Owner { get; }
public virtual void PlayerAttached()
{
_gameHud.InventoryButtonVisible = true;
_gameHud.InventoryButtonToggled = b =>
{
if (b)
{
Window.Open();
}
else
{
Window.Close();
}
};
}
public virtual void PlayerDetached()
{
_gameHud.InventoryButtonVisible = false;
Window.Close();
}
public virtual void Dispose()
{
}
public virtual void AddToSlot(EquipmentSlotDefines.Slots slot, IEntity entity)
{
}
public virtual void RemoveFromSlot(EquipmentSlotDefines.Slots slot)
{
}
protected virtual void HandleInventoryKeybind(BaseButton.ButtonEventArgs args, EquipmentSlotDefines.Slots slot)
{
if (args.Event.CanFocus)
{
UseItemOnInventory(args, slot);
}
}
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;
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

@@ -0,0 +1,289 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Content.Client.GameObjects.EntitySystems;
using JetBrains.Annotations;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using static Robust.Client.GameObjects.SpriteComponent;
namespace Content.Client.GameObjects.Components.IconSmoothing
{
// TODO: Potential improvements:
// Defer updating of these.
// Get told by somebody to use a loop.
/// <summary>
/// Makes sprites of other grid-aligned entities like us connect.
/// </summary>
/// <remarks>
/// The system is based on Baystation12's smoothwalling, and thus will work with those.
/// To use, set <c>base</c> equal to the prefix of the corner states in the sprite base RSI.
/// Any objects with the same <c>key</c> will connect.
/// </remarks>
[RegisterComponent]
public class IconSmoothComponent : Component
{
private string _smoothKey;
private string _stateBase;
private IconSmoothingMode _mode;
public override string Name => "IconSmooth";
internal ISpriteComponent Sprite { get; private set; }
internal SnapGridComponent SnapGrid { get; private set; }
private (GridId, MapIndices) _lastPosition;
/// <summary>
/// We will smooth with other objects with the same key.
/// </summary>
public string SmoothKey => _smoothKey;
/// <summary>
/// Prepended to the RSI state.
/// </summary>
public string StateBase => _stateBase;
/// <summary>
/// Mode that controls how the icon should be selected.
/// </summary>
public IconSmoothingMode Mode => _mode;
/// <summary>
/// Used by <see cref="IconSmoothSystem"/> to reduce redundant updates.
/// </summary>
internal int UpdateGeneration { get; set; }
public override void Initialize()
{
base.Initialize();
SnapGrid = Owner.GetComponent<SnapGridComponent>();
Sprite = Owner.GetComponent<ISpriteComponent>();
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataFieldCached(ref _stateBase, "base", "");
serializer.DataFieldCached(ref _smoothKey, "key", null);
serializer.DataFieldCached(ref _mode, "mode", IconSmoothingMode.Corners);
}
/// <inheritdoc />
protected override void Startup()
{
base.Startup();
SnapGrid.OnPositionChanged += SnapGridOnPositionChanged;
Owner.EntityManager.EventBus.RaiseEvent(Owner, new IconSmoothDirtyEvent(null, SnapGrid.Offset, Mode));
if (Mode == IconSmoothingMode.Corners)
{
var state0 = $"{StateBase}0";
Sprite.LayerMapSet(CornerLayers.SE, Sprite.AddLayerState(state0));
Sprite.LayerSetDirOffset(CornerLayers.SE, DirectionOffset.None);
Sprite.LayerMapSet(CornerLayers.NE, Sprite.AddLayerState(state0));
Sprite.LayerSetDirOffset(CornerLayers.NE, DirectionOffset.CounterClockwise);
Sprite.LayerMapSet(CornerLayers.NW, Sprite.AddLayerState(state0));
Sprite.LayerSetDirOffset(CornerLayers.NW, DirectionOffset.Flip);
Sprite.LayerMapSet(CornerLayers.SW, Sprite.AddLayerState(state0));
Sprite.LayerSetDirOffset(CornerLayers.SW, DirectionOffset.Clockwise);
}
}
internal virtual void CalculateNewSprite()
{
switch (Mode)
{
case IconSmoothingMode.Corners:
CalculateNewSpriteCorers();
break;
case IconSmoothingMode.CardinalFlags:
CalculateNewSpriteCardinal();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private void CalculateNewSpriteCardinal()
{
var dirs = CardinalConnectDirs.None;
if (MatchingEntity(SnapGrid.GetInDir(Direction.North)))
dirs |= CardinalConnectDirs.North;
if (MatchingEntity(SnapGrid.GetInDir(Direction.South)))
dirs |= CardinalConnectDirs.South;
if (MatchingEntity(SnapGrid.GetInDir(Direction.East)))
dirs |= CardinalConnectDirs.East;
if (MatchingEntity(SnapGrid.GetInDir(Direction.West)))
dirs |= CardinalConnectDirs.West;
Sprite.LayerSetState(0, $"{StateBase}{(int) dirs}");
}
private void CalculateNewSpriteCorers()
{
var n = MatchingEntity(SnapGrid.GetInDir(Direction.North));
var ne = MatchingEntity(SnapGrid.GetInDir(Direction.NorthEast));
var e = MatchingEntity(SnapGrid.GetInDir(Direction.East));
var se = MatchingEntity(SnapGrid.GetInDir(Direction.SouthEast));
var s = MatchingEntity(SnapGrid.GetInDir(Direction.South));
var sw = MatchingEntity(SnapGrid.GetInDir(Direction.SouthWest));
var w = MatchingEntity(SnapGrid.GetInDir(Direction.West));
var nw = MatchingEntity(SnapGrid.GetInDir(Direction.NorthWest));
// ReSharper disable InconsistentNaming
var cornerNE = CornerFill.None;
var cornerSE = CornerFill.None;
var cornerSW = CornerFill.None;
var cornerNW = CornerFill.None;
// ReSharper restore InconsistentNaming
if (n)
{
cornerNE |= CornerFill.CounterClockwise;
cornerNW |= CornerFill.Clockwise;
}
if (ne)
{
cornerNE |= CornerFill.Diagonal;
}
if (e)
{
cornerNE |= CornerFill.Clockwise;
cornerSE |= CornerFill.CounterClockwise;
}
if (se)
{
cornerSE |= CornerFill.Diagonal;
}
if (s)
{
cornerSE |= CornerFill.Clockwise;
cornerSW |= CornerFill.CounterClockwise;
}
if (sw)
{
cornerSW |= CornerFill.Diagonal;
}
if (w)
{
cornerSW |= CornerFill.Clockwise;
cornerNW |= CornerFill.CounterClockwise;
}
if (nw)
{
cornerNW |= CornerFill.Diagonal;
}
Sprite.LayerSetState(CornerLayers.NE, $"{StateBase}{(int) cornerNE}");
Sprite.LayerSetState(CornerLayers.SE, $"{StateBase}{(int) cornerSE}");
Sprite.LayerSetState(CornerLayers.SW, $"{StateBase}{(int) cornerSW}");
Sprite.LayerSetState(CornerLayers.NW, $"{StateBase}{(int) cornerNW}");
}
/// <inheritdoc />
protected override void Shutdown()
{
base.Shutdown();
SnapGrid.OnPositionChanged -= SnapGridOnPositionChanged;
Owner.EntityManager.EventBus.RaiseEvent(Owner, new IconSmoothDirtyEvent(_lastPosition, SnapGrid.Offset, Mode));
}
private void SnapGridOnPositionChanged()
{
Owner.EntityManager.EventBus.RaiseEvent(Owner, new IconSmoothDirtyEvent(_lastPosition, SnapGrid.Offset, Mode));
_lastPosition = (Owner.Transform.GridID, SnapGrid.Position);
}
[System.Diagnostics.Contracts.Pure]
protected bool MatchingEntity(IEnumerable<IEntity> candidates)
{
foreach (var entity in candidates)
{
if (!entity.TryGetComponent(out IconSmoothComponent other))
{
continue;
}
if (other.SmoothKey == SmoothKey)
{
return true;
}
}
return false;
}
[Flags]
private enum CardinalConnectDirs : byte
{
None = 0,
North = 1,
South = 2,
East = 4,
West = 8
}
[Flags]
public enum CornerFill : byte
{
// These values are pulled from Baystation12.
// I'm too lazy to convert the state names.
None = 0,
// The cardinal tile counter-clockwise of this corner is filled.
CounterClockwise = 1,
// The diagonal tile in the direction of this corner.
Diagonal = 2,
// The cardinal tile clockwise of this corner is filled.
Clockwise = 4,
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum CornerLayers
{
SE,
NE,
NW,
SW,
}
}
/// <summary>
/// Controls the mode with which icon smoothing is calculated.
/// </summary>
[PublicAPI]
public enum IconSmoothingMode
{
/// <summary>
/// Each icon is made up of 4 corners, each of which can get a different state depending on
/// adjacent entities clockwise, counter-clockwise and diagonal with the corner.
/// </summary>
Corners,
/// <summary>
/// There are 16 icons, only one of which is used at once.
/// The icon selected is a bit field made up of the cardinal direction flags that have adjacent entities.
/// </summary>
CardinalFlags,
}
}

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

@@ -0,0 +1,199 @@
// 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;
using Content.Shared.GameObjects;
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.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects
{
[RegisterComponent]
[ComponentReference(typeof(IHandsComponent))]
public class HandsComponent : SharedHandsComponent, IHandsComponent
{
private HandsGui _gui;
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
#pragma warning restore 649
[ViewVariables] private readonly Dictionary<string, IEntity> _hands = new Dictionary<string, IEntity>();
[ViewVariables] public string ActiveIndex { get; private set; }
[ViewVariables] private ISpriteComponent _sprite;
[ViewVariables] public IEntity ActiveHand => GetEntity(ActiveIndex);
public override void OnRemove()
{
base.OnRemove();
_gui?.Dispose();
}
public override void Initialize()
{
base.Initialize();
if (Owner.TryGetComponent(out _sprite))
{
foreach (var slot in _hands.Keys)
{
_sprite.LayerMapReserveBlank($"hand-{slot}");
_setHand(slot, _hands[slot]);
}
}
}
public IEntity GetEntity(string index)
{
if (_hands.TryGetValue(index, out var entity))
{
return entity;
}
return null;
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
if (curState == null)
return;
var cast = (HandsComponentState) curState;
foreach (var (slot, uid) in cast.Hands)
{
IEntity entity = null;
try
{
entity = Owner.EntityManager.GetEntity(uid);
}
catch
{
// Nothing.
}
_hands[slot] = entity;
_setHand(slot, entity);
}
foreach (var slot in _hands.Keys.ToList())
{
if (!cast.Hands.ContainsKey(slot))
{
_hands[slot] = null;
_setHand(slot, null);
}
}
ActiveIndex = cast.ActiveIndex;
_gui?.UpdateHandIcons();
}
private void _setHand(string hand, IEntity entity)
{
if (_sprite == null)
{
return;
}
if (entity == null)
{
_sprite.LayerSetVisible($"hand-{hand}", false);
return;
}
var item = entity.GetComponent<ItemComponent>();
var maybeInhands = item.GetInHandStateInfo(hand);
if (!maybeInhands.HasValue)
{
_sprite.LayerSetVisible($"hand-{hand}", false);
}
else
{
var (rsi, state) = maybeInhands.Value;
_sprite.LayerSetVisible($"hand-{hand}", true);
_sprite.LayerSetState($"hand-{hand}", state, rsi);
}
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
if (!serializer.Reading)
{
return;
}
foreach (var slot in serializer.ReadDataFieldCached("hands", new List<string>()))
{
_hands.Add(slot, null);
}
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case PlayerAttachedMsg _:
if (_gui == null)
{
_gui = new HandsGui();
}
else
{
_gui.Parent?.RemoveChild(_gui);
}
_gameHud.HandsContainer.AddChild(_gui);
_gui.UpdateHandIcons();
break;
case PlayerDetachedMsg _:
_gui.Parent?.RemoveChild(_gui);
break;
}
}
public void SendChangeHand(string index)
{
SendNetworkMessage(new ClientChangedHandMsg(index));
}
public void AttackByInHand(string index)
{
SendNetworkMessage(new ClientAttackByInHandMsg(index));
}
public void UseActiveHand()
{
if (GetEntity(ActiveIndex) != null)
{
SendNetworkMessage(new UseInHandMsg());
}
}
public void ActivateItemInHand(string handIndex)
{
if (GetEntity(handIndex) == null)
return;
SendNetworkMessage(new ActivateInHandMsg(handIndex));
}
}
}

View File

@@ -0,0 +1,72 @@
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Items;
using Robust.Client.Graphics;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Renderable;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects
{
[RegisterComponent]
public class ItemComponent : Component
{
public override string Name => "Item";
public override uint? NetID => ContentNetIDs.ITEM;
[ViewVariables] protected ResourcePath RsiPath;
private string _equippedPrefix;
[ViewVariables(VVAccess.ReadWrite)]
public string EquippedPrefix
{
get => _equippedPrefix;
set => _equippedPrefix = value;
}
public (RSI rsi, RSI.StateId stateId)? GetInHandStateInfo(string hand)
{
if (RsiPath == null)
{
return null;
}
var rsi = GetRSI();
var stateId = EquippedPrefix != null ? $"{EquippedPrefix}-inhand-{hand}" : $"inhand-{hand}";
if (rsi.TryGetState(stateId, out _))
{
return (rsi, stateId);
}
return null;
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataFieldCached(ref RsiPath, "sprite", null);
serializer.DataFieldCached(ref _equippedPrefix, "prefix", null);
}
protected RSI GetRSI()
{
var resourceCache = IoCManager.Resolve<IResourceCache>();
return resourceCache.GetResource<RSIResource>(SharedSpriteComponent.TextureRoot / RsiPath).RSI;
}
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
if(curState == null)
return;
var itemComponentState = (ItemComponentState)curState;
EquippedPrefix = itemComponentState.EquippedPrefix;
}
}
}

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

@@ -0,0 +1,228 @@
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
{
// TODO: Over layers should be placed ABOVE the window itself too.
// This is gonna require a client entity & parenting,
// so IsMapTransform being naive is gonna be a problem.
/// <summary>
/// Override of icon smoothing to handle the specific complexities of low walls.
/// </summary>
[RegisterComponent]
[ComponentReference(typeof(IconSmoothComponent))]
public class LowWallComponent : IconSmoothComponent
{
public override string Name => "LowWall";
public CornerFill LastCornerNE { get; private set; }
public CornerFill LastCornerSE { get; private set; }
public CornerFill LastCornerSW { get; private set; }
public CornerFill LastCornerNW { get; private set; }
[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";
_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()
{
base.CalculateNewSprite();
var (n, nl) = MatchingWall(SnapGrid.GetInDir(Direction.North));
var (ne, nel) = MatchingWall(SnapGrid.GetInDir(Direction.NorthEast));
var (e, el) = MatchingWall(SnapGrid.GetInDir(Direction.East));
var (se, sel) = MatchingWall(SnapGrid.GetInDir(Direction.SouthEast));
var (s, sl) = MatchingWall(SnapGrid.GetInDir(Direction.South));
var (sw, swl) = MatchingWall(SnapGrid.GetInDir(Direction.SouthWest));
var (w, wl) = MatchingWall(SnapGrid.GetInDir(Direction.West));
var (nw, nwl) = MatchingWall(SnapGrid.GetInDir(Direction.NorthWest));
// ReSharper disable InconsistentNaming
var cornerNE = CornerFill.None;
var cornerSE = CornerFill.None;
var cornerSW = CornerFill.None;
var cornerNW = CornerFill.None;
var lowCornerNE = CornerFill.None;
var lowCornerSE = CornerFill.None;
var lowCornerSW = CornerFill.None;
var lowCornerNW = CornerFill.None;
// ReSharper restore InconsistentNaming
if (n)
{
cornerNE |= CornerFill.CounterClockwise;
cornerNW |= CornerFill.Clockwise;
if (!nl)
{
lowCornerNE |= CornerFill.CounterClockwise;
lowCornerNW |= CornerFill.Clockwise;
}
}
if (ne)
{
cornerNE |= CornerFill.Diagonal;
if (!nel && (nl || el || n && e))
{
lowCornerNE |= CornerFill.Diagonal;
}
}
if (e)
{
cornerNE |= CornerFill.Clockwise;
cornerSE |= CornerFill.CounterClockwise;
if (!el)
{
lowCornerNE |= CornerFill.Clockwise;
lowCornerSE |= CornerFill.CounterClockwise;
}
}
if (se)
{
cornerSE |= CornerFill.Diagonal;
if (!sel && (sl || el || s && e))
{
lowCornerSE |= CornerFill.Diagonal;
}
}
if (s)
{
cornerSE |= CornerFill.Clockwise;
cornerSW |= CornerFill.CounterClockwise;
if (!sl)
{
lowCornerSE |= CornerFill.Clockwise;
lowCornerSW |= CornerFill.CounterClockwise;
}
}
if (sw)
{
cornerSW |= CornerFill.Diagonal;
if (!swl && (sl || wl || s && w))
{
lowCornerSW |= CornerFill.Diagonal;
}
}
if (w)
{
cornerSW |= CornerFill.Clockwise;
cornerNW |= CornerFill.CounterClockwise;
if (!wl)
{
lowCornerSW |= CornerFill.Clockwise;
lowCornerNW |= CornerFill.CounterClockwise;
}
}
if (nw)
{
cornerNW |= CornerFill.Diagonal;
if (!nwl && (nl || wl || n && w))
{
lowCornerNW |= CornerFill.Diagonal;
}
}
Sprite.LayerSetState(CornerLayers.NE, $"{StateBase}{(int) cornerNE}");
Sprite.LayerSetState(CornerLayers.SE, $"{StateBase}{(int) cornerSE}");
Sprite.LayerSetState(CornerLayers.SW, $"{StateBase}{(int) cornerSW}");
Sprite.LayerSetState(CornerLayers.NW, $"{StateBase}{(int) cornerNW}");
_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;
LastCornerSW = cornerSW;
LastCornerNW = cornerNW;
foreach (var entity in SnapGrid.GetLocal())
{
if (entity.TryGetComponent(out WindowComponent window))
{
window.UpdateSprite();
}
}
}
[Pure]
private (bool connected, bool lowWall) MatchingWall(IEnumerable<IEntity> candidates)
{
foreach (var entity in candidates)
{
if (!entity.TryGetComponent(out IconSmoothComponent other))
{
continue;
}
if (other.SmoothKey == SmoothKey)
{
return (true, other is LowWallComponent);
}
}
return (false, false);
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
private enum OverCornerLayers
{
SE,
NE,
NW,
SW,
}
}
}

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

@@ -0,0 +1,105 @@
using System;
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.Maths;
namespace Content.Client.GameObjects.Components.Mobs
{
[RegisterComponent]
[ComponentReference(typeof(SharedCameraRecoilComponent))]
public sealed class CameraRecoilComponent : SharedCameraRecoilComponent
{
// Maximum rate of magnitude restore towards 0 kick.
private const float RestoreRateMax = 2f;
// Minimum rate of magnitude restore towards 0 kick.
private const float RestoreRateMin = 1f;
// Time in seconds since the last kick that lerps RestoreRateMin and RestoreRateMax
private const float RestoreRateRamp = 0.05f;
// The maximum magnitude of the kick applied to the camera at any point.
private const float KickMagnitudeMax = 0.25f;
private Vector2 _currentKick;
private float _lastKickTime;
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();
_eye = Owner.GetComponent<EyeComponent>();
}
public override void Kick(Vector2 recoil)
{
// Use really bad math to "dampen" kicks when we're already kicked.
var existing = _currentKick.Length;
var dampen = existing/KickMagnitudeMax;
_currentKick += recoil * (1-dampen);
if (_currentKick.Length > KickMagnitudeMax)
{
_currentKick = _currentKick.Normalized * KickMagnitudeMax;
}
_lastKickTime = 0;
_updateEye();
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case RecoilKickMessage msg:
Kick(msg.Recoil);
break;
}
}
public void FrameUpdate(float frameTime)
{
var magnitude = _currentKick.Length;
if (magnitude <= 0.005f)
{
_currentKick = Vector2.Zero;
_updateEye();
return;
}
// Continually restore camera to 0.
var normalized = _currentKick.Normalized;
var restoreRate = FloatMath.Lerp(RestoreRateMin, RestoreRateMax, Math.Min(1, _lastKickTime/RestoreRateRamp));
var restore = normalized * restoreRate * frameTime;
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 = 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,21 @@
using Content.Client.GameObjects.Components.Actor;
using Robust.Client.UserInterface;
namespace Content.Client.GameObjects.Components.Mobs
{
/// <summary>
/// An interface which is gathered to assemble the character window from multiple components
/// </summary>
public interface ICharacterUI
{
/// <summary>
/// The godot control which holds the character user interface to be included in the window
/// </summary>
Control Scene { get; }
/// <summary>
/// The order it will appear in the character UI, higher is lower
/// </summary>
UIPriority Priority { get; }
}
}

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

@@ -0,0 +1,29 @@
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Mobs
{
public class SpeciesVisualizer2D : AppearanceVisualizer
{
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (component.TryGetData<SharedSpeciesComponent.MobState>(SharedSpeciesComponent.MobVisuals.RotationState, out var state))
{
switch (state)
{
case SharedSpeciesComponent.MobState.Stand:
sprite.Rotation = 0;
break;
case SharedSpeciesComponent.MobState.Down:
sprite.Rotation = Angle.FromDegrees(90);
break;
}
}
}
}
}

View File

@@ -0,0 +1,35 @@
using System;
using Content.Shared.GameObjects.Components.Movement;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
namespace Content.Client.GameObjects.Components.Movement
{
[UsedImplicitly]
public class HandTeleporterVisualizer2D : AppearanceVisualizer
{
public override void OnChangeData(AppearanceComponent component)
{
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (!component.TryGetData(TeleporterVisuals.VisualState, out TeleporterVisualState state))
{
state = TeleporterVisualState.Ready;
}
switch (state)
{
case TeleporterVisualState.Charging:
sprite.LayerSetState(0, "charging");
break;
case TeleporterVisualState.Ready:
sprite.LayerSetState(0, "ready");
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}

View File

@@ -0,0 +1,55 @@
using Content.Shared.GameObjects.Components.Movement;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Client.GameObjects.Components.Movement
{
[UsedImplicitly]
public class PortalVisualizer2D : AppearanceVisualizer
{
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
var sprite = entity.GetComponent<ISpriteComponent>();
sprite.LayerMapSet(Layers.Portal, sprite.AddLayerState("portal-pending"));
sprite.LayerSetShader(Layers.Portal, "unshaded");
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (component.TryGetData<PortalState>(PortalVisuals.State, out var state))
{
switch (state)
{
case PortalState.Pending:
sprite.LayerSetState(Layers.Portal, "portal-pending");
break;
// TODO: Spritework here?
case PortalState.UnableToTeleport:
sprite.LayerSetState(Layers.Portal, "portal-unconnected");
break;
case PortalState.RecentlyTeleported:
sprite.LayerSetState(Layers.Portal, "portal-unconnected");
break;
}
}
else
{
sprite.LayerSetState(Layers.Portal, "portal-pending");
}
}
enum Layers
{
Portal
}
}
}

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

@@ -0,0 +1,168 @@
using System;
using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components.Power;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Client.Graphics.Drawing;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Power
{
public class ApcBoundUserInterface : BoundUserInterface
{
private ApcWindow _window;
private BaseButton _breakerButton;
private Label _externalPowerStateLabel;
private ProgressBar _chargeBar;
protected override void Open()
{
base.Open();
_window = new ApcWindow();
_window.OnClose += Close;
_window.OpenCenteredMinSize();
_breakerButton = _window.BreakerButton;
_breakerButton.OnPressed += _ => SendMessage(new ApcToggleMainBreakerMessage());
_externalPowerStateLabel = _window.ExternalPowerStateLabel;
_chargeBar = _window.ChargeBar;
}
public ApcBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
var castState = (ApcBoundInterfaceState) state;
_breakerButton.Pressed = castState.MainBreaker;
switch (castState.ApcExternalPower)
{
case ApcExternalPowerState.None:
_externalPowerStateLabel.Text = "None";
_externalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateNone);
break;
case ApcExternalPowerState.Low:
_externalPowerStateLabel.Text = "Low";
_externalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateLow);
break;
case ApcExternalPowerState.Good:
_externalPowerStateLabel.Text = "Good";
_externalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateGood);
break;
default:
throw new ArgumentOutOfRangeException();
}
_chargeBar.Value = castState.Charge;
UpdateChargeBarColor(castState.Charge);
var chargePercentage = (castState.Charge / _chargeBar.MaxValue) * 100.0f;
_window.ChargePercentage.Text = " " + chargePercentage.ToString("0.00") + "%";
}
private void UpdateChargeBarColor(float charge)
{
var normalizedCharge = charge / _chargeBar.MaxValue;
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
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
finalHue = FloatMath.Lerp(leftHue, middleHue, normalizedCharge);
}
else
{
normalizedCharge = (normalizedCharge - leftSideSize) / rightSideSize; // Adjust range to 0.0 to 1.0.
finalHue = FloatMath.Lerp(middleHue, rightHue, normalizedCharge);
}
// Check if null first to avoid repeatedly creating this.
if (_chargeBar.ForegroundStyleBoxOverride == null)
{
_chargeBar.ForegroundStyleBoxOverride = new StyleBoxFlat();
}
var foregroundStyleBoxOverride = (StyleBoxFlat) _chargeBar.ForegroundStyleBoxOverride;
foregroundStyleBoxOverride.BackgroundColor =
Color.FromHsv(new Vector4(finalHue, saturation, value, alpha));
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_window.Dispose();
}
}
private class ApcWindow : SS14Window
{
public Button BreakerButton { get; set; }
public Label ExternalPowerStateLabel { get; set; }
public ProgressBar ChargeBar { get; set; }
public Label ChargePercentage { get; set; }
public ApcWindow()
{
Title = "APC";
var rows = new VBoxContainer();
var statusHeader = new Label {Text = "Power Status: "};
rows.AddChild(statusHeader);
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();
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();
var chargeLabel = new Label {Text = "Charge:"};
ChargeBar = new ProgressBar
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
MinValue = 0.0f,
MaxValue = 1.0f,
Page = 0.0f,
Value = 0.5f
};
ChargePercentage = new Label();
charge.AddChild(chargeLabel);
charge.AddChild(ChargeBar);
charge.AddChild(ChargePercentage);
rows.AddChild(charge);
Contents.AddChild(rows);
}
}
}
}

View File

@@ -0,0 +1,67 @@
using Content.Shared.GameObjects.Components.Power;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Client.GameObjects.Components.Power
{
public class ApcVisualizer2D : AppearanceVisualizer
{
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
var sprite = entity.GetComponent<ISpriteComponent>();
sprite.LayerMapSet(Layers.ChargeState, sprite.AddLayerState("apco3-0"));
sprite.LayerSetShader(Layers.ChargeState, "unshaded");
sprite.LayerMapSet(Layers.Lock, sprite.AddLayerState("apcox-0"));
sprite.LayerSetShader(Layers.Lock, "unshaded");
sprite.LayerMapSet(Layers.Equipment, sprite.AddLayerState("apco0-3"));
sprite.LayerSetShader(Layers.Equipment, "unshaded");
sprite.LayerMapSet(Layers.Lighting, sprite.AddLayerState("apco1-3"));
sprite.LayerSetShader(Layers.Lighting, "unshaded");
sprite.LayerMapSet(Layers.Environment, sprite.AddLayerState("apco2-3"));
sprite.LayerSetShader(Layers.Environment, "unshaded");
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (component.TryGetData<ApcChargeState>(ApcVisuals.ChargeState, out var state))
{
switch (state)
{
case ApcChargeState.Lack:
sprite.LayerSetState(Layers.ChargeState, "apco3-0");
break;
case ApcChargeState.Charging:
sprite.LayerSetState(Layers.ChargeState, "apco3-1");
break;
case ApcChargeState.Full:
sprite.LayerSetState(Layers.ChargeState, "apco3-2");
break;
}
}
else
{
sprite.LayerSetState(Layers.ChargeState, "apco3-0");
}
}
enum Layers
{
ChargeState,
Lock,
Equipment,
Lighting,
Environment,
}
}
}

View File

@@ -0,0 +1,48 @@
using Content.Shared.GameObjects.Components.Power;
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;
namespace Content.Client.GameObjects.Components.Power
{
public class PowerCellVisualizer2D : AppearanceVisualizer
{
private string _prefix;
public override void LoadData(YamlMappingNode node)
{
base.LoadData(node);
_prefix = node.GetNode("prefix").AsString();
}
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
var sprite = entity.GetComponent<ISpriteComponent>();
sprite.LayerMapSet(Layers.Charge, sprite.AddLayerState($"{_prefix}_100"));
sprite.LayerSetShader(Layers.Charge, "unshaded");
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (component.TryGetData(PowerCellVisuals.ChargeLevel, out float fraction))
{
sprite.LayerSetState(Layers.Charge, $"{_prefix}_{ContentHelpers.RoundToLevels(fraction, 1, 5) * 25}");
}
}
private enum Layers
{
Charge
}
}
}

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,35 @@
using Content.Shared.GameObjects.Components.Power;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
namespace Content.Client.GameObjects.Components.Power
{
[RegisterComponent]
public class PowerDebugTool : SharedPowerDebugTool
{
SS14Window LastWindow;
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case OpenDataWindowMsg msg:
if (LastWindow != null && !LastWindow.Disposed)
{
LastWindow.Dispose();
}
LastWindow = new SS14Window()
{
Title = "Power Debug Tool",
};
LastWindow.Contents.AddChild(new Label() { Text = msg.Data });
LastWindow.Open();
break;
}
}
}
}

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

@@ -0,0 +1,72 @@
using Content.Shared.GameObjects.Components.Power;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Client.GameObjects.Components.Power
{
public class SmesVisualizer2D : AppearanceVisualizer
{
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
var sprite = entity.GetComponent<ISpriteComponent>();
sprite.LayerMapSet(Layers.Input, sprite.AddLayerState("smes-oc0"));
sprite.LayerSetShader(Layers.Input, "unshaded");
sprite.LayerMapSet(Layers.Charge, sprite.AddLayerState("smes-og1"));
sprite.LayerSetShader(Layers.Charge, "unshaded");
sprite.LayerSetVisible(Layers.Charge, false);
sprite.LayerMapSet(Layers.Output, sprite.AddLayerState("smes-op0"));
sprite.LayerSetShader(Layers.Output, "unshaded");
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (!component.TryGetData<int>(SmesVisuals.LastChargeLevel, out var level) || level == 0)
{
sprite.LayerSetVisible(Layers.Charge, false);
}
else
{
sprite.LayerSetVisible(Layers.Charge, true);
sprite.LayerSetState(Layers.Charge, $"smes-og{level}");
}
if (component.TryGetData<ChargeState>(SmesVisuals.LastChargeState, out var state))
{
switch (state)
{
case ChargeState.Still:
sprite.LayerSetState(Layers.Input, "smes-oc0");
sprite.LayerSetState(Layers.Output, "smes-op1");
break;
case ChargeState.Charging:
sprite.LayerSetState(Layers.Input, "smes-oc1");
sprite.LayerSetState(Layers.Output, "smes-op1");
break;
case ChargeState.Discharging:
sprite.LayerSetState(Layers.Input, "smes-oc0");
sprite.LayerSetState(Layers.Output, "smes-op2");
break;
}
}
else
{
sprite.LayerSetState(Layers.Input, "smes-oc0");
sprite.LayerSetState(Layers.Output, "smes-op1");
}
}
enum Layers
{
Input,
Charge,
Output,
}
}
}

View File

@@ -0,0 +1,113 @@
using System.Collections.Generic;
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;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.Research
{
public class LatheBoundUserInterface : BoundUserInterface
{
#pragma warning disable CS0649
[Dependency]
private IPrototypeManager _prototypeManager;
#pragma warning restore
[ViewVariables]
private LatheMenu menu;
[ViewVariables]
private LatheQueueMenu queueMenu;
public MaterialStorageComponent Storage { get; private set; }
public SharedLatheComponent Lathe { get; private set; }
public SharedLatheDatabaseComponent Database { get; private set; }
[ViewVariables]
public Queue<LatheRecipePrototype> QueuedRecipes => _queuedRecipes;
private Queue<LatheRecipePrototype> _queuedRecipes = new Queue<LatheRecipePrototype>();
public LatheBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
{
SendMessage(new SharedLatheComponent.LatheSyncRequestMessage());
}
protected override void Open()
{
base.Open();
if (!Owner.Owner.TryGetComponent(out MaterialStorageComponent storage)
|| !Owner.Owner.TryGetComponent(out SharedLatheComponent lathe)
|| !Owner.Owner.TryGetComponent(out SharedLatheDatabaseComponent database)) return;
Storage = storage;
Lathe = lathe;
Database = database;
menu = new LatheMenu(this);
queueMenu = new LatheQueueMenu { Owner = this };
menu.OnClose += Close;
menu.Populate();
menu.PopulateMaterials();
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;
menu.OpenCentered();
}
public void Queue(LatheRecipePrototype recipe, int quantity = 1)
{
SendMessage(new SharedLatheComponent.LatheQueueRecipeMessage(recipe.ID, quantity));
}
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
{
switch (message)
{
case SharedLatheComponent.LatheProducingRecipeMessage msg:
if (!_prototypeManager.TryIndex(msg.ID, out LatheRecipePrototype recipe)) break;
queueMenu?.SetInfo(recipe);
break;
case SharedLatheComponent.LatheStoppedProducingRecipeMessage _:
queueMenu?.ClearInfo();
break;
case SharedLatheComponent.LatheFullQueueMessage msg:
_queuedRecipes.Clear();
foreach (var id in msg.Recipes)
{
if (!_prototypeManager.TryIndex(id, out LatheRecipePrototype recipePrototype)) break;
_queuedRecipes.Enqueue(recipePrototype);
}
queueMenu?.PopulateList();
break;
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
menu?.Dispose();
queueMenu?.Dispose();
}
}
}

View File

@@ -0,0 +1,30 @@
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 LatheDatabaseComponent : SharedLatheDatabaseComponent
{
#pragma warning disable CS0649
[Dependency]
private IPrototypeManager _prototypeManager;
#pragma warning restore
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if (!(curState is LatheDatabaseState state)) return;
Clear();
foreach (var ID in state.Recipes)
{
if(!_prototypeManager.TryIndex(ID, out LatheRecipePrototype recipe)) continue;
AddRecipe(recipe);
}
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Research;
using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Research
{
[RegisterComponent]
[ComponentReference(typeof(SharedMaterialStorageComponent))]
public class MaterialStorageComponent : SharedMaterialStorageComponent
{
protected override Dictionary<string, int> Storage { get; set; } = new Dictionary<string, int>();
public event Action OnMaterialStorageChanged;
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
base.HandleComponentState(curState, nextState);
if (!(curState is MaterialStorageState state)) return;
Storage = state.Storage;
OnMaterialStorageChanged?.Invoke();
}
}
}

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

@@ -0,0 +1,109 @@
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;
namespace Content.Client.GameObjects.Components.Sound
{
[RegisterComponent]
public class SoundComponent : SharedSoundComponent
{
private readonly List<ScheduledSound> _schedules = new List<ScheduledSound>();
private AudioSystem _audioSystem;
#pragma warning disable 649
[Dependency] private readonly IRobustRandom _random;
#pragma warning restore 649
public override void StopAllSounds()
{
foreach (var schedule in _schedules)
{
schedule.Play = false;
}
_schedules.Clear();
}
public override void StopScheduledSound(string filename)
{
foreach (var schedule in _schedules.ToArray())
{
if (schedule.Filename != filename) continue;
schedule.Play = false;
_schedules.Remove(schedule);
}
}
public override void AddScheduledSound(ScheduledSound schedule)
{
_schedules.Add(schedule);
Play(schedule);
}
public void Play(ScheduledSound schedule)
{
if (!schedule.Play) return;
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>();
_audioSystem.Play(schedule.Filename, Owner, schedule.AudioParams);
if (schedule.Times == 0)
{
_schedules.Remove(schedule);
return;
}
if (schedule.Times > 0)
schedule.Times--;
Play(schedule);
});
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case ScheduledSoundMessage msg:
AddScheduledSound(msg.Schedule);
break;
case StopSoundScheduleMessage msg:
StopScheduledSound(msg.Filename);
break;
case StopAllSoundsMessage _:
StopAllSounds();
break;
}
}
public override void Initialize()
{
base.Initialize();
IoCManager.Resolve<IEntitySystemManager>().TryGetEntitySystem(out _audioSystem);
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
if (serializer.Writing) return;
serializer.TryReadDataField("schedules", out List<ScheduledSound> schedules);
if (schedules == null) return;
foreach (var schedule in schedules)
{
if (schedule == null) continue;
AddScheduledSound(schedule);
}
}
}
}

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

@@ -0,0 +1,258 @@
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Storage;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Storage
{
/// <summary>
/// Client version of item storage containers, contains a UI which displays stored entities and their size
/// </summary>
[RegisterComponent]
public class ClientStorageComponent : SharedStorageComponent
{
private Dictionary<EntityUid, int> StoredEntities { get; set; } = new Dictionary<EntityUid, int>();
private int StorageSizeUsed;
private int StorageCapacityMax;
private StorageWindow Window;
public override void OnAdd()
{
base.OnAdd();
Window = new StorageWindow()
{StorageEntity = this};
}
public override void OnRemove()
{
Window.Dispose();
base.OnRemove();
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
IComponent component = null)
{
switch (message)
{
//Updates what we are storing for the UI
case StorageHeldItemsMessage msg:
HandleStorageMessage(msg);
break;
//Opens the UI
case OpenStorageUIMessage _:
OpenUI();
break;
case CloseStorageUIMessage _:
CloseUI();
break;
}
}
/// <summary>
/// Copies received values from server about contents of storage container
/// </summary>
/// <param name="storagestate"></param>
private void HandleStorageMessage(StorageHeldItemsMessage storagestate)
{
StoredEntities = new Dictionary<EntityUid, int>(storagestate.StoredEntities);
StorageSizeUsed = storagestate.StorageSizeUsed;
StorageCapacityMax = storagestate.StorageSizeMax;
Window.BuildEntityList();
}
/// <summary>
/// Opens the storage UI
/// </summary>
private void OpenUI()
{
Window.Open();
}
private void CloseUI()
{
Window.Close();
}
/// <summary>
/// Function for clicking one of the stored entity buttons in the UI, tells server to remove that entity
/// </summary>
/// <param name="entityuid"></param>
private void Interact(EntityUid entityuid)
{
SendNetworkMessage(new RemoveEntityMessage(entityuid));
}
/// <summary>
/// GUI class for client storage component
/// </summary>
private class StorageWindow : SS14Window
{
private Control VSplitContainer;
private VBoxContainer EntityList;
private Label Information;
public ClientStorageComponent StorageEntity;
protected override Vector2? CustomSize => (180, 320);
public StorageWindow()
{
Title = "Storage Item";
RectClipContent = true;
VSplitContainer = new VBoxContainer();
Information = new Label
{
Text = "Items: 0 Volume: 0/0 Stuff",
SizeFlagsVertical = SizeFlags.ShrinkCenter
};
VSplitContainer.AddChild(Information);
var listScrollContainer = new ScrollContainer
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.FillExpand,
HScrollEnabled = true,
VScrollEnabled = true
};
EntityList = new VBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand
};
listScrollContainer.AddChild(EntityList);
VSplitContainer.AddChild(listScrollContainer);
Contents.AddChild(VSplitContainer);
}
public override void Close()
{
StorageEntity.SendNetworkMessage(new CloseStorageUIMessage());
base.Close();
}
/// <summary>
/// Loops through stored entities creating buttons for each, updates information labels
/// </summary>
public void BuildEntityList()
{
EntityList.DisposeAllChildren();
var storagelist = StorageEntity.StoredEntities;
foreach (var entityuid in storagelist)
{
var entity = IoCManager.Resolve<IEntityManager>().GetEntity(entityuid.Key);
var button = new EntityButton()
{
EntityuID = entityuid.Key
};
button.ActualButton.OnToggled += OnItemButtonToggled;
//Name and Size labels set
button.EntityName.Text = entity.Name;
button.EntitySize.Text = string.Format("{0}", entityuid.Value);
//Gets entity sprite and assigns it to button texture
if (entity.TryGetComponent(out ISpriteComponent sprite))
{
button.EntitySpriteView.Sprite = sprite;
}
EntityList.AddChild(button);
}
//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);
}
else
{
Information.Text = String.Format("Items: {0}", storagelist.Count);
}
}
/// <summary>
/// Function assigned to button toggle which removes the entity from storage
/// </summary>
/// <param name="args"></param>
private void OnItemButtonToggled(BaseButton.ButtonToggledEventArgs args)
{
var control = (EntityButton) args.Button.Parent;
args.Button.Pressed = false;
StorageEntity.Interact(control.EntityuID);
}
}
/// <summary>
/// Button created for each entity that represents that item in the storage UI, with a texture, and name and size label
/// </summary>
private class EntityButton : PanelContainer
{
public EntityUid EntityuID { get; set; }
public Button ActualButton { get; }
public SpriteView EntitySpriteView { get; }
public Control EntityControl { get; }
public Label EntityName { get; }
public Label EntitySize { get; }
public EntityButton()
{
ActualButton = new Button
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsVertical = SizeFlags.FillExpand,
ToggleMode = true,
MouseFilter = MouseFilterMode.Stop
};
AddChild(ActualButton);
var hBoxContainer = new HBoxContainer {MouseFilter = MouseFilterMode.Ignore};
EntitySpriteView = new SpriteView
{
CustomMinimumSize = new Vector2(32.0f, 32.0f), MouseFilter = MouseFilterMode.Ignore
};
EntityName = new Label
{
SizeFlagsVertical = SizeFlags.ShrinkCenter,
Text = "Backpack",
MouseFilter = MouseFilterMode.Ignore
};
hBoxContainer.AddChild(EntitySpriteView);
hBoxContainer.AddChild(EntityName);
EntityControl = new Control
{
SizeFlagsHorizontal = SizeFlags.FillExpand, MouseFilter = MouseFilterMode.Ignore
};
EntitySize = new Label
{
SizeFlagsVertical = SizeFlags.ShrinkCenter,
Text = "Size 6",
Align = Label.AlignMode.Right,
/*AnchorLeft = 1.0f,
AnchorRight = 1.0f,
AnchorBottom = 0.5f,
AnchorTop = 0.5f,
MarginLeft = -38.0f,
MarginTop = -7.0f,
MarginRight = -5.0f,
MarginBottom = 7.0f*/
};
EntityControl.AddChild(EntitySize);
hBoxContainer.AddChild(EntityControl);
AddChild(hBoxContainer);
}
}
}
}

View File

@@ -0,0 +1,47 @@
using Content.Shared.GameObjects.Components.Storage;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Content.Client.GameObjects.Components.Storage
{
public sealed class StorageVisualizer2D : AppearanceVisualizer
{
private string _stateOpen;
private string _stateClosed;
public override void LoadData(YamlMappingNode node)
{
base.LoadData(node);
if (node.TryGetNode("state_open", out var child))
{
_stateOpen = child.AsString();
}
if (node.TryGetNode("state_closed", out child))
{
_stateClosed = child.AsString();
}
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
if (!component.Owner.TryGetComponent(out ISpriteComponent sprite))
{
return;
}
component.TryGetData(StorageVisuals.Open, out bool open);
sprite.LayerSetState(StorageVisualLayers.Door, open ? _stateOpen : _stateClosed);
}
}
public enum StorageVisualLayers
{
Door
}
}

View File

@@ -0,0 +1,57 @@
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).
/// </summary>
/// <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();
_snapGridComponent = Owner.GetComponent<SnapGridComponent>();
}
/// <inheritdoc />
protected override void Startup()
{
base.Startup();
_snapGridComponent.OnPositionChanged += SnapGridOnPositionChanged;
Owner.EntityManager.EventBus.RaiseEvent(Owner, new SubFloorHideDirtyEvent());
}
/// <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.EventBus.RaiseEvent(Owner, new SubFloorHideDirtyEvent());
}
}
internal sealed class SubFloorHideDirtyEvent : EntitySystemMessage { }
}

View File

@@ -0,0 +1,76 @@
using System;
using Content.Shared.GameObjects.Components.Triggers;
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 Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Content.Client.GameObjects.Components.Doors
{
public class TimerTriggerVisualizer2D : AppearanceVisualizer
{
private const string AnimationKey = "priming_animation";
private Animation PrimingAnimation;
public override void LoadData(YamlMappingNode node)
{
base.LoadData(node);
var countdownSound = node.GetNode("countdown_sound").AsString();
PrimingAnimation = new Animation { Length = TimeSpan.MaxValue };
{
var flick = new AnimationTrackSpriteFlick();
PrimingAnimation.AnimationTracks.Add(flick);
flick.LayerKey = TriggerVisualLayers.Base;
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("primed", 0f));
var sound = new AnimationTrackPlaySound();
PrimingAnimation.AnimationTracks.Add(sound);
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(countdownSound, 0));
}
}
public override void InitializeEntity(IEntity 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(TriggerVisuals.VisualState, out TriggerVisualState state))
{
state = TriggerVisualState.Unprimed;
}
switch (state)
{
case TriggerVisualState.Primed:
if (!animPlayer.HasRunningAnimation(AnimationKey))
{
animPlayer.Play(PrimingAnimation, AnimationKey);
}
break;
case TriggerVisualState.Unprimed:
sprite.LayerSetState(0, "icon");
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
public enum TriggerVisualLayers
{
Base
}
}

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