502 Commits

Author SHA1 Message Date
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
Pieter-Jan Briers
930cb61af8 Flashlight improvements:
1. Sound effects
2. fixed sprite having a hole
2019-07-27 11:56:36 +02: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
1190 changed files with 33457 additions and 6645 deletions

24
.appveyor.yml Normal file
View File

@@ -0,0 +1,24 @@
version: 1.0.{build}
image: Visual Studio 2017
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
- cmd: nuget restore SpaceStation14.sln
build_script:
- ps: msbuild SpaceStation14.sln /v:m /nologo /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /p:Platform=x64 /p:Configuration=Debug /p:AppVeyor=yes
test:
assemblies:
only:
- bin/Content.IntegrationTests/Content.IntegrationTests.dll
- bin/Content.Tests/Content.Tests.dll

2
.gitignore vendored
View File

@@ -268,6 +268,8 @@ 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

6
.gitmodules vendored
View File

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

View File

@@ -9,29 +9,30 @@ os:
addons:
apt:
sources:
- deadsnakes
#sources:
#- deadsnakes
packages:
- python3.6
- python3.5
- python3-pip
cache:
directories:
- packages/
- engine/Dependencies/
- engine/SS14.Client.Godot/.mono/assemblies/
- RobustToolbox/Dependencies/
- RobustToolbox/SS14.Client.Godot/.mono/assemblies/
#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"
- "pip3 install --user requests"
- "nuget restore SpaceStation14Content.sln"
- "python3.6 RUN_THIS.py --no-prompt"
- "engine/Tools/download_godotsharp.py"
#- "if [ $TRAVIS_OS_NAME = linux ]; then pyenv shell 3.6; fi"
- "python3.5 -m pip install --user requests"
- "nuget restore SpaceStation14.sln"
- "python3.5 RUN_THIS.py --no-prompt"
script:
- "msbuild /p:Configuration=Debug /p:Platform=x64 /p:HEADLESS=1 /p:Configuratio=Debug /nologo /m /p:AllowMissingMacNatives=yes SpaceStation14Content.sln /p:Python=python3.6"
- "msbuild /p:Configuration=Debug /p:Platform=x64 /nologo /m SpaceStation14.sln /p:Python=python3.5"
- "mono packages/nunit.consolerunner/3.10.0/tools/nunit3-console.exe bin/Content.Tests/Content.Tests.dll bin/Content.IntegrationTests/Content.IntegrationTests.dll"

View File

@@ -17,7 +17,7 @@ https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
<Python>python3</Python>
<Python Condition="'$(OS)'=='Windows_NT' Or '$(OS)'=='Windows'">py -3</Python>
<ProjectGuid>{C899FCA4-7037-4E49-ABC2-44DE72487110}</ProjectGuid>
<TargetFrameworkMoniker>.NETFramework, Version=v4.5.1</TargetFrameworkMoniker>
<TargetFrameworkMoniker>.NETFramework, Version=v4.7.1</TargetFrameworkMoniker>
</PropertyGroup>
<PropertyGroup>
<OutputType>Library</OutputType>
@@ -25,6 +25,9 @@ https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>bin\Debug\</OutputPath>
</PropertyGroup>
<Target Name="Build">
<Exec Command="$(Python) git_helper.py" CustomErrorRegularExpression="^Error" />
</Target>

View File

@@ -8,7 +8,7 @@ import shutil
from pathlib import Path
from typing import List
SOLUTION_PATH = Path("..") / "SpaceStation14Content.sln"
SOLUTION_PATH = Path("..") / "SpaceStation14.sln"
# 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"

5
BuildFiles/Linux/SS14.Launcher Executable file
View File

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

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>SS14L</string>
<key>CFBundleDisplayName</key>
<string>Space Station 14 Launcher</string>
<key>CFBundleExecutable</key>
<string>SS14</string>
<!--
Just a note about this icon.
MacOS seems REALLY iffy about this and even when the file is correct,
it can take forever before it decides to actually update it and display it.
TL;DR Apple is stupid.
-->
<key>CFBundleIconFile</key>
<string>ss14</string>
</dict>
</plist>

View File

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

View File

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

View File

@@ -1 +0,0 @@
call Godot\godot.windows.tools.64.mono.exe --path SS14.Client.Godot

View File

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

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 Type[] {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,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<OutputPath>..\bin\Content.Benchmarks\</OutputPath>
<IsPackable>false</IsPackable>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Platforms>x86;x64</Platforms>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.11.5" />
</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,13 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
namespace Content.Benchmarks
{
internal class Program
{
public static void Main(string[] args)
{
BenchmarkRunner.Run<ComponentManagerGetAllComponents>();
}
}
}

View File

@@ -0,0 +1,232 @@
using System.Collections.Generic;
using Content.Shared.Chat;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Input;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using Robust.Shared.Localization;
using Robust.Shared.IoC;
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 ILocalizationManager localize = IoCManager.Resolve<ILocalizationManager>();
public LineEdit Input { get; private set; }
public OutputPanel contents;
// Buttons for filtering
public Button AllButton;
public Button LocalButton;
public Button OOCButton;
/// <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;
protected override void Initialize()
{
base.Initialize();
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.OnKeyDown += InputKeyDown;
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 MouseDown(GUIMouseButtonEventArgs e)
{
base.MouseDown(e);
Input.GrabKeyboardFocus();
}
private void InputKeyDown(GUIKeyEventArgs e)
{
if (e.Key == Keyboard.Key.Escape)
{
Input.ReleaseKeyboardFocus();
e.Handle();
return;
}
if (e.Key == Keyboard.Key.Up)
{
if (_inputIndex == -1 && _inputHistory.Count != 0)
{
_inputTemp = Input.Text;
_inputIndex++;
}
else if (_inputIndex + 1 < _inputHistory.Count)
{
_inputIndex++;
}
if (_inputIndex != -1)
{
Input.Text = _inputHistory[_inputIndex];
}
e.Handle();
return;
}
if (e.Key == Keyboard.Key.Down)
{
if (_inputIndex == 0)
{
Input.Text = _inputTemp;
_inputTemp = "";
_inputIndex--;
}
else if (_inputIndex != -1)
{
_inputIndex--;
Input.Text = _inputHistory[_inputIndex];
}
e.Handle();
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
TextSubmitted = null;
Input = null;
contents = null;
}
}
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,408 @@
using System.Collections.Generic;
using Content.Client.Interfaces.Chat;
using Content.Shared.Chat;
using Robust.Client;
using Robust.Client.Console;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.UserInterface;
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.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 Control();
_speechBubbleRoot.SetAnchorPreset(Control.LayoutPreset.Wide);
_userInterfaceManager.StateRoot.AddChild(_speechBubbleRoot);
_speechBubbleRoot.SetPositionFirst();
}
public void FrameUpdate(RenderFrameEventArgs 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.Elapsed;
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,137 @@
using Content.Client.Interfaces.Chat;
using Robust.Client;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Timers;
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 Control _panel;
private float _timeLeft = TotalTime;
public float VerticalOffset { get; set; }
private float _verticalOffsetAchieved;
public float ContentHeight { get; }
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);
_panel = new PanelContainer
{
StyleClasses = { "tooltipBox" },
Children = { label },
MouseFilter = MouseFilterMode.Ignore,
ModulateSelfOverride = Color.White.WithAlpha(0.75f)
};
AddChild(_panel);
_panel.Size = _panel.CombinedMinimumSize;
ContentHeight = _panel.Height;
Size = (_panel.Width, 0);
_verticalOffsetAchieved = -ContentHeight;
}
protected override void FrameUpdate(RenderFrameEventArgs args)
{
base.FrameUpdate(args);
_timeLeft -= args.Elapsed;
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.Elapsed);
}
var worldPos = _senderEntity.Transform.WorldPosition;
worldPos += (0, EntityVerticalOffset);
var lowerCenter = _eyeManager.WorldToScreen(worldPos) / UIScale;
var screenPos = lowerCenter - (Width / 2, ContentHeight + _verticalOffsetAchieved);
Position = screenPos;
var height = (lowerCenter.Y - screenPos.Y).Clamp(0, ContentHeight);
Size = (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,10 @@
using System;
using Content.Shared;
namespace Content.Client
{
public sealed class ClientModuleTestingCallbacks : SharedModuleTestingCallbacks
{
public Action ClientBeforeIoC { get; set; }
}
}

View File

@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using Content.Client.Interfaces;
using Content.Shared;
using Robust.Client;
using Robust.Client.Interfaces.Console;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Input;
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.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;
label.InitialPos = label.Position = coordinates.Position - minimumSize / 2;
_userInterfaceManager.StateRoot.AddChild(label);
_aliveLabels.Add(label);
}
public void PopupMessage(string message)
{
PopupMessage(new ScreenCoordinates(_inputManager.MouseScreenPosition), message);
}
public void FrameUpdate(RenderFrameEventArgs eventArgs)
{
foreach (var label in _aliveLabels)
{
label.Update(eventArgs);
}
_aliveLabels.RemoveAll(l => l.Disposed);
}
private class PopupLabel : Label
{
private float _timeLeft;
public Vector2 InitialPos { get; set; }
protected override void Initialize()
{
base.Initialize();
ShadowOffsetXOverride = 1;
ShadowOffsetYOverride = 1;
FontColorShadowOverride = Color.Black;
}
public void Update(RenderFrameEventArgs eventArgs)
{
_timeLeft += eventArgs.Elapsed;
Position = InitialPos - new Vector2(0, 20 * (_timeLeft * _timeLeft + _timeLeft));
if (_timeLeft > 0.5f)
{
Modulate = Color.White.WithAlpha(1f - 0.2f * (float)Math.Pow(_timeLeft - 0.5f, 3f));
if (_timeLeft > 3f)
{
Dispose();
}
}
}
}
}
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,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,391 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Client.GameObjects.Components.Construction;
using Content.Shared.Construction;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Interfaces.GameObjects;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Placement;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Placement;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.Utility;
using Robust.Shared.Enums;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Construction
{
public class ConstructionMenu : SS14Window
{
#pragma warning disable CS0649
[Dependency]
readonly IPrototypeManager PrototypeManager;
[Dependency]
readonly IResourceCache ResourceCache;
#pragma warning restore
public ConstructorComponent Owner { get; set; }
Button BuildButton;
Button EraseButton;
LineEdit SearchBar;
Tree RecipeList;
TextureRect InfoIcon;
Label InfoLabel;
ItemList StepList;
CategoryNode RootCategory;
// This list is flattened in such a way that the top most deepest category is first.
List<CategoryNode> FlattenedCategories;
PlacementManager Placement;
public ConstructionMenu()
{
Size = new Vector2(500.0f, 350.0f);
}
protected override void Initialize()
{
base.Initialize();
IoCManager.InjectDependencies(this);
Placement = (PlacementManager)IoCManager.Resolve<IPlacementManager>();
Placement.PlacementCanceled += OnPlacementCanceled;
Title = "Construction";
var hSplitContainer = new HSplitContainer();
// Left side
var recipes = new VBoxContainer("Recipes") {CustomMinimumSize = new Vector2(150.0f, 0.0f)};
SearchBar = new LineEdit("Search") {PlaceHolder = "Search"};
RecipeList = new Tree("Tree") {SizeFlagsVertical = SizeFlags.FillExpand, HideRoot = true};
recipes.AddChild(SearchBar);
recipes.AddChild(RecipeList);
hSplitContainer.AddChild(recipes);
// Right side
var guide = new VBoxContainer("Guide");
var info = new HBoxContainer("Info");
InfoIcon = new TextureRect("TextureRect");
InfoLabel = new Label("Label")
{
SizeFlagsHorizontal = SizeFlags.FillExpand, SizeFlagsVertical = SizeFlags.ShrinkCenter
};
info.AddChild(InfoIcon);
info.AddChild(InfoLabel);
guide.AddChild(info);
var stepsLabel = new Label("Label")
{
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
SizeFlagsVertical = SizeFlags.ShrinkCenter,
Text = "Steps"
};
guide.AddChild(stepsLabel);
StepList = new ItemList("StepsList")
{
SizeFlagsVertical = SizeFlags.FillExpand, SelectMode = ItemList.ItemListSelectMode.None
};
guide.AddChild(StepList);
var buttonsContainer = new HBoxContainer("Buttons");
BuildButton = new Button("BuildButton")
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
TextAlign = Button.AlignMode.Center,
Text = "Build!",
Disabled = true,
ToggleMode = false
};
EraseButton = new Button("EraseButton")
{
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/sheet_metal.png");
text = $"Metal x{mat.Amount}";
break;
case ConstructionStepMaterial.MaterialType.Glass:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/sheet_glass.png");
text = $"Glass x{mat.Amount}";
break;
case ConstructionStepMaterial.MaterialType.Cable:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/cable_coil.png");
text = $"Cable Coil x{mat.Amount}";
break;
default:
throw new NotImplementedException();
}
break;
case ConstructionStepTool tool:
switch (tool.Tool)
{
case ConstructionStepTool.ToolType.Wrench:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/wrench.png");
text = "Wrench";
break;
case ConstructionStepTool.ToolType.Crowbar:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/crowbar.png");
text = "Crowbar";
break;
case ConstructionStepTool.ToolType.Screwdriver:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/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/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(Button.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) != -1)
{
found = true;
}
else
{
foreach (var keyw in prototype.Keywords.Concat(prototype.CategorySegments))
{
// TODO: don't run ToLowerInvariant() constantly.
if (keyw.ToLowerInvariant().IndexOf(searchTerm) != -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 x.Name.CompareTo(y.Name);
}
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,53 @@
using Content.Client.GameObjects.Components.Construction;
using Content.Shared.Construction;
using Robust.Client.Graphics;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Placement;
using Robust.Client.ResourceManagement;
using Robust.Client.Utility;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Content.Client.Construction
{
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);
var res = IoCManager.Resolve<IResourceCache>();
manager.CurrentBaseSprite = Prototype.Icon.DirFrame0();
}
}
}

View File

@@ -1,112 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x64</Platform>
<ProjectGuid>{A2E5F175-78AF-4DDD-8F97-E2D2552372ED}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Content.Client</RootNamespace>
<AssemblyName>Content.Client</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ContentAssemblyTarget>..\engine\bin\Client\Resources\Assemblies\</ContentAssemblyTarget>
<!--
This copies all dependencies,
but on the plus side it's automatically located in the right place.
-->
<TargetFramework>net472</TargetFramework>
<LangVersion>7.3</LangVersion>
<IsPackable>false</IsPackable>
<Platforms>x64;x86</Platforms>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<OutputPath>..\bin\Content.Client\</OutputPath>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet Condition="'$(ActualOS)' == 'Windows'">MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>portable</DebugType>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<DefineConstants>TRACE;RELEASE</DefineConstants>
<Optimize>true</Optimize>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>portable</DebugType>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<DefineConstants>TRACE;RELEASE</DefineConstants>
<Optimize>true</Optimize>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DefineConstants>TRACE;RELEASE</DefineConstants>
<Optimize>true</Optimize>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>portable</DebugType>
</PropertyGroup>
<Import Project="..\RobustToolbox\MSBuild\Robust.DefineConstants.targets" />
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)packages\OpenTK.3.0.0-pre\lib\net20\OpenTK.dll</HintPath>
</Reference>
<Reference Include="YamlDotNet, Version=4.3.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)packages\YamlDotNet.4.3.1\lib\net45\YamlDotNet.dll</HintPath>
</Reference>
<PackageReference Include="Nett" Version="0.11.0" />
<PackageReference Include="SixLabors.Core" Version="1.0.0-beta0007" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-beta0006" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="YamlDotNet" Version="6.0.0" />
<PackageReference Include="SharpZipLib" Version="1.1.0" />
</ItemGroup>
<ItemGroup>
<Compile Include="EntryPoint.cs" />
<Compile Include="GameObjects\Components\Inventory\ClientInventoryComponent.cs" />
<Compile Include="GameObjects\Components\Storage\ClientStorageComponent.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="GameObjects\Components\Items\ClientHandsComponent.cs" />
<Compile Include="Interfaces\GameObjects\Components\Items\IHandsComponent.cs" />
<Compile Include="UserInterface\HandsGui.cs" />
<Compile Include="GameObjects\Components\Power\PowerDebugTool.cs" />
</ItemGroup>
<ItemGroup>
<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\SS14.Client.csproj">
<Project>{83429BD6-6358-4B18-BE51-401DF8EA2673}</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>
<None Include="app.config" />
<None Include="packages.config" />
<ProjectReference Include="..\RobustToolbox\Lidgren.Network\Lidgren.Network.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,50 +1,194 @@
using Content.Client.GameObjects;
using Content.Client.GameObjects.Components.Power;
using Content.Client.GameObjects.Components.Storage;
using Content.Client.Interfaces.GameObjects;
using SS14.Shared.ContentPack;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.IoC;
using System;
using Content.Client.Chat;
using Content.Client.GameObjects.Components.Actor;
using Content.Client.GameTicking;
using Content.Client.Input;
using Content.Client.Interfaces;
using Content.Client.Interfaces.Chat;
using Content.Client.Interfaces.Parallax;
using Content.Client.Parallax;
using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components.Chemistry;
using Content.Shared.GameObjects.Components.Markers;
using Content.Shared.GameObjects.Components.Research;
using Content.Shared.Interfaces;
using Robust.Client;
using Robust.Client.Interfaces;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Shared.ContentPack;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
namespace Content.Client
{
public class EntryPoint : GameClient
{
#pragma warning disable 649
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IEscapeMenuOwner _escapeMenuOwner;
#pragma warning restore 649
public override void Init()
{
var factory = IoCManager.Resolve<IComponentFactory>();
var prototypes = IoCManager.Resolve<IPrototypeManager>();
factory.RegisterIgnore("Item");
factory.RegisterIgnore("Interactable");
factory.RegisterIgnore("Damageable");
factory.RegisterIgnore("Destructible");
factory.RegisterIgnore("Temperature");
factory.RegisterIgnore("PowerTransfer");
factory.RegisterIgnore("PowerNode");
factory.RegisterIgnore("PowerProvider");
factory.RegisterIgnore("PowerDevice");
factory.RegisterIgnore("PowerStorage");
factory.RegisterIgnore("PowerGenerator");
factory.DoAutoRegistrations();
factory.RegisterIgnore("Wirecutter");
factory.RegisterIgnore("Screwdriver");
factory.RegisterIgnore("Multitool");
factory.RegisterIgnore("Welder");
factory.RegisterIgnore("Wrench");
factory.RegisterIgnore("Crowbar");
factory.RegisterIgnore("HitscanWeapon");
factory.RegisterIgnore("ProjectileWeapon");
factory.RegisterIgnore("Projectile");
factory.RegisterIgnore("MeleeWeapon");
var registerIgnore = new[]
{
"Interactable",
"Destructible",
"Temperature",
"PowerTransfer",
"PowerNode",
"PowerProvider",
"PowerDevice",
"PowerStorage",
"PowerGenerator",
"Explosive",
"OnUseTimerTrigger",
"ToolboxElectricalFill",
"ToolLockerFill",
"EmitSoundOnUse",
"FootstepModifier",
"HeatResistance",
"CombatMode",
"Teleportable",
"ItemTeleporter",
"Portal",
"EntityStorage",
"PlaceableSurface",
"Wirecutter",
"Screwdriver",
"Multitool",
"Welder",
"Wrench",
"Crowbar",
"HitscanWeapon",
"ProjectileWeapon",
"Projectile",
"MeleeWeapon",
"Storeable",
"Stack",
"Dice",
"Construction",
"Apc",
"Door",
"PoweredLight",
"Smes",
"Powercell",
"HandheldLight",
"LightBulb",
"Healing",
"Catwalk",
"BallisticMagazine",
"BallisticMagazineWeapon",
"BallisticBullet",
"HitscanWeaponCapacitor",
"PowerCell",
"AiController",
"PlayerInputMover",
};
factory.RegisterIgnore("Storeable");
factory.RegisterIgnore("Clothing");
foreach (var ignoreName in registerIgnore)
{
factory.RegisterIgnore(ignoreName);
}
factory.Register<HandsComponent>();
factory.RegisterReference<HandsComponent, IHandsComponent>();
factory.Register<ClientStorageComponent>();
factory.Register<ClientInventoryComponent>();
factory.Register<PowerDebugTool>();
factory.Register<SharedLatheComponent>();
factory.Register<SharedSpawnPointComponent>();
factory.Register<SolutionComponent>();
prototypes.RegisterIgnore("material");
IoCManager.Register<IGameHud, GameHud>();
IoCManager.Register<IClientNotifyManager, ClientNotifyManager>();
IoCManager.Register<ISharedNotifyManager, ClientNotifyManager>();
IoCManager.Register<IClientGameTicker, ClientGameTicker>();
IoCManager.Register<IParallaxManager, ParallaxManager>();
IoCManager.Register<IChatManager, ChatManager>();
IoCManager.Register<IEscapeMenuOwner, EscapeMenuOwner>();
if (TestingCallbacks != null)
{
var cast = (ClientModuleTestingCallbacks) TestingCallbacks;
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();
}
/// <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();
}
public override void Update(ModUpdateLevel level, float frameTime)
{
base.Update(level, frameTime);
switch (level)
{
case ModUpdateLevel.FramePreEngine:
var renderFrameEventArgs = new RenderFrameEventArgs(frameTime);
IoCManager.Resolve<IClientNotifyManager>().FrameUpdate(renderFrameEventArgs);
IoCManager.Resolve<IClientGameTicker>().FrameUpdate(renderFrameEventArgs);
IoCManager.Resolve<IChatManager>().FrameUpdate(renderFrameEventArgs);
break;
}
}
}
}

View File

@@ -0,0 +1,104 @@
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.Players;
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;
var escapeMenuCommand = InputCmdHandler.FromDelegate(Enabled);
_inputManager.SetInputCommand(EngineKeyFunctions.EscapeMenu, escapeMenuCommand);
}
else if (obj.OldState is GameScreen)
{
// Switched FROM GameScreen.
_escapeMenu.Dispose();
_escapeMenu = null;
_inputManager.SetInputCommand(EngineKeyFunctions.EscapeMenu, null);
}
}
private void Enabled(ICommonSession session)
{
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,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,143 @@
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; }
/// <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
var 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();
Window?.Dispose();
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);
}
Size = CombinedMinimumSize;
}
}
}
/// <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,50 @@
using System;
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.ViewVariables;
namespace Content.Client.GameObjects.Components.Clothing
{
[RegisterComponent]
[ComponentReference(typeof(ItemComponent))]
public class ClothingComponent : ItemComponent
{
public override string Name => "Clothing";
public override uint? NetID => ContentNetIDs.CLOTHING;
public override Type StateType => typeof(ClothingComponentState);
[ViewVariables(VVAccess.ReadWrite)]
public string ClothingEquippedPrefix { get; set; }
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;
}
}
}

View File

@@ -0,0 +1,31 @@
using Content.Shared.Construction;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects.Components.Construction
{
[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; }
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case ClientEntityClickMsg clickMsg:
Master.TryStartConstruction(GhostID);
break;
}
}
}
}

View File

@@ -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.SpawnEntityAt("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,30 @@
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)
{
var damagestate = (DamageComponentState)curState;
CurrentDamage = damagestate.CurrentDamage;
}
}
}
}

View File

@@ -0,0 +1,116 @@
using System;
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;
public override void LoadData(YamlMappingNode node)
{
base.LoadData(node);
var openSound = node.GetNode("open_sound").AsString();
var closeSound = node.GetNode("close_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 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 sound = new AnimationTrackPlaySound();
OpenAnimation.AnimationTracks.Add(sound);
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(openSound, 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;
}
switch (state)
{
case DoorVisualState.Closed:
sprite.LayerSetState(DoorVisualLayers.Base, "closed");
sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "closed_unlit");
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, true);
break;
case DoorVisualState.Closing:
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, true);
if (!animPlayer.HasRunningAnimation(AnimationKey))
{
animPlayer.Play(CloseAnimation, AnimationKey);
}
break;
case DoorVisualState.Opening:
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, true);
if (!animPlayer.HasRunningAnimation(AnimationKey))
{
animPlayer.Play(OpenAnimation, AnimationKey);
}
break;
case DoorVisualState.Open:
sprite.LayerSetState(DoorVisualLayers.Base, "open");
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, false);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
public enum DoorVisualLayers
{
Base,
BaseUnlit
}
}

View File

@@ -0,0 +1,159 @@
using System.Collections.Generic;
using System.Linq;
using Content.Client.GameObjects.Components.Clothing;
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.Utility;
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;
public override void OnRemove()
{
base.OnRemove();
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)
{
if (_slots.ContainsKey(slot))
{
_slots.Remove(slot);
_clearSlot(slot);
}
var entity = Owner.EntityManager.GetEntity(entityUid);
_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)
{
if (_sprite != null && entity.TryGetComponent(out ClothingComponent clothing))
{
var flag = SlotMasks[slot];
var data = clothing.GetEquippedStateInfo(flag);
if (data == null)
{
_sprite.LayerSetVisible(slot, false);
}
else
{
var (rsi, state) = data.Value;
_sprite.LayerSetVisible(slot, true);
_sprite.LayerSetRSI(slot, rsi);
_sprite.LayerSetState(slot, state);
}
}
InterfaceController?.AddToSlot(slot, entity);
}
private void _clearSlot(Slots slot)
{
InterfaceController?.RemoveFromSlot(slot);
_sprite?.LayerSetVisible(slot, false);
}
public void SendUnequipMessage(Slots slot)
{
var unequipmessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Unequip);
SendNetworkMessage(unequipmessage);
}
public void SendEquipMessage(Slots slot)
{
var equipmessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Equip);
SendNetworkMessage(equipmessage);
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null,
IComponent component = null)
{
base.HandleMessage(message, netChannel, component);
switch (message)
{
case PlayerAttachedMsg _:
InterfaceController.PlayerAttached();
break;
case PlayerDetachedMsg _:
InterfaceController.PlayerDetached();
break;
}
}
}
}

View File

@@ -0,0 +1,194 @@
using System.Collections.Generic;
using Content.Client.Utility;
using JetBrains.Annotations;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
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;
#pragma warning restore 649
private readonly Dictionary<Slots, List<InventoryButton>> _inventoryButtons
= new Dictionary<Slots, List<InventoryButton>>();
private InventoryButton _hudButtonPocket1;
private InventoryButton _hudButtonPocket2;
private InventoryButton _hudButtonBelt;
private InventoryButton _hudButtonBack;
private Control _quickButtonsContainer;
public HumanInventoryInterfaceController(ClientInventoryComponent owner) : base(owner)
{
}
public override void Initialize()
{
base.Initialize();
_window = new HumanInventoryWindow(_loc, _resourceCache);
foreach (var (slot, button) in _window.Buttons)
{
button.OnPressed = AddToInventory;
_inventoryButtons.Add(slot, new List<InventoryButton> {button});
}
void AddButton(out InventoryButton variable, Slots slot, string textureName)
{
var texture = _resourceCache.GetTexture($"/Textures/UserInterface/Inventory/{textureName}.png");
variable = new InventoryButton(slot, texture)
{
OnPressed = AddToInventory
};
_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");
_quickButtonsContainer = new HBoxContainer
{
Children =
{
_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;
}
entity.TryGetComponent(out ISpriteComponent sprite);
foreach (var button in buttons)
{
button.SpriteView.Sprite = sprite;
button.OnPressed = RemoveFromInventory;
}
}
public override void RemoveFromSlot(Slots slot)
{
base.RemoveFromSlot(slot);
if (!_inventoryButtons.TryGetValue(slot, out var buttons))
{
return;
}
foreach (var button in buttons)
{
button.SpriteView.Sprite = null;
button.OnPressed = AddToInventory;
}
}
public override void PlayerAttached()
{
base.PlayerAttached();
GameHud.InventoryQuickButtonContainer.AddChild(_quickButtonsContainer);
}
public override void PlayerDetached()
{
base.PlayerDetached();
GameHud.InventoryQuickButtonContainer.RemoveChild(_quickButtonsContainer);
}
private class HumanInventoryWindow : SS14Window
{
private const int ButtonSize = 64;
private const int ButtonSeparation = 2;
private const int RightSeparation = 2;
public IReadOnlyDictionary<Slots, InventoryButton> Buttons { get; }
public HumanInventoryWindow(ILocalizationManager loc, IResourceCache resourceCache)
{
Title = loc.GetString("Your Inventory");
Resizable = false;
var buttonDict = new Dictionary<Slots, InventoryButton>();
Buttons = buttonDict;
const int width = ButtonSize * 4 + ButtonSeparation * 3 + RightSeparation;
const int height = ButtonSize * 4 + ButtonSeparation * 3;
var windowContents = new Control {CustomMinimumSize = (width, height)};
Contents.AddChild(windowContents);
void AddButton(Slots slot, string textureName, Vector2 position)
{
var texture = resourceCache.GetTexture($"/Textures/UserInterface/Inventory/{textureName}.png");
var button = new InventoryButton(slot, texture)
{
Position = position
};
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, "mask", (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)));
Size = CombinedMinimumSize;
}
}
}
}

View File

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

View File

@@ -0,0 +1,79 @@
using System;
using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components.Inventory;
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
{
// ReSharper disable once UnassignedGetOnlyAutoProperty
[field: Dependency] protected IGameHud GameHud { get; }
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 void RemoveFromInventory(BaseButton.ButtonEventArgs args)
{
args.Button.Pressed = false;
var control = (InventoryButton) args.Button.Parent;
Owner.SendUnequipMessage(control.Slot);
}
protected void AddToInventory(BaseButton.ButtonEventArgs args)
{
args.Button.Pressed = false;
var control = (InventoryButton) args.Button.Parent;
Owner.SendEquipMessage(control.Slot);
}
}
}

View File

@@ -0,0 +1,287 @@
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);
}
public override void Startup()
{
base.Startup();
SnapGrid.OnPositionChanged += SnapGridOnPositionChanged;
Owner.EntityManager.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}");
}
public override void Shutdown()
{
SnapGrid.OnPositionChanged -= SnapGridOnPositionChanged;
Owner.EntityManager.RaiseEvent(Owner, new IconSmoothDirtyEvent(_lastPosition, SnapGrid.Offset, Mode));
base.Shutdown();
}
private void SnapGridOnPositionChanged()
{
Owner.EntityManager.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

@@ -1,226 +0,0 @@
using Content.Shared.GameObjects;
using Content.Shared.Input;
using SS14.Client.GameObjects;
using SS14.Client.Interfaces.Input;
using SS14.Client.UserInterface;
using SS14.Client.UserInterface.Controls;
using SS14.Client.UserInterface.CustomControls;
using SS14.Shared.ContentPack;
using SS14.Shared.GameObjects;
using SS14.Shared.GameObjects.Serialization;
using SS14.Shared.Input;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.Interfaces.Network;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using SS14.Shared.Maths;
using SS14.Shared.Utility;
using System;
using System.Collections.Generic;
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
using static Content.Shared.GameObjects.SharedInventoryComponent.ClientInventoryMessage;
using static Content.Shared.GameObjects.SharedInventoryComponent.ServerInventoryMessage;
namespace Content.Client.GameObjects
{
public class ClientInventoryComponent : SharedInventoryComponent
{
private InventoryWindow Window;
private string TemplateName = "HumanInventory"; //stored for serialization purposes
private InputCommand OpenMenuCommand;
public override void OnRemove()
{
base.OnRemove();
Window.Dispose();
}
public override void ExposeData(EntitySerializer serializer)
{
base.ExposeData(serializer);
Window = new InventoryWindow(this);
OpenMenuCommand = InputCommand.FromDelegate(() => { Window.AddToScreen(); Window.Open(); });
serializer.DataField(ref TemplateName, "Template", "HumanInventory");
Window.CreateInventory(TemplateName);
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
{
var inputMgr = IoCManager.Resolve<IInputManager>();
switch (message)
{
//Updates what we are storing in UI slots
case ServerInventoryMessage msg:
if (msg.Updatetype == ServerInventoryUpdate.Addition)
{
Window.AddToSlot(msg);
}
else if (msg.Updatetype == ServerInventoryUpdate.Removal)
{
Window.RemoveFromSlot(msg);
}
break;
case PlayerAttachedMsg _:
inputMgr.SetInputCommand(ContentKeyFunctions.OpenCharacterMenu, OpenMenuCommand);
break;
case PlayerDetachedMsg _:
inputMgr.SetInputCommand(ContentKeyFunctions.OpenCharacterMenu, null);
break;
}
}
public void SendUnequipMessage(Slots slot)
{
var unequipmessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Unequip);
SendNetworkMessage(unequipmessage);
}
public void SendEquipMessage(Slots slot)
{
var equipmessage = new ClientInventoryMessage(slot, ClientInventoryUpdate.Equip);
SendNetworkMessage(equipmessage);
}
/// <summary>
/// Temporary window to hold the basis for inventory hud
/// </summary>
private class InventoryWindow : SS14Window
{
private int elements_x;
private GridContainer GridContainer;
private List<Slots> IndexedSlots;
private Dictionary<Slots, InventoryButton> InventorySlots = new Dictionary<Slots, InventoryButton>(); //ordered dictionary?
private ClientInventoryComponent InventoryComponent;
protected override ResourcePath ScenePath => new ResourcePath("/Scenes/Inventory/HumanInventory.tscn");
public InventoryWindow(ClientInventoryComponent inventory)
{
InventoryComponent = inventory;
HideOnClose = true;
}
/// <summary>
/// Creates a grid container filled with slot buttons loaded from an inventory template
/// </summary>
/// <param name="TemplateName"></param>
public void CreateInventory(string TemplateName)
{
Type type = AppDomain.CurrentDomain.GetAssemblyByName("Content.Shared").GetType("Content.Shared.GameObjects." + TemplateName);
Inventory inventory = (Inventory)Activator.CreateInstance(type);
elements_x = inventory.Columns;
GridContainer = (GridContainer)Contents.GetChild("PanelContainer").GetChild("CenterContainer").GetChild("GridContainer");
GridContainer.Columns = elements_x;
IndexedSlots = new List<Slots>(inventory.SlotMasks);
foreach (Slots slot in IndexedSlots)
{
InventoryButton newbutton = new InventoryButton(slot);
if (slot == Slots.NONE)
{
//TODO: Re-enable when godot grid container maintains grid with invisible elements
//newbutton.Visible = false;
}
else
{
//Store slot button and give it the default onpress behavior for empty elements
newbutton.GetChild<Button>("Button").OnPressed += AddToInventory;
InventorySlots.Add(slot, newbutton);
}
if (SlotNames.ContainsKey(slot))
{
newbutton.GetChild<Button>("Button").Text = SlotNames[slot];
}
GridContainer.AddChild(newbutton);
}
}
/// <summary>
/// Adds the item we have equipped to the slot texture and prepares the slot button for removal
/// </summary>
/// <param name="message"></param>
public void AddToSlot(ServerInventoryMessage message)
{
InventoryButton button = InventorySlots[message.Inventoryslot];
var entity = IoCManager.Resolve<IEntityManager>().GetEntity(message.EntityUid);
button.EntityUid = message.EntityUid;
var container = button.GetChild("CenterContainer");
button.GetChild<Button>("Button").OnPressed += RemoveFromInventory;
button.GetChild<Button>("Button").OnPressed -= AddToInventory;
//Gets entity sprite and assigns it to button texture
if (entity.TryGetComponent(out IconComponent sprite))
{
var tex = sprite.Icon.Default;
var rect = button.GetChild("CenterContainer").GetChild<TextureRect>("TextureRect");
if (tex != null)
{
rect.Texture = tex;
rect.Scale = new Vector2(Math.Min(CalculateMinimumSize().X, 32) / tex.Height, Math.Min(CalculateMinimumSize().Y, 32) / tex.Height);
}
else
{
throw new NotImplementedException();
}
}
}
/// <summary>
/// Remove element from the UI and update its button to blank texture and prepare for insertion again
/// </summary>
/// <param name="message"></param>
public void RemoveFromSlot(ServerInventoryMessage message)
{
InventoryButton button = InventorySlots[message.Inventoryslot];
button.GetChild("CenterContainer").GetChild<TextureRect>("TextureRect").Texture = null;
button.EntityUid = EntityUid.Invalid;
button.GetChild<Button>("Button").OnPressed -= RemoveFromInventory;
button.GetChild<Button>("Button").OnPressed += AddToInventory;
}
private void RemoveFromInventory(BaseButton.ButtonEventArgs args)
{
args.Button.Pressed = false;
var control = (InventoryButton)args.Button.Parent;
InventoryComponent.SendUnequipMessage(control.Slot);
}
private void AddToInventory(BaseButton.ButtonEventArgs args)
{
args.Button.Pressed = false;
var control = (InventoryButton)args.Button.Parent;
InventoryComponent.SendEquipMessage(control.Slot);
}
}
private class InventoryButton : Control
{
public Slots Slot;
public EntityUid EntityUid;
protected override ResourcePath ScenePath => new ResourcePath("/Scenes/Inventory/StorageSlot.tscn");
public InventoryButton(Slots slot)
{
Slot = slot;
}
}
}
}

View File

@@ -1,22 +1,62 @@
using Content.Client.Interfaces.GameObjects;
using System.Collections.Generic;
using System.Linq;
using Content.Client.Interfaces.GameObjects;
using Content.Client.UserInterface;
using Content.Shared.GameObjects;
using SS14.Client.Interfaces.UserInterface;
using SS14.Shared.GameObjects;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.IoC;
using System.Collections.Generic;
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.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameObjects
{
[RegisterComponent]
[ComponentReference(typeof(IHandsComponent))]
public class HandsComponent : SharedHandsComponent, IHandsComponent
{
private readonly Dictionary<string, IEntity> hands = new Dictionary<string, IEntity>();
public string ActiveIndex { get; private set; }
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))
if (_hands.TryGetValue(index, out var entity))
{
return entity;
}
@@ -24,33 +64,109 @@ namespace Content.Client.GameObjects
return null;
}
public override void HandleComponentState(ComponentState state)
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
{
var cast = (HandsComponentState)state;
hands.Clear();
foreach (var hand in cast.Hands)
if (curState == null)
return;
var cast = (HandsComponentState) curState;
foreach (var (slot, uid) in cast.Hands)
{
IEntity entity = null;
try
{
entity = Owner.EntityManager.GetEntity(hand.Value);
entity = Owner.EntityManager.GetEntity(uid);
}
catch
{
// Nothing.
}
hands[hand.Key] = entity;
_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;
// Tell UI to update.
var uiMgr = IoCManager.Resolve<IUserInterfaceManager>();
if (!uiMgr.StateRoot.TryGetChild<HandsGui>("HandsGui", out var control))
{
control = new HandsGui();
uiMgr.StateRoot.AddChild(control);
_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;
}
control.UpdateHandIcons();
}
public void SendChangeHand(string index)
@@ -58,6 +174,11 @@ namespace Content.Client.GameObjects
SendNetworkMessage(new ClientChangedHandMsg(index));
}
public void AttackByInHand(string index)
{
SendNetworkMessage(new ClientAttackByInHandMsg(index));
}
public void UseActiveHand()
{
if (GetEntity(ActiveIndex) != null)

View File

@@ -0,0 +1,74 @@
using System;
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;
public override Type StateType => typeof(ItemComponentState);
[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,209 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Content.Client.GameObjects.Components.IconSmoothing;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Maths;
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; }
public override void Startup()
{
base.Startup();
var overState0 = $"{StateBase}over_0";
Sprite.LayerMapSet(OverCornerLayers.SE, Sprite.AddLayerState(overState0));
Sprite.LayerSetDirOffset(OverCornerLayers.SE, DirectionOffset.None);
Sprite.LayerMapSet(OverCornerLayers.NE, Sprite.AddLayerState(overState0));
Sprite.LayerSetDirOffset(OverCornerLayers.NE, DirectionOffset.CounterClockwise);
Sprite.LayerMapSet(OverCornerLayers.NW, Sprite.AddLayerState(overState0));
Sprite.LayerSetDirOffset(OverCornerLayers.NW, DirectionOffset.Flip);
Sprite.LayerMapSet(OverCornerLayers.SW, Sprite.AddLayerState(overState0));
Sprite.LayerSetDirOffset(OverCornerLayers.SW, DirectionOffset.Clockwise);
}
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}");
Sprite.LayerSetState(OverCornerLayers.NE, $"{StateBase}over_{(int) lowCornerNE}");
Sprite.LayerSetState(OverCornerLayers.SE, $"{StateBase}over_{(int) lowCornerSE}");
Sprite.LayerSetState(OverCornerLayers.SW, $"{StateBase}over_{(int) lowCornerSW}");
Sprite.LayerSetState(OverCornerLayers.NW, $"{StateBase}over_{(int) lowCornerNW}");
LastCornerNE = cornerNE;
LastCornerSE = cornerSE;
LastCornerSW = cornerSW;
LastCornerNW = cornerNW;
foreach (var entity in SnapGrid.GetLocal())
{
if (entity.TryGetComponent(out WindowComponent window))
{
window.UpdateSprite();
}
}
}
[System.Diagnostics.Contracts.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,89 @@
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 = 1.5f;
// Minimum rate of magnitude restore towards 0 kick.
private const float RestoreRateMin = 0.5f;
// 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;
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;
_currentKick -= restore;
_updateEye();
}
private void _updateEye()
{
_eye.Offset = _currentKick;
}
}
}

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

View File

@@ -0,0 +1,35 @@
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Mobs
{
public class SpeciesVisualizer2D : AppearanceVisualizer
{
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
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,177 @@
using System;
using Content.Client.UserInterface;
using Content.Shared.GameObjects.Components.Power;
using NJsonSchema.Validation;
using OpenTK.Graphics.OpenGL4;
using Robust.Client.GameObjects.Components.UserInterface;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects.Components.UserInterface;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
namespace Content.Client.GameObjects.Components.Power
{
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
{
MarginRight = 426.0f, MarginBottom = 270.0f
};
_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);
float ChargePercentage = (castState.Charge / _chargeBar.MaxValue) * 100.0f;
_window.ChargePercentage.Text = " " + ChargePercentage.ToString("0.00") + "%";
}
private void UpdateChargeBarColor(float charge)
{
float normalizedCharge = charge / _chargeBar.MaxValue;
float leftHue = 0.0f;// Red
float middleHue = 0.066f;// Orange
float rightHue = 0.33f;// Green
float saturation = 1.0f;// Uniform saturation
float value = 0.8f;// Uniform value / brightness
float alpha = 1.0f;// Uniform alpha
// These should add up to 1.0 or your transition won't be smooth
float leftSideSize = 0.5f;// Fraction of _chargeBar lerped from leftHue to middleHue
float rightSideSize = 0.5f;// Fraction of _chargeBar lerped from middleHue to rightHue
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("Rows");
var statusHeader = new Label("StatusHeader") { Text = "Power Status: " };
rows.AddChild(statusHeader);
var breaker = new HBoxContainer("Breaker");
var breakerLabel = new Label("Label") { Text = "Main Breaker: " };
BreakerButton = new CheckButton {Name = "Breaker", Text = "Toggle"};
breaker.AddChild(breakerLabel);
breaker.AddChild(BreakerButton);
rows.AddChild(breaker);
var externalStatus = new HBoxContainer("ExternalStatus");
var externalStatusLabel = new Label("Label") { Text = "External Power: " };
ExternalPowerStateLabel = new Label("Status") { Text = "Good" };
ExternalPowerStateLabel.SetOnlyStyleClass(NanoStyle.StyleClassPowerStateGood);
externalStatus.AddChild(externalStatusLabel);
externalStatus.AddChild(ExternalPowerStateLabel);
rows.AddChild(externalStatus);
var charge = new HBoxContainer("Charge");
var chargeLabel = new Label("Label") { Text = "Charge:" };
ChargeBar = new ProgressBar("Charge")
{
SizeFlagsHorizontal = Control.SizeFlags.FillExpand,
MinValue = 0.0f,
MaxValue = 1.0f,
Page = 0.0f,
Value = 0.5f
};
ChargePercentage = new Label("ChargePercentage");
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

@@ -1,15 +1,16 @@
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Power;
using SS14.Client.UserInterface.Controls;
using SS14.Client.UserInterface.CustomControls;
using SS14.Shared.GameObjects;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.Interfaces.Network;
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);
@@ -17,13 +18,16 @@ namespace Content.Client.GameObjects.Components.Power
switch (message)
{
case OpenDataWindowMsg msg:
var window = new SS14Window
if (LastWindow != null && !LastWindow.Disposed)
{
Title = "Power Debug Tool"
LastWindow.Dispose();
}
LastWindow = new SS14Window()
{
Title = "Power Debug Tool",
};
window.Contents.AddChild(new Label() { Text = msg.Data });
window.AddToScreen();
window.Open();
LastWindow.Contents.AddChild(new Label() { Text = msg.Data });
LastWindow.Open();
break;
}
}

View File

@@ -0,0 +1,77 @@
using Content.Shared.GameObjects.Components.Power;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Content.Client.GameObjects.Components.Power
{
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,102 @@
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 LatheDatabaseComponent 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();
IoCManager.InjectDependencies(this);
if (!Owner.Owner.TryGetComponent(out MaterialStorageComponent storage)
|| !Owner.Owner.TryGetComponent(out SharedLatheComponent lathe)
|| !Owner.Owner.TryGetComponent(out LatheDatabaseComponent database)) return;
Storage = storage;
Lathe = lathe;
Database = database;
menu = new LatheMenu {Owner = this};
queueMenu = new LatheQueueMenu { Owner = this };
menu.OnClose += Close;
menu.Populate();
menu.PopulateMaterials();
menu.QueueButton.OnPressed += (args) => { queueMenu.OpenCentered(); };
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 msg:
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,108 @@
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Sound;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.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;
private Random Random;
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;
if (Random == null) Random = new Random(Owner.Uid.GetHashCode() ^ DateTime.Now.GetHashCode());
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 msg:
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

@@ -1,24 +1,23 @@
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Storage;
using SS14.Client.GameObjects;
using SS14.Client.UserInterface;
using SS14.Client.UserInterface.Controls;
using SS14.Client.UserInterface.CustomControls;
using SS14.Shared.GameObjects;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.Interfaces.Network;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using SS14.Shared.Maths;
using SS14.Shared.Utility;
using System;
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;
using Robust.Shared.Serialization;
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>();
@@ -37,10 +36,14 @@ namespace Content.Client.GameObjects.Components.Storage
public override void OnRemove()
{
Window.Dispose();
base.OnRemove();
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
}
public override void HandleMessage(ComponentMessage message, INetChannel netChannel = null, IComponent component = null)
{
switch (message)
@@ -53,6 +56,9 @@ namespace Content.Client.GameObjects.Components.Storage
case OpenStorageUIMessage msg:
OpenUI();
break;
case CloseStorageUIMessage msg:
CloseUI();
break;
}
}
@@ -73,10 +79,14 @@ namespace Content.Client.GameObjects.Components.Storage
/// </summary>
private void OpenUI()
{
Window.AddToScreen();
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>
@@ -96,18 +106,46 @@ namespace Content.Client.GameObjects.Components.Storage
private Label Information;
public ClientStorageComponent StorageEntity;
protected override ResourcePath ScenePath => new ResourcePath("/Scenes/Storage/Storage.tscn");
public StorageWindow()
{
Size = new Vector2(180.0f, 320.0f);
}
protected override void Initialize()
{
base.Initialize();
HideOnClose = true;
Title = "Storage Item";
RectClipContent = true;
// Get all the controls.
VSplitContainer = Contents.GetChild("VSplitContainer");
EntityList = VSplitContainer.GetChild("ListScrollContainer").GetChild<VBoxContainer>("EntityList");
Information = VSplitContainer.GetChild<Label>("Information");
VSplitContainer = new VBoxContainer("VSplitContainer");
Information = new Label("Information")
{
Text = "Items: 0 Volume: 0/0 Stuff",
SizeFlagsVertical = SizeFlags.ShrinkCenter
};
VSplitContainer.AddChild(Information);
var listScrollContainer = new ScrollContainer("ListScrollContainer")
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.FillExpand,
HScrollEnabled = true,
VScrollEnabled = true
};
EntityList = new VBoxContainer("EntityList")
{
SizeFlagsHorizontal = SizeFlags.FillExpand
};
listScrollContainer.AddChild(EntityList);
VSplitContainer.AddChild(listScrollContainer);
Contents.AddChild(VSplitContainer);
}
public override void Close()
{
StorageEntity.SendNetworkMessage(new CloseStorageUIMessage());
base.Close();
}
/// <summary>
@@ -127,29 +165,15 @@ namespace Content.Client.GameObjects.Components.Storage
{
EntityuID = entityuid.Key
};
var container = button.GetChild("HBoxContainer");
button.ActualButton.OnToggled += OnItemButtonToggled;
//Name and Size labels set
container.GetChild<Label>("Name").Text = entity.Name;
container.GetChild<Control>("Control").GetChild<Label>("Size").Text = string.Format("{0}", entityuid.Value);
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 IconComponent icon))
if (entity.TryGetComponent(out ISpriteComponent sprite))
{
var tex = icon.Icon.Default;
var rect = container.GetChild("TextureWrap").GetChild<TextureRect>("TextureRect");
if (tex != null)
{
rect.Texture = tex;
// Copypasted but replaced with 32 dunno if good
var scale = (float)32 / tex.Height;
rect.Scale = new Vector2(scale, scale);
}
else
{
rect.Dispose();
}
button.EntitySpriteView.Sprite = sprite;
}
EntityList.AddChild(button);
@@ -181,17 +205,64 @@ namespace Content.Client.GameObjects.Components.Storage
/// <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 : Control
private class EntityButton : PanelContainer
{
public EntityUid EntityuID { get; set; }
public Button ActualButton { get; private set; }
protected override ResourcePath ScenePath => new ResourcePath("/Scenes/Storage/StorageEntity.tscn");
public SpriteView EntitySpriteView { get; private set; }
public Control EntityControl { get; private set; }
public Label EntityName { get; private set; }
public Label EntitySize { get; private set; }
protected override void Initialize()
{
base.Initialize();
ActualButton = GetChild<Button>("Button");
ActualButton = new Button("Button")
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsVertical = SizeFlags.FillExpand,
ToggleMode = true,
MouseFilter = MouseFilterMode.Stop
};
AddChild(ActualButton);
var hBoxContainer = new HBoxContainer("HBoxContainer") {MouseFilter = MouseFilterMode.Ignore};
EntitySpriteView = new SpriteView("SpriteView")
{
CustomMinimumSize = new Vector2(32.0f, 32.0f), MouseFilter = MouseFilterMode.Ignore
};
EntityName = new Label("Name")
{
SizeFlagsVertical = SizeFlags.ShrinkCenter,
Text = "Backpack",
MouseFilter = MouseFilterMode.Ignore
};
hBoxContainer.AddChild(EntitySpriteView);
hBoxContainer.AddChild(EntityName);
EntityControl = new Control("Control")
{
SizeFlagsHorizontal = SizeFlags.FillExpand, MouseFilter = MouseFilterMode.Ignore
};
EntitySize = new Label("Size")
{
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,51 @@
using Content.Shared.Maps;
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="ContentTileDefinition.IsSubFloor"/>
[RegisterComponent]
public sealed class SubFloorHideComponent : Component
{
private SnapGridComponent _snapGridComponent;
public override string Name => "SubFloorHide";
public override void Initialize()
{
base.Initialize();
_snapGridComponent = Owner.GetComponent<SnapGridComponent>();
}
public override void Startup()
{
base.Startup();
_snapGridComponent.OnPositionChanged += SnapGridOnPositionChanged;
Owner.EntityManager.RaiseEvent(Owner, new SubFloorHideDirtyEvent());
}
public override void Shutdown()
{
base.Shutdown();
_snapGridComponent.OnPositionChanged -= SnapGridOnPositionChanged;
}
private void SnapGridOnPositionChanged()
{
Owner.EntityManager.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
}
}

View File

@@ -0,0 +1,41 @@
using Content.Shared.GameObjects.Components.Weapons.Ranged;
using Content.Shared.Utility;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Content.Client.GameObjects.Components.Weapons.Ranged
{
public sealed class BallisticMagazineVisualizer2D : AppearanceVisualizer
{
private string _baseState;
private int _steps;
public override void LoadData(YamlMappingNode node)
{
base.LoadData(node);
_baseState = node.GetNode("base_state").AsString();
_steps = node.GetNode("steps").AsInt();
}
public override void OnChangeData(AppearanceComponent component)
{
var sprite = component.Owner.GetComponent<ISpriteComponent>();
if (!component.TryGetData(BallisticMagazineVisuals.AmmoCapacity, out int capacity))
{
return;
}
if (!component.TryGetData(BallisticMagazineVisuals.AmmoLeft, out int current))
{
return;
}
var step = ContentHelpers.RoundToLevels(current, capacity, _steps);
sprite.LayerSetState(0, $"{_baseState}-{step}");
}
}
}

View File

@@ -0,0 +1,50 @@
using Content.Shared.GameObjects.Components.Weapons.Ranged;
using Content.Shared.Utility;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
namespace Content.Client.GameObjects.Components.Weapons.Ranged
{
public sealed class BallisticMagazineWeaponVisualizer2D : AppearanceVisualizer
{
private string _baseState;
private int _steps;
public override void LoadData(YamlMappingNode node)
{
base.LoadData(node);
_baseState = node.GetNode("base_state").AsString();
_steps = node.GetNode("steps").AsInt();
}
public override void OnChangeData(AppearanceComponent component)
{
var sprite = component.Owner.GetComponent<ISpriteComponent>();
component.TryGetData(BallisticMagazineWeaponVisuals.MagazineLoaded, out bool loaded);
if (loaded)
{
if (!component.TryGetData(BallisticMagazineWeaponVisuals.AmmoCapacity, out int capacity))
{
return;
}
if (!component.TryGetData(BallisticMagazineWeaponVisuals.AmmoLeft, out int current))
{
return;
}
var step = ContentHelpers.RoundToLevels(current, capacity, _steps);
sprite.LayerSetState(0, $"{_baseState}-{step}");
}
else
{
sprite.LayerSetState(0, _baseState);
}
}
}
}

View File

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

View File

@@ -0,0 +1,33 @@
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 HitscanWeaponVisualizer2D : AppearanceVisualizer
{
private string _prefix;
public override void LoadData(YamlMappingNode node)
{
base.LoadData(node);
_prefix = node.GetNode("prefix").AsString();
}
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(0, $"{_prefix}_{ContentHelpers.RoundToLevels(fraction, 1, 5) * 25}");
}
}
}
}

View File

@@ -0,0 +1,92 @@
using Content.Client.GameObjects.EntitySystems;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.Serialization;
using static Content.Client.GameObjects.Components.IconSmoothing.IconSmoothComponent;
namespace Content.Client.GameObjects.Components
{
[RegisterComponent]
public sealed class WindowComponent : Component
{
public override string Name => "Window";
private string _stateBase;
private ISpriteComponent _sprite;
private SnapGridComponent _snapGrid;
public override void Initialize()
{
base.Initialize();
_sprite = Owner.GetComponent<ISpriteComponent>();
_snapGrid = Owner.GetComponent<SnapGridComponent>();
}
public override void Startup()
{
base.Startup();
_snapGrid.OnPositionChanged += SnapGridOnPositionChanged;
Owner.EntityManager.RaiseEvent(Owner, new WindowSmoothDirtyEvent());
var state0 = $"{_stateBase}0";
_sprite.LayerMapSet(CornerLayers.SE, _sprite.AddLayerState(state0));
_sprite.LayerSetDirOffset(CornerLayers.SE, SpriteComponent.DirectionOffset.None);
_sprite.LayerMapSet(CornerLayers.NE, _sprite.AddLayerState(state0));
_sprite.LayerSetDirOffset(CornerLayers.NE, SpriteComponent.DirectionOffset.CounterClockwise);
_sprite.LayerMapSet(CornerLayers.NW, _sprite.AddLayerState(state0));
_sprite.LayerSetDirOffset(CornerLayers.NW, SpriteComponent.DirectionOffset.Flip);
_sprite.LayerMapSet(CornerLayers.SW, _sprite.AddLayerState(state0));
_sprite.LayerSetDirOffset(CornerLayers.SW, SpriteComponent.DirectionOffset.Clockwise);
}
public override void Shutdown()
{
_snapGrid.OnPositionChanged -= SnapGridOnPositionChanged;
base.Shutdown();
}
private void SnapGridOnPositionChanged()
{
Owner.EntityManager.RaiseEvent(Owner, new WindowSmoothDirtyEvent());
}
public void UpdateSprite()
{
var lowWall = FindLowWall();
if (lowWall == null)
{
return;
}
_sprite.LayerSetState(CornerLayers.NE, $"{_stateBase}{(int) lowWall.LastCornerNE}");
_sprite.LayerSetState(CornerLayers.SE, $"{_stateBase}{(int) lowWall.LastCornerSE}");
_sprite.LayerSetState(CornerLayers.SW, $"{_stateBase}{(int) lowWall.LastCornerSW}");
_sprite.LayerSetState(CornerLayers.NW, $"{_stateBase}{(int) lowWall.LastCornerNW}");
}
private LowWallComponent FindLowWall()
{
foreach (var entity in _snapGrid.GetLocal())
{
if (entity.TryGetComponent(out LowWallComponent lowWall))
{
return lowWall;
}
}
return null;
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _stateBase, "base", null);
}
}
}

View File

@@ -0,0 +1,27 @@
using Content.Client.GameObjects.Components.Mobs;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Client.GameObjects.EntitySystems
{
public sealed class CameraRecoilSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(CameraRecoilComponent));
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
foreach (var entity in RelevantEntities)
{
var recoil = entity.GetComponent<CameraRecoilComponent>();
recoil.FrameUpdate(frameTime);
}
}
}
}

View File

@@ -0,0 +1,75 @@
using Content.Client.GameObjects.Components.Actor;
using Content.Client.UserInterface;
using Content.Shared.Input;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Player;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input;
using Robust.Shared.IoC;
namespace Content.Client.GameObjects.EntitySystems
{
public sealed class CharacterInterfaceSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
[Dependency] private readonly IPlayerManager _playerManager;
#pragma warning restore 649
public override void Initialize()
{
base.Initialize();
var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>();
inputSys.BindMap.BindFunction(ContentKeyFunctions.OpenCharacterMenu,
InputCmdHandler.FromDelegate(s => HandleOpenCharacterMenu()));
}
private void HandleOpenCharacterMenu()
{
if (_playerManager.LocalPlayer.ControlledEntity == null
|| !_playerManager.LocalPlayer.ControlledEntity.TryGetComponent(out CharacterInterface characterInterface))
{
return;
}
var menu = characterInterface.Window;
if (menu == null)
{
return;
}
if (menu.IsOpen)
{
if (menu.IsAtFront())
{
_setOpenValue(menu, false);
}
else
{
menu.MoveToFront();
}
}
else
{
_setOpenValue(menu, true);
}
}
private void _setOpenValue(SS14Window menu, bool value)
{
if (value)
{
_gameHud.CharacterButtonDown = true;
menu.OpenCentered();
}
else
{
_gameHud.CharacterButtonDown = false;
menu.Close();
}
}
}
}

View File

@@ -0,0 +1,69 @@
using Content.Client.UserInterface;
using Content.Shared.Input;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Player;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input;
using Robust.Shared.IoC;
namespace Content.Client.GameObjects.EntitySystems
{
public sealed class ClientInventorySystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
[Dependency] private readonly IPlayerManager _playerManager;
#pragma warning restore 649
public override void Initialize()
{
base.Initialize();
var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>();
inputSys.BindMap.BindFunction(ContentKeyFunctions.OpenInventoryMenu,
InputCmdHandler.FromDelegate(s => HandleOpenInventoryMenu()));
}
private void HandleOpenInventoryMenu()
{
if (_playerManager.LocalPlayer.ControlledEntity == null
|| !_playerManager.LocalPlayer.ControlledEntity.TryGetComponent(out ClientInventoryComponent clientInventory))
{
return;
}
var menu = clientInventory.InterfaceController.Window;
if (menu.IsOpen)
{
if (menu.IsAtFront())
{
_setOpenValue(menu, false);
}
else
{
menu.MoveToFront();
}
}
else
{
_setOpenValue(menu, true);
}
}
private void _setOpenValue(SS14Window menu, bool value)
{
if (value)
{
_gameHud.InventoryButtonDown = true;
menu.OpenCentered();
}
else
{
_gameHud.InventoryButtonDown = false;
menu.Close();
}
}
}
}

View File

@@ -0,0 +1,7 @@
namespace Content.Client.GameObjects.EntitySystems
{
public class ClientNotifySystem
{
}
}

View File

@@ -0,0 +1,70 @@
using Content.Client.Construction;
using Content.Client.GameObjects.Components.Construction;
using Content.Client.UserInterface;
using Content.Shared.Input;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Player;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input;
using Robust.Shared.IoC;
namespace Content.Client.GameObjects.EntitySystems
{
public sealed class ConstructorSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
[Dependency] private readonly IPlayerManager _playerManager;
#pragma warning restore 649
public override void Initialize()
{
base.Initialize();
var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>();
inputSys.BindMap.BindFunction(ContentKeyFunctions.OpenCraftingMenu,
new PointerInputCmdHandler(HandleOpenCraftingMenu));
}
private void HandleOpenCraftingMenu(in PointerInputCmdHandler.PointerInputCmdArgs args)
{
if (_playerManager.LocalPlayer.ControlledEntity == null
|| !_playerManager.LocalPlayer.ControlledEntity.TryGetComponent(out ConstructorComponent constructor))
{
return;
}
var menu = constructor.ConstructionMenu;
if (menu.IsOpen)
{
if (menu.IsAtFront())
{
_setOpenValue(menu, false);
}
else
{
menu.MoveToFront();
}
}
else
{
_setOpenValue(menu, true);
}
}
private void _setOpenValue(ConstructionMenu menu, bool value)
{
if (value)
{
_gameHud.CraftingButtonDown = true;
menu.OpenCentered();
}
else
{
_gameHud.CraftingButtonDown = false;
menu.Close();
}
}
}
}

View File

@@ -0,0 +1,149 @@
using System.Threading;
using System.Threading.Tasks;
using Content.Shared.GameObjects.EntitySystemMessages;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Input;
using JetBrains.Annotations;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Players;
namespace Content.Client.GameObjects.EntitySystems
{
[UsedImplicitly]
internal sealed class ExamineSystem : ExamineSystemShared
{
public const string StyleClassEntityTooltip = "entity-tooltip";
#pragma warning disable 649
[Dependency] private IInputManager _inputManager;
[Dependency] private IUserInterfaceManager _userInterfaceManager;
[Dependency] private IEntityManager _entityManager;
[Dependency] private IPlayerManager _playerManager;
#pragma warning restore 649
private Popup _examineTooltipOpen;
private CancellationTokenSource _requestCancelTokenSource;
public override void Initialize()
{
IoCManager.InjectDependencies(this);
var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>();
inputSys.BindMap.BindFunction(ContentKeyFunctions.ExamineEntity, new PointerInputCmdHandler(HandleExamine));
}
public override void RegisterMessageTypes()
{
base.RegisterMessageTypes();
RegisterMessageType<ExamineSystemMessages.ExamineInfoResponseMessage>();
}
private void HandleExamine(ICommonSession session, GridCoordinates coords, EntityUid uid)
{
if (!uid.IsValid() || !_entityManager.TryGetEntity(uid, out var examined))
{
return;
}
var playerEntity = _playerManager.LocalPlayer.ControlledEntity;
if (playerEntity == null || !CanExamine(playerEntity, examined))
{
return;
}
DoExamine(examined);
}
public async void DoExamine(IEntity entity)
{
CloseTooltip();
var mousePos = _inputManager.MouseScreenPosition;
// Actually open the tooltip.
_examineTooltipOpen = new Popup();
_userInterfaceManager.StateRoot.AddChild(_examineTooltipOpen);
var panel = new PanelContainer();
panel.AddStyleClass(StyleClassEntityTooltip);
panel.ModulateSelfOverride = Color.LightGray.WithAlpha(0.90f);
_examineTooltipOpen.AddChild(panel);
panel.SetAnchorAndMarginPreset(Control.LayoutPreset.Wide);
var vBox = new VBoxContainer();
panel.AddChild(vBox);
var hBox = new HBoxContainer { SeparationOverride = 5};
vBox.AddChild(hBox);
if (entity.TryGetComponent(out ISpriteComponent sprite))
{
hBox.AddChild(new SpriteView {Sprite = sprite});
}
hBox.AddChild(new Label
{
Text = entity.Name,
SizeFlagsHorizontal = Control.SizeFlags.FillExpand,
});
const float minWidth = 300;
var size = Vector2.ComponentMax((minWidth, 0), panel.CombinedMinimumSize);
_examineTooltipOpen.Open(UIBox2.FromDimensions(mousePos, size));
if (entity.Uid.IsClientSide())
{
return;
}
// Ask server for extra examine info.
RaiseNetworkEvent(new ExamineSystemMessages.RequestExamineInfoMessage(entity.Uid));
ExamineSystemMessages.ExamineInfoResponseMessage response;
try
{
_requestCancelTokenSource = new CancellationTokenSource();
response =
await AwaitNetMessage<ExamineSystemMessages.ExamineInfoResponseMessage>(_requestCancelTokenSource
.Token);
}
catch (TaskCanceledException)
{
return;
}
finally
{
_requestCancelTokenSource = null;
}
var richLabel = new RichTextLabel();
richLabel.SetMessage(response.Message);
vBox.AddChild(richLabel);
}
public void CloseTooltip()
{
if (_examineTooltipOpen != null)
{
_examineTooltipOpen.Dispose();
_examineTooltipOpen = null;
}
if (_requestCancelTokenSource != null)
{
_requestCancelTokenSource.Cancel();
_requestCancelTokenSource = null;
}
}
}
}

View File

@@ -0,0 +1,155 @@
using System.Collections.Generic;
using System.Linq;
using Content.Client.GameObjects.Components.IconSmoothing;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.EntitySystems
{
/// <summary>
/// Entity system implementing the logic for <see cref="IconSmoothComponent"/>
/// </summary>
[UsedImplicitly]
internal sealed class IconSmoothSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager;
#pragma warning restore 649
private readonly Queue<IEntity> _dirtyEntities = new Queue<IEntity>();
private int _generation;
public override void SubscribeEvents()
{
base.SubscribeEvents();
SubscribeEvent<IconSmoothDirtyEvent>(HandleDirtyEvent);
}
public override void Initialize()
{
base.Initialize();
IoCManager.InjectDependencies(this);
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
if (_dirtyEntities.Count == 0)
{
return;
}
_generation += 1;
// Performance: This could be spread over multiple updates, or made parallel.
while (_dirtyEntities.Count > 0)
{
CalculateNewSprite(_dirtyEntities.Dequeue());
}
}
private void HandleDirtyEvent(object sender, IconSmoothDirtyEvent ev)
{
// Yes, we updates ALL smoothing entities surrounding us even if they would never smooth with us.
// This is simpler to implement. If you want to optimize it be my guest.
if (sender is IEntity senderEnt && senderEnt.IsValid() &&
senderEnt.HasComponent<IconSmoothComponent>())
{
var snapGrid = senderEnt.GetComponent<SnapGridComponent>();
_dirtyEntities.Enqueue(senderEnt);
AddValidEntities(snapGrid.GetInDir(Direction.North));
AddValidEntities(snapGrid.GetInDir(Direction.South));
AddValidEntities(snapGrid.GetInDir(Direction.East));
AddValidEntities(snapGrid.GetInDir(Direction.West));
if (ev.Mode == IconSmoothingMode.Corners)
{
AddValidEntities(snapGrid.GetInDir(Direction.NorthEast));
AddValidEntities(snapGrid.GetInDir(Direction.SouthEast));
AddValidEntities(snapGrid.GetInDir(Direction.SouthWest));
AddValidEntities(snapGrid.GetInDir(Direction.NorthWest));
}
}
else if (ev.LastPosition.HasValue)
{
// Entity is no longer valid, update around the last position it was at.
var grid = _mapManager.GetGrid(ev.LastPosition.Value.grid);
var pos = ev.LastPosition.Value.pos;
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(1, 0), ev.Offset));
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(-1, 0), ev.Offset));
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(0, 1), ev.Offset));
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(0, -1), ev.Offset));
if (ev.Mode == IconSmoothingMode.Corners)
{
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(1, 1), ev.Offset));
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(-1, -1), ev.Offset));
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(-1, 1), ev.Offset));
AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(1, -1), ev.Offset));
}
}
}
private void AddValidEntities(IEnumerable<IEntity> candidates)
{
foreach (var entity in candidates)
{
if (entity.HasComponent<IconSmoothComponent>())
{
_dirtyEntities.Enqueue(entity);
}
}
}
private void AddValidEntities(IEnumerable<IComponent> candidates)
{
AddValidEntities(candidates.Select(c => c.Owner));
}
private void CalculateNewSprite(IEntity entity)
{
// The generation check prevents updating an entity multiple times per tick.
// As it stands now, it's totally possible for something to get queued twice.
// Generation on the component is set after an update so we can cull updates that happened this generation.
if (!entity.IsValid()
|| !entity.TryGetComponent(out IconSmoothComponent smoothing)
|| smoothing.UpdateGeneration == _generation)
{
return;
}
smoothing.CalculateNewSprite();
smoothing.UpdateGeneration = _generation;
}
}
/// <summary>
/// Event raised by a <see cref="IconSmoothComponent"/> when it needs to be recalculated.
/// </summary>
public sealed class IconSmoothDirtyEvent : EntitySystemMessage
{
public IconSmoothDirtyEvent((GridId grid, MapIndices pos)? lastPosition, SnapGridOffset offset, IconSmoothingMode mode)
{
LastPosition = lastPosition;
Offset = offset;
Mode = mode;
}
public (GridId grid, MapIndices pos)? LastPosition { get; }
public SnapGridOffset Offset { get; }
public IconSmoothingMode Mode { get; }
}
}

View File

@@ -0,0 +1,76 @@
using Content.Client.GameObjects.Components.Weapons.Ranged;
using Content.Client.Interfaces.GameObjects;
using Content.Shared.Input;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Input;
using Robust.Client.Player;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input;
using Robust.Shared.IoC;
namespace Content.Client.GameObjects.EntitySystems
{
public class RangedWeaponSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IEyeManager _eyeManager;
[Dependency] private readonly IInputManager _inputManager;
#pragma warning restore 649
private InputSystem _inputSystem;
private bool _isFirstShot;
private bool _blocked;
public override void Initialize()
{
base.Initialize();
IoCManager.InjectDependencies(this);
_inputSystem = EntitySystemManager.GetEntitySystem<InputSystem>();
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var canFireSemi = _isFirstShot;
var state = _inputSystem.CmdStates.GetState(ContentKeyFunctions.UseItemInHand);
if (state != BoundKeyState.Down)
{
_isFirstShot = true;
_blocked = false;
return;
}
_isFirstShot = false;
var entity = _playerManager.LocalPlayer.ControlledEntity;
if (entity == null || !entity.TryGetComponent(out IHandsComponent hands))
{
return;
}
var held = hands.ActiveHand;
if (held == null || !held.TryGetComponent(out ClientRangedWeaponComponent weapon))
{
_blocked = true;
return;
}
if (_blocked)
{
return;
}
var worldPos = _eyeManager.ScreenToWorld(_inputManager.MouseScreenPosition);
if (weapon.Automatic || canFireSemi)
{
weapon.TryFire(worldPos);
}
}
}
}

View File

@@ -0,0 +1,80 @@
using Content.Client.GameObjects.Components;
using Content.Shared.Maps;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
namespace Content.Client.GameObjects.EntitySystems
{
/// <summary>
/// Entity system backing <see cref="SubFloorHideComponent"/>.
/// </summary>
internal sealed class SubFloorHideSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IMapManager _mapManager;
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
#pragma warning restore 649
public override void Initialize()
{
base.Initialize();
IoCManager.InjectDependencies(this);
_mapManager.GridChanged += MapManagerOnGridChanged;
_mapManager.TileChanged += MapManagerOnTileChanged;
SubscribeEvent<SubFloorHideDirtyEvent>(HandleDirtyEvent);
}
private void HandleDirtyEvent(object sender, SubFloorHideDirtyEvent ev)
{
if (!(sender is IEntity senderEnt))
{
return;
}
var sprite = senderEnt.GetComponent<ISpriteComponent>();
var grid = _mapManager.GetGrid(senderEnt.Transform.GridID);
var position = senderEnt.Transform.GridPosition;
var tileRef = grid.GetTileRef(position);
var tileDef = (ContentTileDefinition) _tileDefinitionManager[tileRef.Tile.TypeId];
sprite.Visible = tileDef.IsSubFloor;
}
private void MapManagerOnTileChanged(object sender, TileChangedEventArgs e)
{
UpdateTile(_mapManager.GetGrid(e.NewTile.GridIndex), e.NewTile.GridIndices);
}
private void MapManagerOnGridChanged(object sender, GridChangedEventArgs e)
{
foreach (var modified in e.Modified)
{
UpdateTile(e.Grid, modified.position);
}
}
private void UpdateTile(IMapGrid grid, MapIndices position)
{
var tile = grid.GetTileRef(position);
var tileDef = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId];
foreach (var snapGridComponent in grid.GetSnapGridCell(position, SnapGridOffset.Center))
{
var entity = snapGridComponent.Owner;
if (!entity.HasComponent<SubFloorHideComponent>() ||
!entity.TryGetComponent(out ISpriteComponent spriteComponent))
{
continue;
}
spriteComponent.Visible = tileDef.IsSubFloor;
}
}
}
}

View File

@@ -0,0 +1,235 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.EntitySystemMessages;
using Content.Shared.Input;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.State;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Client.State.States;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
namespace Content.Client.GameObjects.EntitySystems
{
public class VerbSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IStateManager _stateManager;
[Dependency] private readonly IEntityManager _entityManager;
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IInputManager _inputManager;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager;
#pragma warning restore 649
private Popup _currentPopup;
private EntityUid _currentEntity;
public override void Initialize()
{
base.Initialize();
IoCManager.InjectDependencies(this);
var input = EntitySystemManager.GetEntitySystem<InputSystem>();
input.BindMap.BindFunction(ContentKeyFunctions.OpenContextMenu,
new PointerInputCmdHandler(OnOpenContextMenu));
}
public override void RegisterMessageTypes()
{
base.RegisterMessageTypes();
RegisterMessageType<VerbSystemMessages.VerbsResponseMessage>();
}
public void OpenContextMenu(IEntity entity, ScreenCoordinates screenCoordinates)
{
if (_currentPopup != null)
{
_closeContextMenu();
}
_currentEntity = entity.Uid;
_currentPopup = new Popup();
_currentPopup.UserInterfaceManager.StateRoot.AddChild(_currentPopup);
_currentPopup.OnPopupHide += _closeContextMenu;
var vBox = new VBoxContainer("ButtonBox");
_currentPopup.AddChild(vBox);
vBox.AddChild(new Label {Text = "Waiting on Server..."});
RaiseNetworkEvent(new VerbSystemMessages.RequestVerbsMessage(_currentEntity));
var size = vBox.CombinedMinimumSize;
var box = UIBox2.FromDimensions(screenCoordinates.Position, size);
_currentPopup.Open(box);
}
private void OnOpenContextMenu(in PointerInputCmdHandler.PointerInputCmdArgs args)
{
if (_currentPopup != null)
{
_closeContextMenu();
return;
}
if (!(_stateManager.CurrentState is GameScreen gameScreen))
{
return;
}
var entities = gameScreen.GetEntitiesUnderPosition(args.Coordinates);
_currentPopup = new Popup();
_currentPopup.OnPopupHide += _closeContextMenu;
var vBox = new VBoxContainer("ButtonBox");
_currentPopup.AddChild(vBox);
foreach (var entity in entities)
{
var button = new Button {Text = entity.Name};
vBox.AddChild(button);
button.OnPressed += _ => OnContextButtonPressed(entity);
}
_currentPopup.UserInterfaceManager.StateRoot.AddChild(_currentPopup);
var size = vBox.CombinedMinimumSize;
var box = UIBox2.FromDimensions(args.ScreenCoordinates.Position, size);
_currentPopup.Open(box);
}
private void OnContextButtonPressed(IEntity entity)
{
OpenContextMenu(entity, new ScreenCoordinates(_inputManager.MouseScreenPosition));
}
public override void HandleNetMessage(INetChannel channel, EntitySystemMessage message)
{
base.HandleNetMessage(channel, message);
switch (message)
{
case VerbSystemMessages.VerbsResponseMessage resp:
_fillEntityPopup(resp);
break;
}
}
private void _fillEntityPopup(VerbSystemMessages.VerbsResponseMessage msg)
{
if (_currentEntity != msg.Entity || !_entityManager.TryGetEntity(_currentEntity, out var entity))
{
return;
}
DebugTools.AssertNotNull(_currentPopup);
var buttons = new List<Button>();
var vBox = _currentPopup.GetChild<VBoxContainer>("ButtonBox");
vBox.DisposeAllChildren();
foreach (var data in msg.Verbs)
{
var button = new Button {Text = data.Text, Disabled = !data.Available};
if (data.Available)
{
button.OnPressed += _ =>
{
RaiseNetworkEvent(new VerbSystemMessages.UseVerbMessage(_currentEntity, data.Key));
_closeContextMenu();
};
}
buttons.Add(button);
}
var user = GetUserEntity();
foreach (var (component, verb) in VerbUtility.GetVerbs(entity))
{
if (verb.RequireInteractionRange)
{
var distanceSquared = (user.Transform.WorldPosition - entity.Transform.WorldPosition)
.LengthSquared;
if (distanceSquared > Verb.InteractionRangeSquared)
{
continue;
}
}
var disabled = verb.GetVisibility(user, component) != VerbVisibility.Visible;
var button = new Button
{
Text = verb.GetText(user, component),
Disabled = disabled
};
if (!disabled)
{
button.OnPressed += _ =>
{
_closeContextMenu();
try
{
verb.Activate(user, component);
}
catch (Exception e)
{
Logger.ErrorS("verb", "Exception in verb {0} on {1}:\n{2}", verb, entity, e);
}
};
}
buttons.Add(button);
}
if (buttons.Count > 0)
{
buttons.Sort((a, b) => string.Compare(a.Text, b.Text, StringComparison.Ordinal));
foreach (var button in buttons)
{
vBox.AddChild(button);
}
}
else
{
var panel = new PanelContainer();
panel.AddChild(new Label {Text = "No verbs!"});
vBox.AddChild(panel);
}
_currentPopup.Size = vBox.CombinedMinimumSize;
// If we're at the bottom of the window and the menu would go below the bottom of the window,
// shift it up so it extends UP.
var bottomCoord = vBox.CombinedMinimumSize.Y + _currentPopup.Position.Y;
if (bottomCoord > _userInterfaceManager.StateRoot.Size.Y)
{
_currentPopup.Position = _currentPopup.Position - new Vector2(0, vBox.CombinedMinimumSize.Y);
}
}
private void _closeContextMenu()
{
_currentPopup?.Dispose();
_currentPopup = null;
_currentEntity = EntityUid.Invalid;
}
private IEntity GetUserEntity()
{
return _playerManager.LocalPlayer.ControlledEntity;
}
}
}

View File

@@ -0,0 +1,51 @@
using System.Collections.Generic;
using Content.Client.GameObjects.Components;
using Content.Client.GameObjects.Components.IconSmoothing;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Map;
namespace Content.Client.GameObjects.EntitySystems
{
[UsedImplicitly]
public sealed class WindowSystem : EntitySystem
{
private readonly Queue<IEntity> _dirtyEntities = new Queue<IEntity>();
public override void Initialize()
{
base.Initialize();
SubscribeEvent<WindowSmoothDirtyEvent>(HandleDirtyEvent);
}
private void HandleDirtyEvent(object sender, WindowSmoothDirtyEvent ev)
{
if (sender is IEntity senderEnt && senderEnt.HasComponent<WindowComponent>())
{
_dirtyEntities.Enqueue(senderEnt);
}
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
// Performance: This could be spread over multiple updates, or made parallel.
while (_dirtyEntities.Count > 0)
{
_dirtyEntities.Dequeue().GetComponent<WindowComponent>().UpdateSprite();
}
}
}
/// <summary>
/// Event raised by a <see cref="WindowComponent"/> when it needs to be recalculated.
/// </summary>
public sealed class WindowSmoothDirtyEvent : EntitySystemMessage
{
}
}

View File

@@ -0,0 +1,270 @@
using System;
using System.Linq;
using Content.Client.Chat;
using Content.Client.Interfaces;
using Content.Client.Interfaces.Chat;
using Content.Client.UserInterface;
using Content.Shared;
using Content.Shared.Input;
using Robust.Client;
using Robust.Client.Console;
using Robust.Client.Interfaces;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Content.Client.GameTicking
{
public class ClientGameTicker : SharedGameTicker, IClientGameTicker
{
#pragma warning disable 649
[Dependency] private IClientNetManager _netManager;
[Dependency] private IUserInterfaceManager _userInterfaceManager;
[Dependency] private IInputManager _inputManager;
[Dependency] private IBaseClient _baseClient;
[Dependency] private IChatManager _chatManager;
[Dependency] private IClientConsole _console;
[Dependency] private ILocalizationManager _localization;
[Dependency] private IResourceCache _resourceCache;
[Dependency] private IPlayerManager _playerManager;
[Dependency] private IGameHud _gameHud;
#pragma warning restore 649
[ViewVariables] private bool _areWeReady;
[ViewVariables] private bool _initialized;
[ViewVariables] private TickerState _tickerState;
[ViewVariables] private ChatBox _gameChat;
[ViewVariables] private LobbyGui _lobby;
[ViewVariables] private bool _gameStarted;
[ViewVariables] private DateTime _startTime;
public void Initialize()
{
DebugTools.Assert(!_initialized);
_netManager.RegisterNetMessage<MsgTickerJoinLobby>(nameof(MsgTickerJoinLobby), _joinLobby);
_netManager.RegisterNetMessage<MsgTickerJoinGame>(nameof(MsgTickerJoinGame), _joinGame);
_netManager.RegisterNetMessage<MsgTickerLobbyStatus>(nameof(MsgTickerLobbyStatus), _lobbyStatus);
_baseClient.RunLevelChanged += BaseClientOnRunLevelChanged;
_playerManager.PlayerListUpdated += PlayerManagerOnPlayerListUpdated;
_initialized = true;
}
private void PlayerManagerOnPlayerListUpdated(object sender, EventArgs e)
{
if (_lobby == null)
{
return;
}
_updatePlayerList();
}
private void _updatePlayerList()
{
_lobby.OnlinePlayerItemList.Clear();
foreach (var session in _playerManager.Sessions.OrderBy(s => s.Name))
{
_lobby.OnlinePlayerItemList.AddItem(session.Name);
}
}
private void BaseClientOnRunLevelChanged(object sender, RunLevelChangedEventArgs e)
{
if (e.NewLevel != ClientRunLevel.Initialize)
{
return;
}
_tickerState = TickerState.Unset;
_lobby?.Dispose();
_lobby = null;
_gameChat?.Dispose();
_gameChat = null;
_gameHud.RootControl.Orphan();
}
public void FrameUpdate(RenderFrameEventArgs renderFrameEventArgs)
{
if (_lobby == null)
{
return;
}
if (_gameStarted)
{
_lobby.StartTime.Text = "";
return;
}
string text;
var difference = _startTime - DateTime.UtcNow;
if (difference.Ticks < 0)
{
if (difference.TotalSeconds < -5)
{
text = _localization.GetString("Right Now?");
}
else
{
text = _localization.GetString("Right Now");
}
}
else
{
text = $"{(int) Math.Floor(difference.TotalMinutes)}:{difference.Seconds:D2}";
}
_lobby.StartTime.Text = _localization.GetString("Round Starts In: {0}", text);
}
private void _lobbyStatus(MsgTickerLobbyStatus message)
{
_startTime = message.StartTime;
_gameStarted = message.IsRoundStarted;
_areWeReady = message.YouAreReady;
_updateLobbyUi();
}
private void _updateLobbyUi()
{
if (_lobby == null)
{
return;
}
if (_gameStarted)
{
_lobby.ReadyButton.Text = _localization.GetString("Join");
_lobby.ReadyButton.ToggleMode = false;
_lobby.ReadyButton.Pressed = false;
}
else
{
_lobby.StartTime.Text = "";
_lobby.ReadyButton.Text = _localization.GetString("Ready Up");
_lobby.ReadyButton.ToggleMode = true;
_lobby.ReadyButton.Pressed = _areWeReady;
}
}
private void _joinLobby(MsgTickerJoinLobby message)
{
if (_tickerState == TickerState.InLobby)
{
return;
}
if (_gameChat != null)
{
_gameChat.Dispose();
_gameChat = null;
}
_gameHud.RootControl.Orphan();
_tickerState = TickerState.InLobby;
_lobby = new LobbyGui(_localization, _resourceCache);
_userInterfaceManager.StateRoot.AddChild(_lobby);
_lobby.SetAnchorAndMarginPreset(Control.LayoutPreset.Wide, margin: 20);
_chatManager.SetChatBox(_lobby.Chat);
_lobby.Chat.DefaultChatFormat = "ooc \"{0}\"";
_lobby.ServerName.Text = _baseClient.GameInfo.ServerName;
_inputManager.SetInputCommand(ContentKeyFunctions.FocusChat,
InputCmdHandler.FromDelegate(session =>
{
_lobby.Chat.Input.IgnoreNext = true;
_lobby.Chat.Input.GrabKeyboardFocus();
}));
_updateLobbyUi();
_lobby.ObserveButton.OnPressed += args => _console.ProcessCommand("observe");
_lobby.ReadyButton.OnPressed += args =>
{
if (!_gameStarted)
{
return;
}
_console.ProcessCommand("joingame");
};
_lobby.ReadyButton.OnToggled += args =>
{
if (_gameStarted)
{
return;
}
_console.ProcessCommand($"toggleready {args.Pressed}");
};
_lobby.LeaveButton.OnPressed += args => _console.ProcessCommand("disconnect");
_updatePlayerList();
}
private void _joinGame(MsgTickerJoinGame message)
{
if (_tickerState == TickerState.InGame)
{
return;
}
_tickerState = TickerState.InGame;
if (_lobby != null)
{
_lobby.Dispose();
_lobby = null;
}
_inputManager.SetInputCommand(ContentKeyFunctions.FocusChat,
InputCmdHandler.FromDelegate(session =>
{
_gameChat.Input.IgnoreNext = true;
_gameChat.Input.GrabKeyboardFocus();
}));
_gameChat = new ChatBox();
_userInterfaceManager.StateRoot.AddChild(_gameChat);
_userInterfaceManager.StateRoot.AddChild(_gameHud.RootControl);
_chatManager.SetChatBox(_gameChat);
_gameChat.DefaultChatFormat = "say \"{0}\"";
_gameChat.Input.PlaceHolder = _localization.GetString("Say something! [ for OOC");
}
private enum TickerState
{
Unset = 0,
/// <summary>
/// The client is in the lobby.
/// </summary>
InLobby = 1,
/// <summary>
/// The client is NOT in the lobby.
/// Do not confuse this with the client session status.
/// </summary>
InGame = 2
}
}
}

View File

@@ -0,0 +1,34 @@
using Robust.Client.Graphics.Drawing;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Graphics.Shaders;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
namespace Content.Client.Graphics.Overlays
{
public class CircleMaskOverlay : Overlay
{
#pragma warning disable 649
[Dependency] private readonly IPrototypeManager _prototypeManager;
[Dependency] private readonly IEyeManager _eyeManager;
#pragma warning restore 649
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public CircleMaskOverlay() : base(nameof(CircleMaskOverlay))
{
IoCManager.InjectDependencies(this);
Shader = _prototypeManager.Index<ShaderPrototype>("circlemask").Instance();
}
protected override void Draw(DrawingHandleBase handle)
{
var worldHandle = (DrawingHandleWorld)handle;
var viewport = _eyeManager.GetWorldViewport();
worldHandle.DrawRect(viewport, Color.White);
}
}
}

View File

@@ -0,0 +1,33 @@
using Robust.Client.Graphics.Drawing;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Graphics.Shaders;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
namespace Content.Client.Graphics.Overlays
{
public class GradientCircleMask : Overlay
{
#pragma warning disable 649
[Dependency] private readonly IPrototypeManager _prototypeManager;
[Dependency] private readonly IEyeManager _eyeManager;
#pragma warning restore 649
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public GradientCircleMask() : base(nameof(GradientCircleMask))
{
IoCManager.InjectDependencies(this);
Shader = _prototypeManager.Index<ShaderPrototype>("gradientcirclemask").Instance();
}
protected override void Draw(DrawingHandleBase handle)
{
var worldHandle = (DrawingHandleWorld)handle;
var viewport = _eyeManager.GetWorldViewport();
worldHandle.DrawRect(viewport, Color.White);
}
}
}

View File

@@ -0,0 +1,42 @@
using Content.Shared.Input;
using Robust.Shared.Input;
namespace Content.Client.Input
{
/// <summary>
/// Contains a helper function for setting up all content
/// contexts, and modifying existing engine ones.
/// </summary>
public static class ContentContexts
{
public static void SetupContexts(IInputContextContainer contexts)
{
var common = contexts.GetContext("common");
common.AddFunction(ContentKeyFunctions.FocusChat);
common.AddFunction(ContentKeyFunctions.ExamineEntity);
common.AddFunction(ContentKeyFunctions.OpenTutorial);
var human = contexts.GetContext("human");
human.AddFunction(ContentKeyFunctions.SwapHands);
human.AddFunction(ContentKeyFunctions.Drop);
human.AddFunction(ContentKeyFunctions.ActivateItemInHand);
human.AddFunction(ContentKeyFunctions.OpenCharacterMenu);
human.AddFunction(ContentKeyFunctions.UseItemInHand);
human.AddFunction(ContentKeyFunctions.ActivateItemInWorld);
human.AddFunction(ContentKeyFunctions.ThrowItemInHand);
human.AddFunction(ContentKeyFunctions.OpenContextMenu);
human.AddFunction(ContentKeyFunctions.OpenCraftingMenu);
human.AddFunction(ContentKeyFunctions.OpenInventoryMenu);
// Disabled until there is feedback, so hitting tab doesn't suddenly break interaction.
// human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
var ghost = contexts.New("ghost", "common");
ghost.AddFunction(EngineKeyFunctions.MoveUp);
ghost.AddFunction(EngineKeyFunctions.MoveDown);
ghost.AddFunction(EngineKeyFunctions.MoveLeft);
ghost.AddFunction(EngineKeyFunctions.MoveRight);
ghost.AddFunction(EngineKeyFunctions.Run);
ghost.AddFunction(ContentKeyFunctions.OpenContextMenu);
}
}
}

View File

@@ -0,0 +1,17 @@
using Content.Client.Chat;
using Robust.Client;
using Robust.Shared.GameObjects;
namespace Content.Client.Interfaces.Chat
{
public interface IChatManager
{
void Initialize();
void FrameUpdate(RenderFrameEventArgs delta);
void SetChatBox(ChatBox chatBox);
void RemoveSpeechBubble(EntityUid entityUid, SpeechBubble bubble);
}
}

View File

@@ -1,4 +1,4 @@
using SS14.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Client.Interfaces.GameObjects
{
@@ -8,7 +8,9 @@ namespace Content.Client.Interfaces.GameObjects
{
IEntity GetEntity(string index);
string ActiveIndex { get; }
IEntity ActiveHand { get; }
void SendChangeHand(string index);
void AttackByInHand(string index);
}
}

View File

@@ -0,0 +1,11 @@
using Content.Client.UserInterface;
using Robust.Client;
namespace Content.Client.Interfaces
{
public interface IClientGameTicker
{
void Initialize();
void FrameUpdate(RenderFrameEventArgs renderFrameEventArgs);
}
}

View File

@@ -0,0 +1,14 @@
using Content.Shared.Interfaces;
using Robust.Client;
using Robust.Shared.Map;
namespace Content.Client.Interfaces
{
public interface IClientNotifyManager : ISharedNotifyManager
{
void Initialize();
void PopupMessage(ScreenCoordinates coordinates, string message);
void PopupMessage(string message);
void FrameUpdate(RenderFrameEventArgs eventArgs);
}
}

View File

@@ -0,0 +1,12 @@
using System;
using Robust.Client.Graphics;
namespace Content.Client.Interfaces.Parallax
{
public interface IParallaxManager
{
event Action<Texture> OnTextureLoaded;
Texture ParallaxTexture { get; }
void LoadParallax();
}
}

View File

@@ -0,0 +1,418 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Nett;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
using Robust.Client.Utility;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Noise;
using BlendFactor = Robust.Shared.Maths.Color.BlendFactor;
namespace Content.Client.Parallax
{
public class ParallaxGenerator
{
private readonly List<Layer> Layers = new List<Layer>();
public static Image<Rgba32> GenerateParallax(TomlTable config, Size size, ISawmill sawmill)
{
sawmill.Debug("Generating parallax!");
var generator = new ParallaxGenerator();
generator._loadConfig(config);
var image = new Image<Rgba32>(Configuration.Default, size.Width, size.Height, Rgba32.Black);
var count = 0;
foreach (var layer in generator.Layers)
{
layer.Apply(image);
sawmill.Debug("Layer {0} done!", count++);
}
return image;
}
private void _loadConfig(TomlTable config)
{
foreach (var layerArray in config.Get<TomlTableArray>("layers").Items)
{
var layer = layerArray.Get<TomlTable>();
switch (layer.Get<string>("type"))
{
case "noise":
var layerNoise = new LayerNoise(layer);
Layers.Add(layerNoise);
break;
case "points":
var layerPoint = new LayerPoints(layer);
Layers.Add(layerPoint);
break;
default:
throw new NotSupportedException();
}
}
}
private abstract class Layer
{
public abstract void Apply(Image<Rgba32> bitmap);
}
private class LayerNoise : Layer
{
private readonly Color InnerColor = Color.White;
private readonly Color OuterColor = Color.Black;
private readonly NoiseGenerator.NoiseType NoiseType = NoiseGenerator.NoiseType.Fbm;
private readonly uint Seed = 1234;
private readonly double Persistence = 0.5;
private readonly double Lacunarity = Math.PI * 2 / 3;
private readonly double Frequency = 1;
private readonly uint Octaves = 3;
private readonly double Threshold;
private readonly double Power = 1;
private readonly BlendFactor SrcFactor = BlendFactor.One;
private readonly BlendFactor DstFactor = BlendFactor.One;
public LayerNoise(TomlTable table)
{
if (table.TryGetValue("innercolor", out var tomlObject))
{
InnerColor = Color.FromHex(tomlObject.Get<string>());
}
if (table.TryGetValue("outercolor", out tomlObject))
{
OuterColor = Color.FromHex(tomlObject.Get<string>());
}
if (table.TryGetValue("seed", out tomlObject))
{
Seed = (uint) tomlObject.Get<int>();
}
if (table.TryGetValue("persistence", out tomlObject))
{
Persistence = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
}
if (table.TryGetValue("lacunarity", out tomlObject))
{
Lacunarity = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
}
if (table.TryGetValue("frequency", out tomlObject))
{
Frequency = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
}
if (table.TryGetValue("octaves", out tomlObject))
{
Octaves = (uint) tomlObject.Get<int>();
}
if (table.TryGetValue("threshold", out tomlObject))
{
Threshold = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
}
if (table.TryGetValue("sourcefactor", out tomlObject))
{
SrcFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get<string>());
}
if (table.TryGetValue("destfactor", out tomlObject))
{
DstFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get<string>());
}
if (table.TryGetValue("power", out tomlObject))
{
Power = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
}
if (table.TryGetValue("noise_type", out tomlObject))
{
switch (tomlObject.Get<string>())
{
case "fbm":
NoiseType = NoiseGenerator.NoiseType.Fbm;
break;
case "ridged":
NoiseType = NoiseGenerator.NoiseType.Ridged;
break;
default:
throw new InvalidOperationException();
}
}
}
public override void Apply(Image<Rgba32> bitmap)
{
var noise = new NoiseGenerator(NoiseType);
noise.SetSeed(Seed);
noise.SetFrequency(Frequency);
noise.SetPersistence(Persistence);
noise.SetLacunarity(Lacunarity);
noise.SetOctaves(Octaves);
noise.SetPeriodX(bitmap.Width);
noise.SetPeriodY(bitmap.Height);
var threshVal = 1 / (1 - Threshold);
var powFactor = 1 / Power;
for (var x = 0; x < bitmap.Width; x++)
{
for (var y = 0; y < bitmap.Height; y++)
{
// Do noise calculations.
var noiseVal = Math.Min(1, Math.Max(0, (noise.GetNoiseTiled(x, y) + 1) / 2));
// Threshold
noiseVal = Math.Max(0, noiseVal - Threshold);
noiseVal *= threshVal;
noiseVal = Math.Pow(noiseVal, powFactor);
// Get colors based on noise values.
var srcColor = Color.InterpolateBetween(InnerColor, OuterColor, (float) noiseVal)
.WithAlpha((float) noiseVal);
// Apply blending factors & write back.
var dstColor = bitmap[x, y].ConvertImgSharp();
bitmap[x, y] = Color.Blend(dstColor, srcColor, DstFactor, SrcFactor).ConvertImgSharp();
}
}
}
}
private class LayerPoints : Layer
{
private readonly int Seed = 1234;
private readonly int PointCount = 100;
private readonly Color CloseColor = Color.White;
private readonly Color FarColor = Color.Black;
private readonly BlendFactor SrcFactor = BlendFactor.One;
private readonly BlendFactor DstFactor = BlendFactor.One;
// Noise mask stuff.
private readonly bool Masked;
private readonly NoiseGenerator.NoiseType MaskNoiseType = NoiseGenerator.NoiseType.Fbm;
private readonly uint MaskSeed = 1234;
private readonly double MaskPersistence = 0.5;
private readonly double MaskLacunarity = Math.PI * 2 / 3;
private readonly double MaskFrequency = 1;
private readonly uint MaskOctaves = 3;
private readonly double MaskThreshold;
private readonly int PointSize = 1;
private readonly double MaskPower = 1;
public LayerPoints(TomlTable table)
{
if (table.TryGetValue("seed", out var tomlObject))
{
Seed = tomlObject.Get<int>();
}
if (table.TryGetValue("count", out tomlObject))
{
PointCount = tomlObject.Get<int>();
}
if (table.TryGetValue("sourcefactor", out tomlObject))
{
SrcFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get<string>());
}
if (table.TryGetValue("destfactor", out tomlObject))
{
DstFactor = (BlendFactor) Enum.Parse(typeof(BlendFactor), tomlObject.Get<string>());
}
if (table.TryGetValue("farcolor", out tomlObject))
{
FarColor = Color.FromHex(tomlObject.Get<string>());
}
if (table.TryGetValue("closecolor", out tomlObject))
{
CloseColor = Color.FromHex(tomlObject.Get<string>());
}
if (table.TryGetValue("pointsize", out tomlObject))
{
PointSize = tomlObject.Get<int>();
}
// Noise mask stuff.
if (table.TryGetValue("mask", out tomlObject))
{
Masked = tomlObject.Get<bool>();
}
if (table.TryGetValue("maskseed", out tomlObject))
{
MaskSeed = (uint) tomlObject.Get<int>();
}
if (table.TryGetValue("maskpersistence", out tomlObject))
{
MaskPersistence = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
}
if (table.TryGetValue("masklacunarity", out tomlObject))
{
MaskLacunarity = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
}
if (table.TryGetValue("maskfrequency", out tomlObject))
{
MaskFrequency = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
}
if (table.TryGetValue("maskoctaves", out tomlObject))
{
MaskOctaves = (uint) tomlObject.Get<int>();
}
if (table.TryGetValue("maskthreshold", out tomlObject))
{
MaskThreshold = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
}
if (table.TryGetValue("masknoise_type", out tomlObject))
{
switch (tomlObject.Get<string>())
{
case "fbm":
MaskNoiseType = NoiseGenerator.NoiseType.Fbm;
break;
case "ridged":
MaskNoiseType = NoiseGenerator.NoiseType.Ridged;
break;
default:
throw new InvalidOperationException();
}
}
if (table.TryGetValue("maskpower", out tomlObject))
{
MaskPower = double.Parse(tomlObject.Get<string>(), CultureInfo.InvariantCulture);
}
}
public override void Apply(Image<Rgba32> bitmap)
{
// Temporary buffer so we don't mess up blending.
var buffer = new Image<Rgba32>(Configuration.Default, bitmap.Width, bitmap.Height, Rgba32.Black);
if (Masked)
{
GenPointsMasked(buffer);
}
else
{
GenPoints(buffer);
}
for (var x = 0; x < bitmap.Width; x++)
{
for (var y = 0; y < bitmap.Height; y++)
{
var dstColor = bitmap[x, y].ConvertImgSharp();
var srcColor = buffer[x, y].ConvertImgSharp();
bitmap[x, y] = Color.Blend(dstColor, srcColor, DstFactor, SrcFactor).ConvertImgSharp();
}
}
}
private void GenPoints(Image<Rgba32> buffer)
{
var o = PointSize - 1;
var random = new Random(Seed);
for (var i = 0; i < PointCount; i++)
{
var relX = random.NextDouble();
var relY = random.NextDouble();
var x = (int) (relX * buffer.Width);
var y = (int) (relY * buffer.Height);
var dist = random.NextDouble();
for (var ox = x - o; ox <= x + o; ox++)
{
for (var oy = y - o; oy <= y + o; oy++)
{
var color = Color.InterpolateBetween(FarColor, CloseColor, (float) dist).ConvertImgSharp();
buffer[MathHelper.Mod(ox, buffer.Width), MathHelper.Mod(oy, buffer.Width)] = color;
}
}
}
}
void GenPointsMasked(Image<Rgba32> buffer)
{
var o = PointSize - 1;
var random = new Random(Seed);
var noise = new NoiseGenerator(MaskNoiseType);
noise.SetSeed(MaskSeed);
noise.SetFrequency(MaskFrequency);
noise.SetPersistence(MaskPersistence);
noise.SetLacunarity(MaskLacunarity);
noise.SetOctaves(MaskOctaves);
noise.SetPeriodX(buffer.Width);
noise.SetPeriodY(buffer.Height);
var threshVal = 1 / (1 - MaskThreshold);
var powFactor = 1 / MaskPower;
const int maxPointAttemptCount = 9999;
var pointAttemptCount = 0;
for (var i = 0; i < PointCount; i++)
{
var relX = random.NextDouble();
var relY = random.NextDouble();
var x = (int) (relX * buffer.Width);
var y = (int) (relY * buffer.Height);
// Grab noise at this point.
var noiseVal = Math.Min(1, Math.Max(0, (noise.GetNoiseTiled(x, y) + 1) / 2));
// Threshold
noiseVal = Math.Max(0, noiseVal - MaskThreshold);
noiseVal *= threshVal;
noiseVal = Math.Pow(noiseVal, powFactor);
var randomThresh = random.NextDouble();
if (randomThresh > noiseVal)
{
if (++pointAttemptCount <= maxPointAttemptCount)
{
i--;
}
continue;
}
var dist = random.NextDouble();
for (var ox = x - o; ox <= x + o; ox++)
{
for (var oy = y - o; oy <= y + o; oy++)
{
var color = Color.InterpolateBetween(FarColor, CloseColor, (float) dist).ConvertImgSharp();
buffer[MathHelper.Mod(ox, buffer.Width), MathHelper.Mod(oy, buffer.Height)] = color;
}
}
}
}
}
}
}

View File

@@ -0,0 +1,113 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Content.Client.Interfaces.Parallax;
using Nett;
using SixLabors.ImageSharp;
using SixLabors.Primitives;
using Robust.Client.Graphics;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Utility;
namespace Content.Client.Parallax
{
internal sealed class ParallaxManager : IParallaxManager, IPostInjectInit
{
#pragma warning disable 649
[Dependency] private readonly IResourceCache _resourceCache;
[Dependency] private readonly ILogManager _logManager;
[Dependency] private readonly IConfigurationManager _configurationManager;
#pragma warning restore 649
private static readonly ResourcePath ParallaxConfigPath = new ResourcePath("/parallax_config.toml");
// Both of these below are in the user directory.
private static readonly ResourcePath ParallaxPath = new ResourcePath("/parallax_cache.png");
private static readonly ResourcePath ParallaxConfigOld = new ResourcePath("/parallax_config_old");
public event Action<Texture> OnTextureLoaded;
public Texture ParallaxTexture { get; private set; }
public async void LoadParallax()
{
if (!_configurationManager.GetCVar<bool>("parallax.enabled"))
{
return;
}
MemoryStream configStream = null;
string contents;
TomlTable table;
try
{
// Load normal config into memory
if (!_resourceCache.TryContentFileRead(ParallaxConfigPath, out configStream))
{
Logger.ErrorS("parallax", "Parallax config not found.");
return;
}
using (var reader = new StreamReader(configStream, EncodingHelpers.UTF8))
{
contents = reader.ReadToEnd();
}
if (_resourceCache.UserData.Exists(ParallaxConfigOld))
{
bool match;
using (var data = _resourceCache.UserData.Open(ParallaxConfigOld, FileMode.Open))
using (var reader = new StreamReader(data, EncodingHelpers.UTF8))
{
match = reader.ReadToEnd() == contents;
}
if (match)
{
using (var stream = _resourceCache.UserData.Open(ParallaxPath, FileMode.Open))
{
ParallaxTexture = Texture.LoadFromPNGStream(stream, "Parallax");
}
OnTextureLoaded?.Invoke(ParallaxTexture);
return;
}
}
table = Toml.ReadString(contents);
}
finally
{
configStream?.Dispose();
}
var sawmill = _logManager.GetSawmill("parallax");
// Generate the parallax in the thread pool.
var image = await Task.Run(() => ParallaxGenerator.GenerateParallax(table, new Size(1920, 1080), sawmill));
// And load it in the main thread for safety reasons.
ParallaxTexture = Texture.LoadFromImage(image, "Parallax");
// Store it and CRC so further game starts don't need to regenerate it.
using (var stream = _resourceCache.UserData.Open(ParallaxPath, FileMode.Create))
{
image.SaveAsPng(stream);
}
using (var stream = _resourceCache.UserData.Open(ParallaxConfigOld, FileMode.Create))
using (var writer = new StreamWriter(stream, EncodingHelpers.UTF8))
{
writer.Write(contents);
}
OnTextureLoaded?.Invoke(ParallaxTexture);
}
public void PostInject()
{
_configurationManager.RegisterCVar("parallax.enabled", true);
}
}
}

View File

@@ -0,0 +1,67 @@
using Content.Client.Interfaces.Parallax;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Graphics.Overlays;
using Robust.Client.Graphics.Shaders;
using Robust.Client.Interfaces.Graphics.ClientEye;
using Robust.Client.Interfaces.Graphics.Overlays;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
namespace Content.Client.Parallax
{
public class ParallaxOverlay : Overlay
{
#pragma warning disable 649
[Dependency] private readonly IParallaxManager _parallaxManager;
[Dependency] private readonly IEyeManager _eyeManager;
[Dependency] private readonly IPrototypeManager _prototypeManager;
[Dependency] private readonly IMapManager _mapManager;
#pragma warning restore 649
public override bool AlwaysDirty => true;
private const float Slowness = 0.5f;
private Texture _parallaxTexture;
public override OverlaySpace Space => OverlaySpace.ScreenSpaceBelowWorld;
public ParallaxOverlay() : base(nameof(ParallaxOverlay))
{
IoCManager.InjectDependencies(this);
Shader = _prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
if (_parallaxManager.ParallaxTexture == null)
{
_parallaxManager.OnTextureLoaded += texture => _parallaxTexture = texture;
}
else
{
_parallaxTexture = _parallaxManager.ParallaxTexture;
}
}
protected override void Draw(DrawingHandleBase handle)
{
if (_parallaxTexture == null)
{
return;
}
var screenHandle = (DrawingHandleScreen) handle;
var (sizeX, sizeY) = _parallaxTexture.Size;
var (posX, posY) = _eyeManager.ScreenToWorld(Vector2.Zero).ToWorld(_mapManager).Position;
var (ox, oy) = (Vector2i) new Vector2(-posX / Slowness, posY / Slowness);
ox = MathHelper.Mod(ox, sizeX);
oy = MathHelper.Mod(oy, sizeY);
screenHandle.DrawTexture(_parallaxTexture, new Vector2(ox, oy));
screenHandle.DrawTexture(_parallaxTexture, new Vector2(ox - sizeX, oy));
screenHandle.DrawTexture(_parallaxTexture, new Vector2(ox, oy - sizeY));
screenHandle.DrawTexture(_parallaxTexture, new Vector2(ox - sizeX, oy - sizeY));
}
}
}

View File

@@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Content.Client")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Content.Client")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("a2e5f175-78af-4ddd-8f97-e2d2552372ed")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,251 @@
using System.Collections.Generic;
using Content.Client.GameObjects.Components.Research;
using Content.Shared.Materials;
using Content.Shared.Research;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.Utility;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Timers;
using Robust.Shared.Utility;
namespace Content.Client.Research
{
public class LatheMenu : SS14Window
{
#pragma warning disable CS0649
[Dependency]
private IPrototypeManager PrototypeManager;
#pragma warning restore
private ItemList Items;
private ItemList Materials;
private LineEdit AmountLineEdit;
private LineEdit SearchBar;
public Button QueueButton;
protected override Vector2? CustomSize => (300, 450);
public LatheBoundUserInterface Owner { get; set; }
private List<LatheRecipePrototype> _recipes = new List<LatheRecipePrototype>();
private List<LatheRecipePrototype> _shownRecipes = new List<LatheRecipePrototype>();
public LatheMenu()
{
}
public LatheMenu(string name) : base(name)
{
}
protected override void Initialize()
{
base.Initialize();
IoCManager.InjectDependencies(this);
Title = "Lathe Menu";
var margin = new MarginContainer()
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.FillExpand,
MarginTop = 5f,
MarginLeft = 5f,
MarginRight = -5f,
MarginBottom = -5f,
};
margin.SetAnchorAndMarginPreset(LayoutPreset.Wide);
var vbox = new VBoxContainer()
{
SizeFlagsVertical = SizeFlags.FillExpand,
SeparationOverride = 5,
};
vbox.SetAnchorAndMarginPreset(LayoutPreset.Wide);
var hboxButtons = new HBoxContainer()
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 1,
};
QueueButton = new Button()
{
Text = "Queue",
TextAlign = Button.AlignMode.Center,
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 1,
};
var spacer = new Control()
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 3,
};
spacer.SetAnchorAndMarginPreset(LayoutPreset.Wide);
var hboxFilter = new HBoxContainer()
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 1
};
SearchBar = new LineEdit()
{
PlaceHolder = "Search Designs",
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 3
};
SearchBar.OnTextChanged += Populate;
var filterButton = new Button()
{
Text = "Filter",
TextAlign = Button.AlignMode.Center,
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 1,
Disabled = true,
};
Items = new ItemList()
{
SizeFlagsStretchRatio = 8,
SizeFlagsVertical = SizeFlags.FillExpand,
};
Items.OnItemSelected += ItemSelected;
AmountLineEdit = new LineEdit()
{
PlaceHolder = "Amount",
Text = "1",
SizeFlagsHorizontal = SizeFlags.FillExpand,
};
AmountLineEdit.OnTextChanged += PopulateDisabled;
Materials = new ItemList()
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 3
};
hboxButtons.AddChild(spacer);
hboxButtons.AddChild(QueueButton);
hboxFilter.AddChild(SearchBar);
hboxFilter.AddChild(filterButton);
vbox.AddChild(hboxButtons);
vbox.AddChild(hboxFilter);
vbox.AddChild(Items);
vbox.AddChild(AmountLineEdit);
vbox.AddChild(Materials);
margin.AddChild(vbox);
Contents.AddChild(margin);
}
public void ItemSelected(ItemList.ItemListSelectedEventArgs args)
{
int.TryParse(AmountLineEdit.Text, out var quantity);
if (quantity <= 0) quantity = 1;
Owner.Queue(_shownRecipes[args.ItemIndex], quantity);
Items.SelectMode = ItemList.ItemListSelectMode.None;
Timer.Spawn(100, () =>
{
Items.Unselect(args.ItemIndex);
Items.SelectMode = ItemList.ItemListSelectMode.Single;
});
}
public void PopulateMaterials()
{
Materials.Clear();
foreach (var (id, amount) in Owner.Storage)
{
if (!PrototypeManager.TryIndex(id, out MaterialPrototype materialPrototype)) continue;
var material = materialPrototype.Material;
Materials.AddItem($"{material.Name} {amount} cm³", material.Icon.Frame0(), false);
}
}
/// <summary>
/// Disables or enables shown recipes depending on whether there are enough materials for it or not.
/// </summary>
public void PopulateDisabled()
{
int.TryParse(AmountLineEdit.Text, out var quantity);
if (quantity <= 0) quantity = 1;
for (var i = 0; i < _shownRecipes.Count; i++)
{
var prototype = _shownRecipes[i];
Items.SetItemDisabled(i, !Owner.Lathe.CanProduce(prototype, quantity));
}
}
/// <inheritdoc cref="PopulateDisabled()"/>
public void PopulateDisabled(LineEdit.LineEditEventArgs args)
{
PopulateDisabled();
}
/// <summary>
/// Adds shown recipes to the ItemList control.
/// </summary>
public void PopulateList()
{
Items.Clear();
for (var i = 0; i < _shownRecipes.Count; i++)
{
var prototype = _shownRecipes[i];
Items.AddItem(prototype.Name, prototype.Icon.Frame0());
}
PopulateDisabled();
}
/// <summary>
/// Populates the list of recipes that will actually be shown, using the current filters.
/// </summary>
public void Populate()
{
_shownRecipes.Clear();
foreach (var prototype in Owner.Database)
{
if (SearchBar.Text.Trim().Length != 0)
{
if (prototype.Name.ToLowerInvariant().Contains(SearchBar.Text.Trim().ToLowerInvariant()))
_shownRecipes.Add(prototype);
continue;
}
_shownRecipes.Add(prototype);
}
PopulateList();
}
/// <inheritdoc cref="Populate"/>
public void Populate(LineEdit.LineEditEventArgs args)
{
Populate();
}
}
}

View File

@@ -0,0 +1,142 @@
using Content.Client.GameObjects.Components.Research;
using Content.Shared.Research;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.Utility;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.ViewVariables;
namespace Content.Client.Research
{
public class LatheQueueMenu : SS14Window
{
protected override Vector2? CustomSize => (300, 450);
public LatheBoundUserInterface Owner { get; set; }
[ViewVariables]
private ItemList QueueList;
private Label NameLabel;
private Label Description;
private TextureRect Icon;
protected override void Initialize()
{
base.Initialize();
Title = "Lathe Queue";
var margin = new MarginContainer()
{
MarginTop = 5f,
MarginLeft = 5f,
MarginRight = -5f,
MarginBottom = -5f,
};
margin.SetAnchorAndMarginPreset(LayoutPreset.Wide);
var vbox = new VBoxContainer();
vbox.SetAnchorAndMarginPreset(LayoutPreset.Wide);
var descMargin = new MarginContainer()
{
MarginTop = 5f,
MarginLeft = 5f,
MarginRight = -5f,
MarginBottom = -5f,
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 2,
};
var hbox = new HBoxContainer()
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
};
Icon = new TextureRect()
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 2,
};
var vboxInfo = new VBoxContainer()
{
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 3,
};
NameLabel = new Label()
{
RectClipContent = true,
SizeFlagsHorizontal = SizeFlags.Fill,
};
Description = new Label()
{
RectClipContent = true,
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsHorizontal = SizeFlags.Fill,
};
QueueList = new ItemList()
{
SizeFlagsHorizontal = SizeFlags.Fill,
SizeFlagsVertical = SizeFlags.FillExpand,
SizeFlagsStretchRatio = 3,
SelectMode = ItemList.ItemListSelectMode.None
};
vboxInfo.AddChild(NameLabel);
vboxInfo.AddChild(Description);
hbox.AddChild(Icon);
hbox.AddChild(vboxInfo);
descMargin.AddChild(hbox);
vbox.AddChild(descMargin);
vbox.AddChild(QueueList);
margin.AddChild(vbox);
Contents.AddChild(margin);
ClearInfo();
}
public void SetInfo(LatheRecipePrototype recipe)
{
Icon.Texture = recipe.Icon.Frame0();
if (recipe.Name != null)
NameLabel.Text = recipe.Name;
if (recipe.Description != null)
Description.Text = recipe.Description;
}
public void ClearInfo()
{
Icon.Texture = Texture.Transparent;
NameLabel.Text = "-------";
Description.Text = "Not producing anything.";
}
public void PopulateList()
{
QueueList.Clear();
var idx = 1;
foreach (var recipe in Owner.QueuedRecipes)
{
QueueList.AddItem($"{idx}. {recipe.Name}", recipe.Icon.Frame0(), false);
idx++;
}
}
}
}

View File

@@ -0,0 +1,114 @@
using Robust.Client.Console;
using Robust.Client.Interfaces.Graphics;
using Robust.Client.Interfaces.Placement;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Interfaces.Configuration;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
namespace Content.Client.UserInterface
{
internal sealed class EscapeMenu : SS14Window
{
private readonly IClientConsole _console;
private readonly ITileDefinitionManager __tileDefinitionManager;
private readonly IPlacementManager _placementManager;
private readonly IPrototypeManager _prototypeManager;
private readonly IResourceCache _resourceCache;
private readonly IConfigurationManager _configSystem;
private readonly ILocalizationManager _localizationManager;
private BaseButton QuitButton;
private BaseButton OptionsButton;
private BaseButton SpawnEntitiesButton;
private BaseButton SpawnTilesButton;
private OptionsMenu optionsMenu;
public EscapeMenu(IClientConsole console,
ITileDefinitionManager tileDefinitionManager,
IPlacementManager placementManager,
IPrototypeManager prototypeManager,
IResourceCache resourceCache,
IConfigurationManager configSystem, ILocalizationManager localizationManager)
{
_configSystem = configSystem;
_localizationManager = localizationManager;
_console = console;
__tileDefinitionManager = tileDefinitionManager;
_placementManager = placementManager;
_prototypeManager = prototypeManager;
_resourceCache = resourceCache;
PerformLayout();
}
private void PerformLayout()
{
optionsMenu = new OptionsMenu(_configSystem);
Resizable = false;
Title = "Menu";
var vBox = new VBoxContainer {SeparationOverride = 2};
Contents.AddChild(vBox);
SpawnEntitiesButton = new Button {Text = "Spawn Entities"};
SpawnEntitiesButton.OnPressed += OnSpawnEntitiesButtonClicked;
vBox.AddChild(SpawnEntitiesButton);
SpawnTilesButton = new Button {Text = "Spawn Tiles"};
SpawnTilesButton.OnPressed += OnSpawnTilesButtonClicked;
vBox.AddChild(SpawnTilesButton);
// Add a spacer.
vBox.AddChild(new Control { CustomMinimumSize = (0, 5)});
OptionsButton = new Button {Text = "Options"};
OptionsButton.OnPressed += OnOptionsButtonClicked;
vBox.AddChild(OptionsButton);
QuitButton = new Button {Text = "Quit"};
QuitButton.OnPressed += OnQuitButtonClicked;
vBox.AddChild(QuitButton);
Size = CombinedMinimumSize;
}
private void OnQuitButtonClicked(BaseButton.ButtonEventArgs args)
{
_console.ProcessCommand("disconnect");
Dispose();
}
private void OnOptionsButtonClicked(BaseButton.ButtonEventArgs args)
{
optionsMenu.OpenCentered();
}
private void OnSpawnEntitiesButtonClicked(BaseButton.ButtonEventArgs args)
{
var window = new EntitySpawnWindow(_placementManager, _prototypeManager, _resourceCache, _localizationManager);
window.OpenToLeft();
}
private void OnSpawnTilesButtonClicked(BaseButton.ButtonEventArgs args)
{
var window = new TileSpawnWindow(__tileDefinitionManager, _placementManager, _resourceCache);
window.OpenToLeft();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
optionsMenu.Dispose();
}
}
}
}

View File

@@ -0,0 +1,391 @@
using System;
using Content.Client.Utility;
using Content.Shared.Input;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.ResourceManagement;
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;
namespace Content.Client.UserInterface
{
/// <summary>
/// Responsible for laying out the default game HUD.
/// </summary>
public interface IGameHud
{
Control RootControl { get; }
// Escape top button.
bool EscapeButtonDown { get; set; }
Action<bool> EscapeButtonToggled { get; set; }
// Character top button.
bool CharacterButtonDown { get; set; }
bool CharacterButtonVisible { get; set; }
Action<bool> CharacterButtonToggled { get; set; }
// Inventory top button.
bool InventoryButtonDown { get; set; }
bool InventoryButtonVisible { get; set; }
Action<bool> InventoryButtonToggled { get; set; }
// Crafting top button.
bool CraftingButtonDown { get; set; }
bool CraftingButtonVisible { get; set; }
Action<bool> CraftingButtonToggled { get; set; }
// Sandbox top button.
bool SandboxButtonDown { get; set; }
bool SandboxButtonVisible { get; set; }
Action<bool> SandboxButtonToggled { get; set; }
Control HandsContainer { get; }
Control InventoryQuickButtonContainer { get; }
// Init logic.
void Initialize();
}
internal sealed class GameHud : IGameHud
{
private HBoxContainer _topButtonsContainer;
private TopButton _buttonEscapeMenu;
private TopButton _buttonTutorial;
private TopButton _buttonCharacterMenu;
private TopButton _buttonInventoryMenu;
private TopButton _buttonCraftingMenu;
private TopButton _buttonSandboxMenu;
private TutorialWindow _tutorialWindow;
#pragma warning disable 649
[Dependency] private readonly IResourceCache _resourceCache;
[Dependency] private readonly ILocalizationManager _loc;
[Dependency] private readonly IInputManager _inputManager;
#pragma warning restore 649
public Control HandsContainer { get; private set; }
public Control InventoryQuickButtonContainer { get; private set; }
public void Initialize()
{
RootControl = new Control {MouseFilter = Control.MouseFilterMode.Ignore};
RootControl.SetAnchorPreset(Control.LayoutPreset.Wide);
var escapeTexture = _resourceCache.GetTexture("/Textures/UserInterface/hamburger.svg.96dpi.png");
var characterTexture = _resourceCache.GetTexture("/Textures/UserInterface/character.svg.96dpi.png");
var inventoryTexture = _resourceCache.GetTexture("/Textures/UserInterface/inventory.svg.96dpi.png");
var craftingTexture = _resourceCache.GetTexture("/Textures/UserInterface/hammer.svg.96dpi.png");
var tutorialTexture = _resourceCache.GetTexture("/Textures/UserInterface/students-cap.svg.96dpi.png");
var sandboxTexture = _resourceCache.GetTexture("/Textures/UserInterface/sandbox.svg.96dpi.png");
_topButtonsContainer = new HBoxContainer
{
SeparationOverride = 4
};
RootControl.AddChild(_topButtonsContainer);
_topButtonsContainer.SetAnchorAndMarginPreset(Control.LayoutPreset.TopLeft, margin: 10);
// TODO: Pull key names here from the actual key binding config.
// Escape
_buttonEscapeMenu = new TopButton(escapeTexture, "Esc")
{
ToolTip = _loc.GetString("Open escape menu.")
};
_topButtonsContainer.AddChild(_buttonEscapeMenu);
_buttonEscapeMenu.OnToggled += args => EscapeButtonToggled?.Invoke(args.Pressed);
// Tutorial
_buttonTutorial = new TopButton(tutorialTexture, "F1")
{
ToolTip = _loc.GetString("Open tutorial.")
};
_topButtonsContainer.AddChild(_buttonTutorial);
_buttonTutorial.OnToggled += a => ButtonTutorialOnOnToggled();
// Character
_buttonCharacterMenu = new TopButton(characterTexture, "C")
{
ToolTip = _loc.GetString("Open character menu."),
Visible = false
};
_topButtonsContainer.AddChild(_buttonCharacterMenu);
_buttonCharacterMenu.OnToggled += args => CharacterButtonToggled?.Invoke(args.Pressed);
// Inventory
_buttonInventoryMenu = new TopButton(inventoryTexture, "I")
{
ToolTip = _loc.GetString("Open inventory menu."),
Visible = false
};
_topButtonsContainer.AddChild(_buttonInventoryMenu);
_buttonInventoryMenu.OnToggled += args => InventoryButtonToggled?.Invoke(args.Pressed);
// Crafting
_buttonCraftingMenu = new TopButton(craftingTexture, "G")
{
ToolTip = _loc.GetString("Open crafting menu."),
Visible = false
};
_topButtonsContainer.AddChild(_buttonCraftingMenu);
_buttonCraftingMenu.OnToggled += args => CraftingButtonToggled?.Invoke(args.Pressed);
// Sandbox
_buttonSandboxMenu = new TopButton(sandboxTexture, "B")
{
ToolTip = _loc.GetString("Open sandbox menu."),
Visible = true
};
_topButtonsContainer.AddChild(_buttonSandboxMenu);
_buttonSandboxMenu.OnToggled += args => SandboxButtonToggled?.Invoke(args.Pressed);
_tutorialWindow = new TutorialWindow();
_tutorialWindow.OnClose += () => _buttonTutorial.Pressed = false;
_inputManager.SetInputCommand(ContentKeyFunctions.OpenTutorial,
InputCmdHandler.FromDelegate(s => ButtonTutorialOnOnToggled()));
var inventoryContainer = new HBoxContainer
{
GrowHorizontal = Control.GrowDirection.Begin,
GrowVertical = Control.GrowDirection.Begin,
SeparationOverride = 10
};
RootControl.AddChild(inventoryContainer);
inventoryContainer.SetAnchorAndMarginPreset(Control.LayoutPreset.BottomRight);
InventoryQuickButtonContainer = new MarginContainer
{
GrowHorizontal = Control.GrowDirection.Begin,
GrowVertical = Control.GrowDirection.Begin,
};
HandsContainer = new MarginContainer
{
GrowHorizontal = Control.GrowDirection.Both,
GrowVertical = Control.GrowDirection.Begin
};
inventoryContainer.Children.Add(HandsContainer);
inventoryContainer.Children.Add(InventoryQuickButtonContainer);
}
private void ButtonTutorialOnOnToggled()
{
if (_tutorialWindow.IsOpen)
{
if (!_tutorialWindow.IsAtFront())
{
_tutorialWindow.MoveToFront();
_buttonTutorial.Pressed = true;
}
else
{
_tutorialWindow.Close();
_buttonTutorial.Pressed = false;
}
}
else
{
_tutorialWindow.OpenCentered();
_buttonTutorial.Pressed = true;
}
}
public Control RootControl { get; private set; }
public bool EscapeButtonDown
{
get => _buttonEscapeMenu.Pressed;
set => _buttonEscapeMenu.Pressed = value;
}
public Action<bool> EscapeButtonToggled { get; set; }
public bool CharacterButtonDown
{
get => _buttonCharacterMenu.Pressed;
set => _buttonCharacterMenu.Pressed = value;
}
public bool CharacterButtonVisible
{
get => _buttonCharacterMenu.Visible;
set => _buttonCharacterMenu.Visible = value;
}
public Action<bool> CharacterButtonToggled { get; set; }
public bool InventoryButtonDown
{
get => _buttonInventoryMenu.Pressed;
set => _buttonInventoryMenu.Pressed = value;
}
public bool InventoryButtonVisible
{
get => _buttonInventoryMenu.Visible;
set => _buttonInventoryMenu.Visible = value;
}
public Action<bool> InventoryButtonToggled { get; set; }
public bool CraftingButtonDown
{
get => _buttonCraftingMenu.Pressed;
set => _buttonCraftingMenu.Pressed = value;
}
public bool CraftingButtonVisible
{
get => _buttonCraftingMenu.Visible;
set => _buttonCraftingMenu.Visible = value;
}
public Action<bool> CraftingButtonToggled { get; set; }
public bool SandboxButtonDown
{
get => _buttonSandboxMenu.Pressed;
set => _buttonSandboxMenu.Pressed = value;
}
public bool SandboxButtonVisible
{
get => _buttonSandboxMenu.Visible;
set => _buttonSandboxMenu.Visible = value;
}
public Action<bool> SandboxButtonToggled { get; set; }
public sealed class TopButton : BaseButton
{
public const string StyleClassLabelTopButton = "topButtonLabel";
private static readonly Color ColorNormal = Color.FromHex("#7b7e9e");
private static readonly Color ColorHovered = Color.FromHex("#9699bb");
private static readonly Color ColorPressed = Color.FromHex("#789B8C");
private readonly VBoxContainer _container;
private readonly TextureRect _textureRect;
private readonly Label _label;
public TopButton(Texture texture, string keyName)
{
ToggleMode = true;
_container = new VBoxContainer {MouseFilter = MouseFilterMode.Ignore};
AddChild(_container);
_container.AddChild(_textureRect = new TextureRect
{
Texture = texture,
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
SizeFlagsVertical = SizeFlags.Expand | SizeFlags.ShrinkCenter,
MouseFilter = MouseFilterMode.Ignore,
ModulateSelfOverride = ColorNormal,
CustomMinimumSize = (0, 32),
Stretch = TextureRect.StretchMode.KeepCentered
});
_container.AddChild(_label = new Label
{
Text = keyName,
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
MouseFilter = MouseFilterMode.Ignore,
ModulateSelfOverride = ColorNormal
});
_label.AddStyleClass(StyleClassLabelTopButton);
_container.SetAnchorAndMarginPreset(LayoutPreset.Wide);
DrawModeChanged();
}
protected override Vector2 CalculateMinimumSize()
{
var styleSize = ActualStyleBox?.MinimumSize ?? Vector2.Zero;
return (0, 4) + styleSize + _container?.CombinedMinimumSize ?? Vector2.Zero;
}
protected override void Draw(DrawingHandleScreen handle)
{
ActualStyleBox?.Draw(handle, PixelSizeBox);
}
private StyleBox ActualStyleBox
{
get
{
TryGetStyleProperty(Button.StylePropertyStyleBox, out StyleBox ret);
return ret;
}
}
protected override void DrawModeChanged()
{
switch (DrawMode)
{
case DrawModeEnum.Normal:
StylePseudoClass = Button.StylePseudoClassNormal;
_textureRect.ModulateSelfOverride = ColorNormal;
_label.ModulateSelfOverride = ColorNormal;
break;
case DrawModeEnum.Pressed:
StylePseudoClass = Button.StylePseudoClassPressed;
_textureRect.ModulateSelfOverride = ColorPressed;
_label.ModulateSelfOverride = ColorPressed;
break;
case DrawModeEnum.Hover:
StylePseudoClass = Button.StylePseudoClassHover;
_textureRect.ModulateSelfOverride = ColorHovered;
_label.ModulateSelfOverride = ColorHovered;
break;
case DrawModeEnum.Disabled:
break;
}
}
protected override void StylePropertiesChanged()
{
base.StylePropertiesChanged();
if (_container == null)
{
return;
}
var box = ActualStyleBox ?? new StyleBoxEmpty();
_container.MarginLeft = box.GetContentMargin(StyleBox.Margin.Left);
_container.MarginRight = -box.GetContentMargin(StyleBox.Margin.Right);
_container.MarginTop = box.GetContentMargin(StyleBox.Margin.Top) + 4;
_container.MarginBottom = -box.GetContentMargin(StyleBox.Margin.Bottom);
}
}
}
}

View File

@@ -1,95 +1,109 @@
using Content.Client.GameObjects;
using Content.Client.GameObjects.EntitySystems;
using Content.Client.Interfaces.GameObjects;
using SS14.Client.GameObjects;
using SS14.Client.Graphics;
using SS14.Client.Graphics.Drawing;
using SS14.Client.Input;
using SS14.Client.Interfaces.Player;
using SS14.Client.Interfaces.ResourceManagement;
using SS14.Client.Interfaces.UserInterface;
using SS14.Client.ResourceManagement;
using SS14.Client.UserInterface;
using SS14.Client.UserInterface.Controls;
using SS14.Shared.Interfaces.GameObjects;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using SS14.Shared.Maths;
using Content.Client.Utility;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Content.Client.UserInterface
{
public class HandsGui : Control
{
private static readonly Color _inactiveColor = new Color(90, 90, 90);
private const int BOX_SPACING = 1;
// The boxes are square so that's both width and height.
private const int BOX_SIZE = 50;
private const int BoxSpacing = 0;
private const int BoxSize = 64;
private readonly IPlayerManager _playerManager = IoCManager.Resolve<IPlayerManager>();
private readonly IUserInterfaceManager _userInterfaceManager = IoCManager.Resolve<IUserInterfaceManager>();
private StyleBoxTexture handBox;
private StyleBoxTexture inactiveHandBox;
#pragma warning disable 0649
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IResourceCache _resourceCache;
[Dependency] private readonly ILocalizationManager _loc;
#pragma warning restore 0649
private UiHandInfo LeftHand;
private UiHandInfo RightHand;
private Box2i handL;
private Box2i handR;
private Texture TextureHandLeft;
private Texture TextureHandRight;
private Texture TextureHandActive;
private IEntity LeftHand;
private IEntity RightHand;
private UIBox2i _handL;
private UIBox2i _handR;
private SpriteView LeftSpriteView;
private SpriteView RightSpriteView;
private TextureRect ActiveHandRect;
protected override void Initialize()
{
base.Initialize();
var _resMgr = IoCManager.Resolve<IResourceCache>();
var handsBoxTexture = _resMgr.GetResource<TextureResource>("/Textures/UserInterface/handsbox.png");
handBox = new StyleBoxTexture()
{
Texture = handsBoxTexture,
};
handBox.SetMargin(StyleBox.Margin.All, 6);
inactiveHandBox = new StyleBoxTexture(handBox)
{
Modulate = _inactiveColor,
};
SetMarginsPreset(LayoutPreset.CenterBottom);
SetAnchorPreset(LayoutPreset.CenterBottom);
IoCManager.InjectDependencies(this);
ToolTip = _loc.GetString("Your hands");
_handR = new UIBox2i(0, 0, BoxSize, BoxSize);
_handL = _handR.Translated((BoxSize + BoxSpacing, 0));
handL = new Box2i(0, 0, BOX_SIZE, BOX_SIZE);
handR = handL.Translated(new Vector2i(BOX_SIZE + BOX_SPACING, 0));
SS14.Shared.Log.Logger.Debug($"{handL}, {handR}");
MouseFilter = MouseFilterMode.Stop;
TextureHandLeft = _resourceCache.GetTexture("/Textures/UserInterface/Inventory/hand_l.png");
TextureHandRight = _resourceCache.GetTexture("/Textures/UserInterface/Inventory/hand_r.png");
TextureHandActive = _resourceCache.GetTexture("/Textures/UserInterface/Inventory/hand_active.png");
AddChild(new TextureRect
{
Texture = TextureHandLeft,
Size = _handL.Size,
Position = _handL.TopLeft,
TextureScale = (2, 2)
});
AddChild(new TextureRect
{
Texture = TextureHandRight,
Size = _handR.Size,
Position = _handR.TopLeft,
TextureScale = (2, 2)
});
AddChild(ActiveHandRect = new TextureRect
{
Texture = TextureHandActive,
Size = _handL.Size,
Position = _handL.TopLeft,
TextureScale = (2, 2)
});
LeftSpriteView = new SpriteView
{
MouseFilter = MouseFilterMode.Ignore,
Scale = (2, 2)
};
AddChild(LeftSpriteView);
LeftSpriteView.Size = _handL.Size;
LeftSpriteView.Position = _handL.TopLeft;
RightSpriteView = new SpriteView
{
MouseFilter = MouseFilterMode.Ignore,
Scale = (2, 2)
};
AddChild(RightSpriteView);
RightSpriteView.Size = _handR.Size;
RightSpriteView.Position = _handR.TopLeft;
}
protected override Vector2 CalculateMinimumSize()
{
return new Vector2(BOX_SIZE * 2 + 1, BOX_SIZE);
}
protected override void Draw(DrawingHandle handle)
{
if (!TryGetHands(out IHandsComponent hands))
return;
var leftActive = hands.ActiveIndex == "left";
handle.DrawStyleBox(handBox, leftActive ? handL : handR);
handle.DrawStyleBox(inactiveHandBox, leftActive ? handR : handL);
if (LeftHand.Entity != null && LeftHand.HeldSprite != null)
{
var bounds = LeftHand.HeldSprite.Size;
handle.DrawTextureRect(LeftHand.HeldSprite,
Box2i.FromDimensions(handL.Left + (int)(handL.Width / 2f - bounds.X / 2f),
handL.Top + (int)(handL.Height / 2f - bounds.Y / 2f),
(int)bounds.X, (int)bounds.Y), tile: false);
}
if (RightHand.Entity != null && RightHand.HeldSprite != null)
{
var bounds = RightHand.HeldSprite.Size;
handle.DrawTextureRect(RightHand.HeldSprite,
Box2i.FromDimensions(handR.Left + (int)(handR.Width / 2f - bounds.Y / 2f),
handR.Top + (int)(handR.Height / 2f - bounds.Y / 2f),
(int)bounds.X, (int)bounds.Y), tile: false);
}
return new Vector2(BoxSize * 2 + BoxSpacing, BoxSize) * UIScale;
}
/// <summary>
@@ -99,57 +113,58 @@ namespace Content.Client.UserInterface
/// <returns></returns>
private bool TryGetHands(out IHandsComponent hands)
{
hands = null;
if (_playerManager?.LocalPlayer == null)
{
return false;
}
hands = default;
IEntity entity = _playerManager.LocalPlayer.ControlledEntity;
if (entity == null || !entity.TryGetComponent(out hands))
{
return false;
}
return true;
var entity = _playerManager?.LocalPlayer?.ControlledEntity;
return entity != null && entity.TryGetComponent(out hands);
}
public void UpdateHandIcons()
{
if (Parent == null)
{
return;
}
UpdateDraw();
if (!TryGetHands(out IHandsComponent hands))
if (!TryGetHands(out var hands))
return;
var left = hands.GetEntity("left");
var right = hands.GetEntity("right");
ActiveHandRect.Position = hands.ActiveIndex == "left" ? _handL.TopLeft : _handR.TopLeft;
if (left != null)
{
if (left != LeftHand.Entity)
if (left != LeftHand)
{
LeftHand.Entity = left;
LeftHand.HeldSprite = GetIconSprite(left);
LeftHand = left;
if (LeftHand.TryGetComponent(out ISpriteComponent sprite))
{
LeftSpriteView.Sprite = sprite;
}
}
}
else
{
LeftHand.Entity = null;
LeftHand.HeldSprite = null;
LeftHand = null;
LeftSpriteView.Sprite = null;
}
if (right != null)
{
if (right != RightHand.Entity)
RightHand = right;
if (RightHand.TryGetComponent(out ISpriteComponent sprite))
{
RightHand.Entity = right;
RightHand.HeldSprite = GetIconSprite(right);
RightSpriteView.Sprite = sprite;
}
}
else
{
RightHand.Entity = null;
RightHand.HeldSprite = null;
RightHand = null;
RightSpriteView.Sprite = null;
}
}
@@ -170,53 +185,77 @@ namespace Content.Client.UserInterface
((HandsComponent) hands).UseActiveHand();
}
private void AttackByInHand(string hand)
{
if (!TryGetHands(out var hands))
return;
hands.AttackByInHand(hand);
}
protected override bool HasPoint(Vector2 point)
{
return handL.Contains((Vector2i)point) || handR.Contains((Vector2i)point);
return _handL.Contains((Vector2i) point) || _handR.Contains((Vector2i) point);
}
protected override void MouseDown(GUIMouseButtonEventArgs args)
{
base.MouseDown(args);
var lefthandcontains = handL.Contains((Vector2i)args.RelativePosition);
var righthandcontains = handR.Contains((Vector2i)args.RelativePosition);
var leftHandContains = _handL.Contains((Vector2i) args.RelativePosition);
var rightHandContains = _handR.Contains((Vector2i) args.RelativePosition);
string handIndex;
if (leftHandContains)
{
handIndex = "left";
}
else if (rightHandContains)
{
handIndex = "right";
}
else
{
return;
}
if (args.Button == Mouse.Button.Left)
{
if (!TryGetHands(out IHandsComponent hands))
if (!TryGetHands(out var hands))
return;
if ((hands.ActiveIndex == "left" && lefthandcontains)
|| (hands.ActiveIndex == "right" && righthandcontains))
if (hands.ActiveIndex == handIndex)
{
UseActiveHand();
}
else
{
AttackByInHand(handIndex);
}
}
else if (args.Button == Mouse.Button.Middle)
{
SendSwitchHandTo(handIndex);
}
else if (args.Button == Mouse.Button.Right)
{
if (lefthandcontains)
if (!TryGetHands(out var hands))
{
SendSwitchHandTo("left");
}
if (righthandcontains)
{
SendSwitchHandTo("right");
}
}
return;
}
private static Texture GetIconSprite(IEntity entity)
var entity = hands.GetEntity(handIndex);
if (entity == null)
{
if (entity.TryGetComponent<IconComponent>(out var component) && component.Icon != null)
{
return component.Icon.Default;
}
return IoCManager.Resolve<IResourceCache>().GetFallback<TextureResource>();
return;
}
private struct UiHandInfo
{
public IEntity Entity { get; set; }
public Texture HeldSprite { get; set; }
var esm = IoCManager.Resolve<IEntitySystemManager>();
esm.GetEntitySystem<VerbSystem>().OpenContextMenu(entity, new ScreenCoordinates(args.GlobalPosition));
}
}
}
}

View File

@@ -0,0 +1,114 @@
using Content.Client.Chat;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
namespace Content.Client.UserInterface
{
internal sealed class LobbyGui : PanelContainer
{
public Label ServerName { get; }
public Label StartTime { get; }
public Button ReadyButton { get; }
public Button ObserveButton { get; }
public Button LeaveButton { get; }
public ChatBox Chat { get; }
public ItemList OnlinePlayerItemList { get; }
public LobbyGui(ILocalizationManager localization, IResourceCache resourceCache)
{
PanelOverride = new StyleBoxFlat {BackgroundColor = new Color(37, 37, 45)};
PanelOverride.SetContentMarginOverride(StyleBox.Margin.All, 4);
var vBox = new VBoxContainer();
AddChild(vBox);
{
// Title bar.
var titleContainer = new HBoxContainer();
vBox.AddChild(titleContainer);
var lobbyTitle = new Label
{
Text = localization.GetString("Lobby"),
SizeFlagsHorizontal = SizeFlags.None
};
lobbyTitle.AddStyleClass(NanoStyle.StyleClassLabelHeading);
titleContainer.AddChild(lobbyTitle);
titleContainer.AddChild(ServerName = new Label
{
SizeFlagsHorizontal = SizeFlags.ShrinkCenter | SizeFlags.Expand
});
ServerName.AddStyleClass(NanoStyle.StyleClassLabelHeading);
titleContainer.AddChild(LeaveButton = new Button
{
SizeFlagsHorizontal = SizeFlags.ShrinkEnd,
Text = localization.GetString("Leave")
});
LeaveButton.AddStyleClass(NanoStyle.StyleClassButtonBig);
}
var hBox = new HBoxContainer {SizeFlagsVertical = SizeFlags.FillExpand};
vBox.AddChild(hBox);
{
var leftVBox = new VBoxContainer {SizeFlagsHorizontal = SizeFlags.FillExpand};
hBox.AddChild(leftVBox);
leftVBox.AddChild(new Placeholder(resourceCache)
{
SizeFlagsVertical = SizeFlags.FillExpand,
PlaceholderText = localization.GetString("Character UI\nPlaceholder")
});
var readyButtons = new HBoxContainer();
leftVBox.AddChild(readyButtons);
readyButtons.AddChild(ObserveButton = new Button
{
Text = localization.GetString("Observe")
});
ObserveButton.AddStyleClass(NanoStyle.StyleClassButtonBig);
readyButtons.AddChild(StartTime = new Label
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
Align = Label.AlignMode.Right
});
readyButtons.AddChild(ReadyButton = new Button
{
ToggleMode = true,
Text = localization.GetString("Ready Up")
});
ReadyButton.AddStyleClass(NanoStyle.StyleClassButtonBig);
leftVBox.AddChild(Chat = new ChatBox {SizeFlagsVertical = SizeFlags.FillExpand});
Chat.Input.PlaceHolder = localization.GetString("Say something!");
}
{
var rightVBox = new VBoxContainer {SizeFlagsHorizontal = SizeFlags.FillExpand};
hBox.AddChild(rightVBox);
rightVBox.AddChild(new Label
{
Text = localization.GetString("Online Players:")
});
rightVBox.AddChild(OnlinePlayerItemList = new ItemList
{
SizeFlagsVertical = SizeFlags.FillExpand,
//SelectMode = ItemList.ItemListSelectMode.None
});
rightVBox.AddChild(new Placeholder(resourceCache)
{
SizeFlagsVertical = SizeFlags.FillExpand,
PlaceholderText = localization.GetString("Server Info\nPlaceholder")
});
}
}
}
}

View File

@@ -0,0 +1,487 @@
using Content.Client.GameObjects.EntitySystems;
using Content.Client.Utility;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
namespace Content.Client.UserInterface
{
public sealed class NanoStyle
{
public const string StyleClassLabelHeading = "LabelHeading";
public const string StyleClassLabelSubText = "LabelSubText";
public const string StyleClassButtonBig = "ButtonBig";
private static readonly Color NanoGold = Color.FromHex("#A88B5E");
//Used by the APC and SMES menus
public const string StyleClassPowerStateNone = "PowerStateNone";
public const string StyleClassPowerStateLow = "PowerStateLow";
public const string StyleClassPowerStateGood = "PowerStateGood";
public Stylesheet Stylesheet { get; }
public NanoStyle()
{
var resCache = IoCManager.Resolve<IResourceCache>();
var notoSans10 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 10);
var notoSans12 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 12);
var notoSansDisplayBold14 = resCache.GetFont("/Fonts/NotoSansDisplay/NotoSansDisplay-Bold.ttf", 14);
var notoSans16 = resCache.GetFont("/Nano/NotoSans/NotoSans-Regular.ttf", 16);
var notoSansBold16 = resCache.GetFont("/Nano/NotoSans/NotoSans-Bold.ttf", 16);
var textureCloseButton = resCache.GetTexture("/Nano/cross.svg.png");
var windowHeaderTex = resCache.GetTexture("/Nano/window_header.png");
var windowHeader = new StyleBoxTexture
{
Texture = windowHeaderTex,
PatchMarginBottom = 3,
ExpandMarginBottom = 3,
};
var windowBackgroundTex = resCache.GetTexture("/Nano/window_background.png");
var windowBackground = new StyleBoxTexture
{
Texture = windowBackgroundTex,
};
windowBackground.SetPatchMargin(StyleBox.Margin.Horizontal | StyleBox.Margin.Bottom, 2);
windowBackground.SetExpandMargin(StyleBox.Margin.Horizontal | StyleBox.Margin.Bottom, 2);
var buttonNormalTex = resCache.GetTexture("/Nano/button_normal.png");
var buttonNormal = new StyleBoxTexture
{
Texture = buttonNormalTex,
};
buttonNormal.SetPatchMargin(StyleBox.Margin.All, 2);
buttonNormal.SetContentMarginOverride(StyleBox.Margin.Horizontal, 8);
var buttonHoverTex = resCache.GetTexture("/Nano/button_hover.png");
var buttonHover = new StyleBoxTexture
{
Texture = buttonHoverTex,
};
buttonHover.SetPatchMargin(StyleBox.Margin.All, 2);
buttonHover.SetContentMarginOverride(StyleBox.Margin.Horizontal, 8);
var buttonPressedTex = resCache.GetTexture("/Nano/button_pressed.png");
var buttonPressed = new StyleBoxTexture
{
Texture = buttonPressedTex,
};
buttonPressed.SetPatchMargin(StyleBox.Margin.All, 2);
buttonPressed.SetContentMarginOverride(StyleBox.Margin.Horizontal, 8);
var buttonDisabledTex = resCache.GetTexture("/Nano/button_disabled.png");
var buttonDisabled = new StyleBoxTexture
{
Texture = buttonDisabledTex,
};
buttonDisabled.SetPatchMargin(StyleBox.Margin.All, 2);
buttonDisabled.SetContentMarginOverride(StyleBox.Margin.Horizontal, 8);
var lineEditTex = resCache.GetTexture("/Nano/lineedit.png");
var lineEdit = new StyleBoxTexture
{
Texture = lineEditTex,
};
lineEdit.SetPatchMargin(StyleBox.Margin.All, 3);
lineEdit.SetContentMarginOverride(StyleBox.Margin.Horizontal, 5);
var tabContainerPanelTex = resCache.GetTexture("/Nano/tabcontainer_panel.png");
var tabContainerPanel = new StyleBoxTexture
{
Texture = tabContainerPanelTex,
};
tabContainerPanel.SetPatchMargin(StyleBox.Margin.All, 2);
var tabContainerBoxActive = new StyleBoxFlat {BackgroundColor = new Color(64, 64, 64)};
tabContainerBoxActive.SetContentMarginOverride(StyleBox.Margin.Horizontal, 3);
var tabContainerBoxInactive = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 32)};
tabContainerBoxInactive.SetContentMarginOverride(StyleBox.Margin.Horizontal, 3);
var vScrollBarGrabberNormal = new StyleBoxFlat
{
BackgroundColor = Color.Gray.WithAlpha(0.35f), ContentMarginLeftOverride = 10
};
var vScrollBarGrabberHover = new StyleBoxFlat
{
BackgroundColor = new Color(140, 140, 140).WithAlpha(0.35f), ContentMarginLeftOverride = 10
};
var vScrollBarGrabberGrabbed = new StyleBoxFlat
{
BackgroundColor = new Color(160, 160, 160).WithAlpha(0.35f), ContentMarginLeftOverride = 10
};
var hScrollBarGrabberNormal = new StyleBoxFlat
{
BackgroundColor = Color.Gray.WithAlpha(0.35f), ContentMarginTopOverride = 10
};
var hScrollBarGrabberHover = new StyleBoxFlat
{
BackgroundColor = new Color(140, 140, 140).WithAlpha(0.35f), ContentMarginTopOverride = 10
};
var hScrollBarGrabberGrabbed = new StyleBoxFlat
{
BackgroundColor = new Color(160, 160, 160).WithAlpha(0.35f), ContentMarginTopOverride = 10
};
var progressBarBackground = new StyleBoxFlat
{
BackgroundColor = new Color(0.25f, 0.25f, 0.25f)
};
progressBarBackground.SetContentMarginOverride(StyleBox.Margin.Vertical, 5);
var progressBarForeground = new StyleBoxFlat
{
BackgroundColor = new Color(0.25f, 0.50f, 0.25f)
};
progressBarForeground.SetContentMarginOverride(StyleBox.Margin.Vertical, 5);
// CheckBox
var checkBoxTextureChecked = resCache.GetTexture("/Nano/checkbox_checked.svg.96dpi.png");
var checkBoxTextureUnchecked = resCache.GetTexture("/Nano/checkbox_unchecked.svg.96dpi.png");
// Tooltip box
var tooltipTexture = resCache.GetTexture("/Nano/tooltip.png");
var tooltipBox = new StyleBoxTexture
{
Texture = tooltipTexture,
};
tooltipBox.SetPatchMargin(StyleBox.Margin.All, 2);
tooltipBox.SetContentMarginOverride(StyleBox.Margin.Horizontal, 5);
// Placeholder
var placeholderTexture = resCache.GetTexture("/Nano/placeholder.png");
var placeholder = new StyleBoxTexture { Texture = placeholderTexture };
placeholder.SetPatchMargin(StyleBox.Margin.All, 24);
placeholder.SetExpandMargin(StyleBox.Margin.All, -5);
var itemListBackgroundSelected = new StyleBoxFlat {BackgroundColor = new Color(75, 75, 86)};
itemListBackgroundSelected.SetContentMarginOverride(StyleBox.Margin.Vertical, 2);
itemListBackgroundSelected.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4);
var itemListItemBackgroundDisabled = new StyleBoxFlat {BackgroundColor = new Color(10, 10, 12)};
itemListItemBackgroundDisabled.SetContentMarginOverride(StyleBox.Margin.Vertical, 2);
itemListItemBackgroundDisabled.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4);
var itemListItemBackground = new StyleBoxFlat {BackgroundColor = new Color(55, 55, 68)};
itemListItemBackground.SetContentMarginOverride(StyleBox.Margin.Vertical, 2);
itemListItemBackground.SetContentMarginOverride(StyleBox.Margin.Horizontal, 4);
Stylesheet = new Stylesheet(new[]
{
// Default font.
new StyleRule(
new SelectorElement(null, null, null, null),
new[]
{
new StyleProperty("font", notoSans12),
}),
// Window title.
new StyleRule(
new SelectorElement(typeof(Label), new[] {SS14Window.StyleClassWindowTitle}, null, null),
new[]
{
new StyleProperty(Label.StylePropertyFontColor, NanoGold),
new StyleProperty(Label.StylePropertyFont, notoSansDisplayBold14),
}),
// Window background.
new StyleRule(
new SelectorElement(null, new[] {SS14Window.StyleClassWindowPanel}, null, null),
new[]
{
new StyleProperty(Panel.StylePropertyPanel, windowBackground),
}),
// Window header.
new StyleRule(
new SelectorElement(typeof(Panel), new[] {SS14Window.StyleClassWindowHeader}, null, null),
new[]
{
new StyleProperty(Panel.StylePropertyPanel, windowHeader),
}),
// Window close button base texture.
new StyleRule(
new SelectorElement(typeof(TextureButton), new[] {SS14Window.StyleClassWindowCloseButton}, null,
null),
new[]
{
new StyleProperty(TextureButton.StylePropertyTexture, textureCloseButton),
new StyleProperty(Control.StylePropertyModulateSelf, Color.FromHex("#4B596A")),
}),
// Window close button hover.
new StyleRule(
new SelectorElement(typeof(TextureButton), new[] {SS14Window.StyleClassWindowCloseButton}, null,
TextureButton.StylePseudoClassHover),
new[]
{
new StyleProperty(Control.StylePropertyModulateSelf, Color.FromHex("#7F3636")),
}),
// Window close button pressed.
new StyleRule(
new SelectorElement(typeof(TextureButton), new[] {SS14Window.StyleClassWindowCloseButton}, null,
TextureButton.StylePseudoClassPressed),
new[]
{
new StyleProperty(Control.StylePropertyModulateSelf, Color.FromHex("#753131")),
}),
// Regular buttons!
new StyleRule(
new SelectorElement(typeof(Button), null, null, Button.StylePseudoClassNormal),
new[]
{
new StyleProperty(Button.StylePropertyStyleBox, buttonNormal),
}),
new StyleRule(
new SelectorElement(typeof(Button), null, null, Button.StylePseudoClassHover),
new[]
{
new StyleProperty(Button.StylePropertyStyleBox, buttonHover),
}),
new StyleRule(
new SelectorElement(typeof(Button), null, null, Button.StylePseudoClassPressed),
new[]
{
new StyleProperty(Button.StylePropertyStyleBox, buttonPressed),
}),
new StyleRule(
new SelectorElement(typeof(Button), null, null, Button.StylePseudoClassDisabled),
new[]
{
new StyleProperty(Button.StylePropertyStyleBox, buttonDisabled),
new StyleProperty("font-color", Color.FromHex("#E5E5E581")),
}),
// Main menu: Make those buttons bigger.
new StyleRule(
new SelectorChild(
new SelectorElement(null, null, "mainMenuVBox", null),
new SelectorElement(typeof(Button), null, null, null)),
new[]
{
new StyleProperty("font", notoSansBold16),
}),
// Main menu: also make those buttons slightly more separated.
new StyleRule(new SelectorElement(typeof(BoxContainer), null, "mainMenuVBox", null),
new[]
{
new StyleProperty(BoxContainer.StylePropertySeparation, 2),
}),
// Fancy LineEdit
new StyleRule(new SelectorElement(typeof(LineEdit), null, null, null),
new[]
{
new StyleProperty(LineEdit.StylePropertyStyleBox, lineEdit),
}),
new StyleRule(
new SelectorElement(typeof(LineEdit), new[] {LineEdit.StyleClassLineEditNotEditable}, null, null),
new[]
{
new StyleProperty("font-color", new Color(192, 192, 192)),
}),
new StyleRule(new SelectorElement(typeof(LineEdit), null, null, LineEdit.StylePseudoClassPlaceholder),
new[]
{
new StyleProperty("font-color", Color.Gray),
}),
// TabContainer
new StyleRule(new SelectorElement(typeof(TabContainer), null, null, null),
new[]
{
new StyleProperty(TabContainer.StylePropertyPanelStyleBox, tabContainerPanel),
new StyleProperty(TabContainer.StylePropertyTabStyleBox, tabContainerBoxActive),
new StyleProperty(TabContainer.StylePropertyTabStyleBoxInactive, tabContainerBoxInactive),
}),
// Scroll bars
new StyleRule(new SelectorElement(typeof(VScrollBar), null, null, null),
new[]
{
new StyleProperty(ScrollBar.StylePropertyGrabber,
vScrollBarGrabberNormal),
}),
new StyleRule(new SelectorElement(typeof(VScrollBar), null, null, ScrollBar.StylePseudoClassHover),
new[]
{
new StyleProperty(ScrollBar.StylePropertyGrabber,
vScrollBarGrabberHover),
}),
new StyleRule(new SelectorElement(typeof(VScrollBar), null, null, ScrollBar.StylePseudoClassGrabbed),
new[]
{
new StyleProperty(ScrollBar.StylePropertyGrabber,
vScrollBarGrabberGrabbed),
}),
new StyleRule(new SelectorElement(typeof(HScrollBar), null, null, null),
new[]
{
new StyleProperty(ScrollBar.StylePropertyGrabber,
hScrollBarGrabberNormal),
}),
new StyleRule(new SelectorElement(typeof(HScrollBar), null, null, ScrollBar.StylePseudoClassHover),
new[]
{
new StyleProperty(ScrollBar.StylePropertyGrabber,
hScrollBarGrabberHover),
}),
new StyleRule(new SelectorElement(typeof(HScrollBar), null, null, ScrollBar.StylePseudoClassGrabbed),
new[]
{
new StyleProperty(ScrollBar.StylePropertyGrabber,
hScrollBarGrabberGrabbed),
}),
// ProgressBar
new StyleRule(new SelectorElement(typeof(ProgressBar), null, null, null),
new[]
{
new StyleProperty(ProgressBar.StylePropertyBackground, progressBarBackground),
new StyleProperty(ProgressBar.StylePropertyForeground, progressBarForeground)
}),
// CheckBox
new StyleRule(new SelectorElement(typeof(CheckBox), null, null, null), new[]
{
new StyleProperty(CheckBox.StylePropertyIcon, checkBoxTextureUnchecked),
}),
new StyleRule(new SelectorElement(typeof(CheckBox), null, null, Button.StylePseudoClassPressed), new[]
{
new StyleProperty(CheckBox.StylePropertyIcon, checkBoxTextureChecked),
}),
new StyleRule(new SelectorElement(typeof(CheckBox), null, null, null), new[]
{
new StyleProperty(CheckBox.StylePropertyHSeparation, 3),
}),
// Tooltip
new StyleRule(new SelectorElement(typeof(Tooltip), null, null, null), new[]
{
new StyleProperty(PanelContainer.StylePropertyPanel, tooltipBox)
}),
new StyleRule(new SelectorElement(typeof(PanelContainer), new []{"tooltipBox"}, null, null), new[]
{
new StyleProperty(PanelContainer.StylePropertyPanel, tooltipBox)
}),
// Entity tooltip
new StyleRule(
new SelectorElement(typeof(PanelContainer), new[] {ExamineSystem.StyleClassEntityTooltip}, null,
null), new[]
{
new StyleProperty(PanelContainer.StylePropertyPanel, tooltipBox)
}),
// ItemList
new StyleRule(new SelectorElement(typeof(ItemList), null, null, null), new[]
{
new StyleProperty(ItemList.StylePropertyBackground,
new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)}),
new StyleProperty(ItemList.StylePropertyItemBackground,
itemListItemBackground),
new StyleProperty(ItemList.StylePropertyDisabledItemBackground,
itemListItemBackgroundDisabled),
new StyleProperty(ItemList.StylePropertySelectedItemBackground,
itemListBackgroundSelected)
}),
// Tree
new StyleRule(new SelectorElement(typeof(Tree), null, null, null), new[]
{
new StyleProperty(Tree.StylePropertyBackground,
new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)}),
new StyleProperty(Tree.StylePropertyItemBoxSelected, new StyleBoxFlat
{
BackgroundColor = new Color(55, 55, 68),
ContentMarginLeftOverride = 4
})
}),
// Placeholder
new StyleRule(new SelectorElement(typeof(Placeholder), null, null, null), new []
{
new StyleProperty(PanelContainer.StylePropertyPanel, placeholder),
}),
new StyleRule(new SelectorElement(typeof(Label), new []{Placeholder.StyleClassPlaceholderText}, null, null), new []
{
new StyleProperty(Label.StylePropertyFont, notoSans16),
new StyleProperty(Label.StylePropertyFontColor, new Color(103, 103, 103, 128)),
}),
// Big Label
new StyleRule(new SelectorElement(typeof(Label), new []{StyleClassLabelHeading}, null, null), new []
{
new StyleProperty(Label.StylePropertyFont, notoSansBold16),
new StyleProperty(Label.StylePropertyFontColor, NanoGold),
} ),
// Small Label
new StyleRule(new SelectorElement(typeof(Label), new []{StyleClassLabelSubText}, null, null), new []
{
new StyleProperty(Label.StylePropertyFont, notoSans10),
new StyleProperty(Label.StylePropertyFontColor, Color.DarkGray),
} ),
// Big Button
new StyleRule(new SelectorElement(typeof(Button), new []{StyleClassButtonBig}, null, null), new []
{
new StyleProperty("font", notoSans16)
}),
//APC and SMES power state label colors
new StyleRule(new SelectorElement(typeof(Label), new []{StyleClassPowerStateNone}, null, null), new []
{
new StyleProperty(Label.StylePropertyFontColor, new Color(0.8f, 0.0f, 0.0f))
}),
new StyleRule(new SelectorElement(typeof(Label), new []{StyleClassPowerStateLow}, null, null), new []
{
new StyleProperty(Label.StylePropertyFontColor, new Color(0.9f, 0.36f, 0.0f))
}),
new StyleRule(new SelectorElement(typeof(Label), new []{StyleClassPowerStateGood}, null, null), new []
{
new StyleProperty(Label.StylePropertyFontColor, new Color(0.024f, 0.8f, 0.0f))
}),
// Those top menu buttons.
new StyleRule(
new SelectorElement(typeof(GameHud.TopButton), null, null, Button.StylePseudoClassNormal), new []
{
new StyleProperty(Button.StylePropertyStyleBox, buttonNormal),
}),
new StyleRule(
new SelectorElement(typeof(GameHud.TopButton), null, null, Button.StylePseudoClassPressed), new []
{
new StyleProperty(Button.StylePropertyStyleBox, buttonPressed),
}),
new StyleRule(
new SelectorElement(typeof(GameHud.TopButton), null, null, Button.StylePseudoClassHover), new []
{
new StyleProperty(Button.StylePropertyStyleBox, buttonHover),
}),
new StyleRule(
new SelectorElement(typeof(Label), new [] {GameHud.TopButton.StyleClassLabelTopButton}, null, null), new []
{
new StyleProperty(Label.StylePropertyFont, notoSansDisplayBold14),
}),
});
}
}
}

View File

@@ -0,0 +1,31 @@
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.UserInterface
{
public sealed class Placeholder : PanelContainer
{
public const string StyleClassPlaceholderText = "PlaceholderText";
private readonly Label _label;
public string PlaceholderText
{
get => _label.Text;
set => _label.Text = value;
}
public Placeholder(IResourceCache _resourceCache)
{
_label = new Label
{
SizeFlagsHorizontal = SizeFlags.Fill,
SizeFlagsVertical = SizeFlags.Fill,
Align = Label.AlignMode.Center,
VAlign = Label.VAlignMode.Center
};
_label.AddStyleClass(StyleClassPlaceholderText);
AddChild(_label);
}
}
}

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