From 1e36a61339bc3d50ec37b9bb4e945188aa4d2dd9 Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Thu, 27 Mar 2025 17:20:20 +0300 Subject: [PATCH] Skill progression system [Draft] (#1056) * fuck all * clean up * remove knowledge books * Update base.yml * cl * fix #991 * fix * Update subgamemodes.yml * manual migration + recipes fix * Update comoss.yml * setup data * setup skills systems * more blacksmithing skills * Update base.yml * Create CP14SkillEffectAction.cs * skill button returns * base graph UI window * skill tree drawing * UI improve * pyro setup * skill trees selection, dragging control * refactor skill system: rename Skills to LearnedSkills, add experience tracking and learning logic * Create available.png * skill description generation setup * auto parsing skill names and descriptions * Hydrosophistry * water light fire and metamagic ported to skill tre * ice dagger spell returns * Update ice_dagger.yml * delete old files * Update modular_garde.yml * ice arrow spell * link graph with parallax * show experience counter * polish * puf * p * pipap * finally ready to merg? * fix * fix 2 --- Content.Client/Input/ContentContexts.cs | 7 +- .../Options/UI/Tabs/KeyRebindTab.xaml.cs | 3 +- Content.Client/Parallax/ParallaxControl.cs | 37 +- .../MenuBar/GameTopMenuBarUIController.cs | 6 +- .../MenuBar/Widgets/GameTopMenuBar.xaml | 6 +- .../Knowledge/ClientCP14KnowledgeSystem.cs | 43 --- .../_CP14/Skill/CP14ClientSkillSystem.cs | 54 +++ .../Skill/Ui/CP14SkillTreeButtonControl.xaml | 24 ++ .../Ui/CP14SkillTreeButtonControl.xaml.cs | 22 ++ .../Skill/Ui/CP14SkillTreeGraphControl.xaml | 7 + .../Ui/CP14SkillTreeGraphControl.xaml.cs | 218 ++++++++++++ .../Knowledge/CP14KnowledgeUIController.cs | 158 --------- .../Windows/CP14KnowledgeWindow.xaml | 13 - .../Systems/Skill/CP14SkillUIController.cs | 249 ++++++++++++++ .../Systems/Skill/Window/CP14SkillWindow.xaml | 89 +++++ .../Window/CP14SkillWindow.xaml.cs} | 6 +- .../Chemistry/EntitySystems/InjectorSystem.cs | 1 - .../CP14DemiplaneSystem.Stabilization.cs | 3 + .../CP14KnowledgeAdminUtilitiesSystem.cs | 71 ---- .../_CP14/Knowledge/CP14KnowledgeSystem.cs | 212 ------------ .../Specials/CP14AddKnowledgeSpecial.cs | 27 -- .../CP14AutoLearnActionComponent.cs | 10 - .../_CP14/MagicSpell/CP14MagicSystem.cs | 11 - Content.Server/_CP14/Skill/CP14SkillSystem.cs | 24 ++ .../_CP14/Workbench/CP14WorkbenchSystem.UI.cs | 21 +- .../_CP14/Workbench/CP14WorkbenchSystem.cs | 4 +- Content.Shared/Input/ContentKeyFunctions.cs | 2 +- .../Preferences/Loadouts/LoadoutPrototype.cs | 8 + .../Station/SharedStationSpawningSystem.cs | 9 +- Content.Shared/Verbs/VerbCategory.cs | 8 +- .../Knowledge/CP14KnowledgePaperTextSystem.cs | 62 ---- .../Components/CP14AllKnowingComponent.cs | 6 - .../CP14AutoAddKnowledgeComponent.cs | 15 - .../CP14KnowledgeLearningSourceComponent.cs | 17 - .../CP14KnowledgePaperTextComponent.cs | 8 - .../CP14KnowledgeStorageComponent.cs | 14 - .../Events/CP14KnowledgeInfoEvent.cs | 18 - .../Events/CP14KnowledgeLearnDoAfterEvent.cs | 17 - .../Events/CP14KnowledgeUsedEvent.cs | 18 - .../Events/CP14RequestKnowledgeInfoEvent.cs | 14 - .../Prototypes/CP14KnowledgePrototype.cs | 25 -- .../Knowledge/SharedCP14KnowledgeSystem.cs | 31 -- .../_CP14/Skill/CP14SharedSkillSystem.cs | 230 +++++++++++++ .../_CP14/Skill/CP14SkillSystem.Admin.cs | 67 ++++ .../_CP14/Skill/CP14SkillSystem.Learning.cs | 48 +++ .../Components/CP14SkillStorageComponent.cs | 46 +++ .../Skill/Prototypes/CP14SkillPrototype.cs | 64 ++++ .../Prototypes/CP14SkillTreePrototype.cs | 44 +++ .../_CP14/Skill/Specials/AddAction.cs | 52 +++ .../_CP14/Skill/Specials/CP14SkillEffect.cs | 17 + .../Requirements/KnowledgeRequired.cs | 41 ++- .../Requirements/MaterialResource.cs | 10 +- .../Workbench/Requirements/ProtoIdResource.cs | 6 +- .../Workbench/Requirements/StackResource.cs | 6 +- .../Workbench/Requirements/TagResource.cs | 10 +- .../Workbench/WorkbenchCraftRequirement.cs | 9 +- .../Audio/_CP14/Effects/attributions.yml | 5 + Resources/Audio/_CP14/Effects/skill_up1.ogg | Bin 0 -> 49041 bytes Resources/Audio/_CP14/Effects/skill_up2.ogg | Bin 0 -> 52750 bytes Resources/Audio/_CP14/Effects/skill_up3.ogg | Bin 0 -> 45781 bytes Resources/Audio/_CP14/Effects/skill_up4.ogg | Bin 0 -> 47738 bytes .../en-US/_CP14/knowledge/knowledge-ui.ftl | 2 - .../en-US/_CP14/knowledge/knowledge.ftl | 2 + .../en-US/_CP14/knowledge/skill-examined.ftl | 18 - .../en-US/_CP14/knowledge/skill-issues.ftl | 6 - .../en-US/_CP14/knowledge/skill-meta.ftl | 24 -- .../Locale/en-US/_CP14/loadouts/loadout.ftl | 1 + .../Locale/en-US/_CP14/skill/skill_tree.ftl | 22 ++ Resources/Locale/en-US/_CP14/skill/ui.ftl | 10 + .../ru-RU/_CP14/knowledge/knowledge-ui.ftl | 2 - .../ru-RU/_CP14/knowledge/knowledge.ftl | 2 + .../ru-RU/_CP14/knowledge/skill-examined.ftl | 18 - .../ru-RU/_CP14/knowledge/skill-issues.ftl | 6 - .../ru-RU/_CP14/knowledge/skill-meta.ftl | 24 -- .../Locale/ru-RU/_CP14/loadouts/loadout.ftl | 1 + .../Locale/ru-RU/_CP14/skill/skill_tree.ftl | 24 ++ Resources/Locale/ru-RU/_CP14/skill/ui.ftl | 10 + Resources/Maps/_CP14/comoss.yml | 12 - Resources/Maps/_CP14/dev_map.yml | 21 -- Resources/Maps/_CP14/factoria.yml | 7 - .../Entities/Mobs/Player/admin_ghost.yml | 1 - .../Catalog/Cargo/Store/SpiceStream/buy.yml | 29 -- ...T1_beer_creation.yml => beer_creation.yml} | 0 .../Water/{T0_freeze.yml => freeze.yml} | 0 .../Actions/Spells/Water/ice_arrow.yml | 109 ++++++ .../Actions/Spells/Water/ice_dagger.yml | 146 ++++++++ .../{T1_ice_shards.yml => ice_shards.yml} | 0 ..._water_creation.yml => water_creation.yml} | 0 .../_CP14/Entities/Mobs/NPC/skeleton.yml | 20 +- .../_CP14/Entities/Mobs/Species/base.yml | 2 +- .../_CP14/Entities/Mobs/Species/elf.yml | 4 - .../_CP14/Entities/Mobs/Species/goblin.yml | 4 - .../_CP14/Entities/Mobs/Species/silva.yml | 4 - .../_CP14/Entities/Mobs/Species/skeleton.yml | 1 - .../Objects/Bureaucracy/Books/base.yml | 12 +- .../Objects/Bureaucracy/Books/knowledge.yml | 68 ---- .../Structures/Furniture/bookshelf.yml | 11 - .../_CP14/GameRules/subgamemodes.yml | 5 +- .../Prototypes/_CP14/Knowledge/knowledges.yml | 42 --- .../_CP14/Loadouts/antag_loadouts.yml | 4 - .../_CP14/Loadouts/role_loadouts.yml | 20 +- .../Prototypes/_CP14/Loadouts/skill_tree.yml | 152 +++++++++ .../Prototypes/_CP14/Loadouts/spells.yml | 322 ------------------ .../_CP14/ModularCraft/Tip/arrow.yml | 2 +- .../_CP14/Recipes/Workbench/Anvil/misc.yml | 36 -- .../Recipes/Workbench/Anvil/modular_blade.yml | 96 ------ .../Workbench/Anvil/modular_breastplate.yml | 24 -- .../Workbench/Anvil/modular_cuisses.yml | 16 - .../Recipes/Workbench/Anvil/modular_garde.yml | 16 - .../Workbench/Anvil/modular_greave.yml | 16 - .../Recipes/Workbench/Anvil/modular_tip.yml | 30 +- .../_CP14/Recipes/Workbench/furnace.yml | 28 -- .../_CP14/Recipes/Workbench/sewing_table.yml | 44 --- .../_CP14/Roles/Jobs/Artisan/alchemist.yml | 4 - .../_CP14/Roles/Jobs/Artisan/apprentice.yml | 4 - .../_CP14/Roles/Jobs/Artisan/blacksmith.yml | 7 - .../_CP14/Roles/Jobs/Artisan/innkeeper.yml | 4 - .../_CP14/Roles/Jobs/Guard/guard.yml | 4 - .../Roles/Jobs/Guard/guard_commander.yml | 4 - .../_CP14/Roles/Jobs/Mercenary/adventurer.yml | 4 - .../_CP14/Roles/Jobs/Traders/merchant.yml | 4 - Resources/Prototypes/_CP14/Skill/atlethic.yml | 19 ++ Resources/Prototypes/_CP14/Skill/healing.yml | 73 ++++ .../Prototypes/_CP14/Skill/hydrosophistry.yml | 59 ++++ Resources/Prototypes/_CP14/Skill/illusion.yml | 52 +++ .../Prototypes/_CP14/Skill/metamagic.yml | 41 +++ .../Prototypes/_CP14/Skill/pyrokinetic.yml | 19 ++ .../Prototypes/_CP14/Skill/skill_tree.yml | 35 ++ .../_CP14/SoundCollections/music.yml | 10 +- .../Actions/Spells/water.rsi/ice_arrow.png | Bin 0 -> 383 bytes .../_CP14/Actions/Spells/water.rsi/meta.json | 5 +- .../Skills/default.rsi/available.png | Bin 0 -> 373 bytes .../Interface/Skills/default.rsi/frame.png | Bin 0 -> 260 bytes .../Interface/Skills/default.rsi/hovered.png | Bin 0 -> 240 bytes .../Interface/Skills/default.rsi/learned.png | Bin 0 -> 399 bytes .../Interface/Skills/default.rsi/meta.json | 26 ++ .../Interface/Skills/default.rsi/selected.png | Bin 0 -> 191 bytes .../_CP14/Interface/Skills/skillpoint.png | Bin 0 -> 219 bytes .../battleHammer.rsi/equipped-NECK.png | Bin 945 -> 0 bytes .../BattleHammer/battleHammer.rsi/icon.png | Bin 510 -> 0 bytes .../battleHammer.rsi/inhand-left.png | Bin 1354 -> 0 bytes .../battleHammer.rsi/inhand-right.png | Bin 1327 -> 0 bytes .../battleHammer.rsi/wielded-inhand-left.png | Bin 1361 -> 0 bytes .../battleHammer.rsi/wielded-inhand-right.png | Bin 1360 -> 0 bytes .../LightHammer/lightHammer.rsi/icon.png | Bin 388 -> 0 bytes .../lightHammer.rsi/inhand-left.png | Bin 705 -> 0 bytes .../lightHammer.rsi/inhand-right.png | Bin 683 -> 0 bytes .../Melee/ice_dagger.rsi/equipped-BELT1.png | Bin 0 -> 244 bytes .../Melee/ice_dagger.rsi/equipped-BELT2.png | Bin 0 -> 251 bytes .../Melee/ice_dagger.rsi/equipped-NECK.png | Bin 0 -> 220 bytes .../Weapons/Melee/ice_dagger.rsi/icon.png | Bin 0 -> 236 bytes .../Melee/ice_dagger.rsi/inhand-left.png | Bin 0 -> 438 bytes .../Melee/ice_dagger.rsi/inhand-right.png | Bin 0 -> 439 bytes .../meta.json | 34 +- .../Ranged/Projectiles/ice_arrow.rsi/icon.png | Bin 0 -> 213 bytes .../Projectiles/ice_arrow.rsi/inhand-left.png | Bin 0 -> 208 bytes .../ice_arrow.rsi/inhand-right.png | Bin 0 -> 211 bytes .../Projectiles/ice_arrow.rsi}/meta.json | 4 +- Resources/keybinds.yml | 5 + 159 files changed, 2283 insertions(+), 1863 deletions(-) delete mode 100644 Content.Client/_CP14/Knowledge/ClientCP14KnowledgeSystem.cs create mode 100644 Content.Client/_CP14/Skill/CP14ClientSkillSystem.cs create mode 100644 Content.Client/_CP14/Skill/Ui/CP14SkillTreeButtonControl.xaml create mode 100644 Content.Client/_CP14/Skill/Ui/CP14SkillTreeButtonControl.xaml.cs create mode 100644 Content.Client/_CP14/Skill/Ui/CP14SkillTreeGraphControl.xaml create mode 100644 Content.Client/_CP14/Skill/Ui/CP14SkillTreeGraphControl.xaml.cs delete mode 100644 Content.Client/_CP14/UserInterface/Systems/Knowledge/CP14KnowledgeUIController.cs delete mode 100644 Content.Client/_CP14/UserInterface/Systems/Knowledge/Windows/CP14KnowledgeWindow.xaml create mode 100644 Content.Client/_CP14/UserInterface/Systems/Skill/CP14SkillUIController.cs create mode 100644 Content.Client/_CP14/UserInterface/Systems/Skill/Window/CP14SkillWindow.xaml rename Content.Client/_CP14/UserInterface/Systems/{Knowledge/Windows/CP14KnowledgeWindow.xaml.cs => Skill/Window/CP14SkillWindow.xaml.cs} (55%) delete mode 100644 Content.Server/_CP14/Knowledge/CP14KnowledgeAdminUtilitiesSystem.cs delete mode 100644 Content.Server/_CP14/Knowledge/CP14KnowledgeSystem.cs delete mode 100644 Content.Server/_CP14/Knowledge/Specials/CP14AddKnowledgeSpecial.cs delete mode 100644 Content.Server/_CP14/MagicSpell/CP14AutoLearnActionComponent.cs create mode 100644 Content.Server/_CP14/Skill/CP14SkillSystem.cs delete mode 100644 Content.Shared/_CP14/Knowledge/CP14KnowledgePaperTextSystem.cs delete mode 100644 Content.Shared/_CP14/Knowledge/Components/CP14AllKnowingComponent.cs delete mode 100644 Content.Shared/_CP14/Knowledge/Components/CP14AutoAddKnowledgeComponent.cs delete mode 100644 Content.Shared/_CP14/Knowledge/Components/CP14KnowledgeLearningSourceComponent.cs delete mode 100644 Content.Shared/_CP14/Knowledge/Components/CP14KnowledgePaperTextComponent.cs delete mode 100644 Content.Shared/_CP14/Knowledge/Components/CP14KnowledgeStorageComponent.cs delete mode 100644 Content.Shared/_CP14/Knowledge/Events/CP14KnowledgeInfoEvent.cs delete mode 100644 Content.Shared/_CP14/Knowledge/Events/CP14KnowledgeLearnDoAfterEvent.cs delete mode 100644 Content.Shared/_CP14/Knowledge/Events/CP14KnowledgeUsedEvent.cs delete mode 100644 Content.Shared/_CP14/Knowledge/Events/CP14RequestKnowledgeInfoEvent.cs delete mode 100644 Content.Shared/_CP14/Knowledge/Prototypes/CP14KnowledgePrototype.cs delete mode 100644 Content.Shared/_CP14/Knowledge/SharedCP14KnowledgeSystem.cs create mode 100644 Content.Shared/_CP14/Skill/CP14SharedSkillSystem.cs create mode 100644 Content.Shared/_CP14/Skill/CP14SkillSystem.Admin.cs create mode 100644 Content.Shared/_CP14/Skill/CP14SkillSystem.Learning.cs create mode 100644 Content.Shared/_CP14/Skill/Components/CP14SkillStorageComponent.cs create mode 100644 Content.Shared/_CP14/Skill/Prototypes/CP14SkillPrototype.cs create mode 100644 Content.Shared/_CP14/Skill/Prototypes/CP14SkillTreePrototype.cs create mode 100644 Content.Shared/_CP14/Skill/Specials/AddAction.cs create mode 100644 Content.Shared/_CP14/Skill/Specials/CP14SkillEffect.cs create mode 100644 Resources/Audio/_CP14/Effects/skill_up1.ogg create mode 100644 Resources/Audio/_CP14/Effects/skill_up2.ogg create mode 100644 Resources/Audio/_CP14/Effects/skill_up3.ogg create mode 100644 Resources/Audio/_CP14/Effects/skill_up4.ogg delete mode 100644 Resources/Locale/en-US/_CP14/knowledge/knowledge-ui.ftl create mode 100644 Resources/Locale/en-US/_CP14/knowledge/knowledge.ftl delete mode 100644 Resources/Locale/en-US/_CP14/knowledge/skill-examined.ftl delete mode 100644 Resources/Locale/en-US/_CP14/knowledge/skill-issues.ftl delete mode 100644 Resources/Locale/en-US/_CP14/knowledge/skill-meta.ftl create mode 100644 Resources/Locale/en-US/_CP14/skill/skill_tree.ftl create mode 100644 Resources/Locale/en-US/_CP14/skill/ui.ftl delete mode 100644 Resources/Locale/ru-RU/_CP14/knowledge/knowledge-ui.ftl create mode 100644 Resources/Locale/ru-RU/_CP14/knowledge/knowledge.ftl delete mode 100644 Resources/Locale/ru-RU/_CP14/knowledge/skill-examined.ftl delete mode 100644 Resources/Locale/ru-RU/_CP14/knowledge/skill-issues.ftl delete mode 100644 Resources/Locale/ru-RU/_CP14/knowledge/skill-meta.ftl create mode 100644 Resources/Locale/ru-RU/_CP14/skill/skill_tree.ftl create mode 100644 Resources/Locale/ru-RU/_CP14/skill/ui.ftl rename Resources/Prototypes/_CP14/Entities/Actions/Spells/Water/{T1_beer_creation.yml => beer_creation.yml} (100%) rename Resources/Prototypes/_CP14/Entities/Actions/Spells/Water/{T0_freeze.yml => freeze.yml} (100%) create mode 100644 Resources/Prototypes/_CP14/Entities/Actions/Spells/Water/ice_arrow.yml create mode 100644 Resources/Prototypes/_CP14/Entities/Actions/Spells/Water/ice_dagger.yml rename Resources/Prototypes/_CP14/Entities/Actions/Spells/Water/{T1_ice_shards.yml => ice_shards.yml} (100%) rename Resources/Prototypes/_CP14/Entities/Actions/Spells/Water/{T0_water_creation.yml => water_creation.yml} (100%) delete mode 100644 Resources/Prototypes/_CP14/Entities/Objects/Bureaucracy/Books/knowledge.yml delete mode 100644 Resources/Prototypes/_CP14/Knowledge/knowledges.yml delete mode 100644 Resources/Prototypes/_CP14/Loadouts/antag_loadouts.yml create mode 100644 Resources/Prototypes/_CP14/Loadouts/skill_tree.yml delete mode 100644 Resources/Prototypes/_CP14/Loadouts/spells.yml create mode 100644 Resources/Prototypes/_CP14/Skill/atlethic.yml create mode 100644 Resources/Prototypes/_CP14/Skill/healing.yml create mode 100644 Resources/Prototypes/_CP14/Skill/hydrosophistry.yml create mode 100644 Resources/Prototypes/_CP14/Skill/illusion.yml create mode 100644 Resources/Prototypes/_CP14/Skill/metamagic.yml create mode 100644 Resources/Prototypes/_CP14/Skill/pyrokinetic.yml create mode 100644 Resources/Prototypes/_CP14/Skill/skill_tree.yml create mode 100644 Resources/Textures/_CP14/Actions/Spells/water.rsi/ice_arrow.png create mode 100644 Resources/Textures/_CP14/Interface/Skills/default.rsi/available.png create mode 100644 Resources/Textures/_CP14/Interface/Skills/default.rsi/frame.png create mode 100644 Resources/Textures/_CP14/Interface/Skills/default.rsi/hovered.png create mode 100644 Resources/Textures/_CP14/Interface/Skills/default.rsi/learned.png create mode 100644 Resources/Textures/_CP14/Interface/Skills/default.rsi/meta.json create mode 100644 Resources/Textures/_CP14/Interface/Skills/default.rsi/selected.png create mode 100644 Resources/Textures/_CP14/Interface/Skills/skillpoint.png delete mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/BattleHammer/battleHammer.rsi/equipped-NECK.png delete mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/BattleHammer/battleHammer.rsi/icon.png delete mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/BattleHammer/battleHammer.rsi/inhand-left.png delete mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/BattleHammer/battleHammer.rsi/inhand-right.png delete mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/BattleHammer/battleHammer.rsi/wielded-inhand-left.png delete mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/BattleHammer/battleHammer.rsi/wielded-inhand-right.png delete mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/LightHammer/lightHammer.rsi/icon.png delete mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/LightHammer/lightHammer.rsi/inhand-left.png delete mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/LightHammer/lightHammer.rsi/inhand-right.png create mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/ice_dagger.rsi/equipped-BELT1.png create mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/ice_dagger.rsi/equipped-BELT2.png create mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/ice_dagger.rsi/equipped-NECK.png create mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/ice_dagger.rsi/icon.png create mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/ice_dagger.rsi/inhand-left.png create mode 100644 Resources/Textures/_CP14/Objects/Weapons/Melee/ice_dagger.rsi/inhand-right.png rename Resources/Textures/_CP14/Objects/Weapons/Melee/{BattleHammer/battleHammer.rsi => ice_dagger.rsi}/meta.json (68%) create mode 100644 Resources/Textures/_CP14/Objects/Weapons/Ranged/Projectiles/ice_arrow.rsi/icon.png create mode 100644 Resources/Textures/_CP14/Objects/Weapons/Ranged/Projectiles/ice_arrow.rsi/inhand-left.png create mode 100644 Resources/Textures/_CP14/Objects/Weapons/Ranged/Projectiles/ice_arrow.rsi/inhand-right.png rename Resources/Textures/_CP14/Objects/Weapons/{Melee/LightHammer/lightHammer.rsi => Ranged/Projectiles/ice_arrow.rsi}/meta.json (76%) diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs index 8ba399fb61..e3fabd8cd5 100644 --- a/Content.Client/Input/ContentContexts.cs +++ b/Content.Client/Input/ContentContexts.cs @@ -84,6 +84,9 @@ namespace Content.Client.Input human.AddFunction(ContentKeyFunctions.Arcade1); human.AddFunction(ContentKeyFunctions.Arcade2); human.AddFunction(ContentKeyFunctions.Arcade3); + //CP14 Keys + human.AddFunction(ContentKeyFunctions.CP14OpenSkillMenu); + //CP14 Keys end // actions should be common (for ghosts, mobs, etc) common.AddFunction(ContentKeyFunctions.OpenActionsMenu); @@ -123,10 +126,6 @@ namespace Content.Client.Input common.AddFunction(ContentKeyFunctions.OpenDecalSpawnWindow); common.AddFunction(ContentKeyFunctions.OpenAdminMenu); common.AddFunction(ContentKeyFunctions.OpenGuidebook); - - //CP14 Keys - human.AddFunction(ContentKeyFunctions.CP14OpenKnowledgeMenu); - //CP14 Keys end } } } diff --git a/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs b/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs index c9b6ec841a..db12f66df9 100644 --- a/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs @@ -312,9 +312,8 @@ namespace Content.Client.Options.UI.Tabs //CP14 AddHeader("ui-options-header-cp14"); - AddButton(ContentKeyFunctions.CP14OpenKnowledgeMenu); + AddButton(ContentKeyFunctions.CP14OpenSkillMenu); //CP14 end - foreach (var control in _keyControls.Values) { UpdateKeyControl(control); diff --git a/Content.Client/Parallax/ParallaxControl.cs b/Content.Client/Parallax/ParallaxControl.cs index 8f56857214..02875c302e 100644 --- a/Content.Client/Parallax/ParallaxControl.cs +++ b/Content.Client/Parallax/ParallaxControl.cs @@ -1,7 +1,9 @@ -using System.Numerics; +using System.Numerics; +using Content.Client.Parallax.Data; using Content.Client.Parallax.Managers; using Robust.Client.Graphics; using Robust.Client.UserInterface; +using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; using Robust.Shared.ViewVariables; @@ -17,27 +19,50 @@ public sealed class ParallaxControl : Control [Dependency] private readonly IParallaxManager _parallaxManager = default!; [Dependency] private readonly IRobustRandom _random = default!; + private string _parallaxPrototype = "FastSpace"; + [ViewVariables(VVAccess.ReadWrite)] public Vector2 Offset { get; set; } + [ViewVariables(VVAccess.ReadWrite)] public float SpeedX { get; set; } = 0.0f; + [ViewVariables(VVAccess.ReadWrite)] public float SpeedY { get; set; } = 0.0f; + [ViewVariables(VVAccess.ReadWrite)] public float ScaleX { get; set; } = 1.0f; + [ViewVariables(VVAccess.ReadWrite)] public float ScaleY { get; set; } = 1.0f; + [ViewVariables(VVAccess.ReadWrite)] public string ParallaxPrototype + { + get => _parallaxPrototype; + set + { + _parallaxPrototype = value; + _parallaxManager.LoadParallaxByName(value); + } + } public ParallaxControl() { IoCManager.InjectDependencies(this); Offset = new Vector2(_random.Next(0, 1000), _random.Next(0, 1000)); + RectClipContent = true; - _parallaxManager.LoadParallaxByName("FastSpace"); + _parallaxManager.LoadParallaxByName(_parallaxPrototype); } protected override void Draw(DrawingHandleScreen handle) { - foreach (var layer in _parallaxManager.GetParallaxLayers("FastSpace")) + var currentTime = (float) _timing.RealTime.TotalSeconds; + var offset = Offset + new Vector2(currentTime * SpeedX, currentTime * SpeedY); + + foreach (var layer in _parallaxManager.GetParallaxLayers(_parallaxPrototype)) { var tex = layer.Texture; - var texSize = (tex.Size.X * (int) Size.X, tex.Size.Y * (int) Size.X) * layer.Config.Scale.Floored() / 1920; + var texSize = new Vector2i( + (int)(tex.Size.X * Size.X * layer.Config.Scale.X / 1920 * ScaleX), + (int)(tex.Size.Y * Size.X * layer.Config.Scale.Y / 1920 * ScaleY) + ); var ourSize = PixelSize; - var currentTime = (float) _timing.RealTime.TotalSeconds; - var offset = Offset + new Vector2(currentTime * 100f, currentTime * 0f); + //Protection from division by zero. + texSize.X = Math.Max(texSize.X, 1); + texSize.Y = Math.Max(texSize.Y, 1); if (layer.Config.Tiled) { diff --git a/Content.Client/UserInterface/Systems/MenuBar/GameTopMenuBarUIController.cs b/Content.Client/UserInterface/Systems/MenuBar/GameTopMenuBarUIController.cs index afb45ec172..4ac49ceebc 100644 --- a/Content.Client/UserInterface/Systems/MenuBar/GameTopMenuBarUIController.cs +++ b/Content.Client/UserInterface/Systems/MenuBar/GameTopMenuBarUIController.cs @@ -24,7 +24,7 @@ public sealed class GameTopMenuBarUIController : UIController [Dependency] private readonly SandboxUIController _sandbox = default!; [Dependency] private readonly GuidebookUIController _guidebook = default!; [Dependency] private readonly EmotesUIController _emotes = default!; - [Dependency] private readonly CP14KnowledgeUIController _knowledge = default!; //CP14 + [Dependency] private readonly CP14SkillUIController _skill = default!; //CP14 private GameTopMenuBar? GameTopMenuBar => UIManager.GetActiveUIWidgetOrNull(); @@ -48,7 +48,7 @@ public sealed class GameTopMenuBarUIController : UIController _action.UnloadButton(); _sandbox.UnloadButton(); _emotes.UnloadButton(); - _knowledge.UnloadButton(); //CP14 + _skill.UnloadButton(); //CP14 } public void LoadButtons() @@ -62,6 +62,6 @@ public sealed class GameTopMenuBarUIController : UIController _action.LoadButton(); _sandbox.LoadButton(); _emotes.LoadButton(); - _knowledge.LoadButton(); //CP14 + _skill.LoadButton(); //CP14 } } diff --git a/Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml b/Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml index aaf230d266..9bcf64cf0e 100644 --- a/Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml +++ b/Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml @@ -35,11 +35,11 @@ /> ? OnKnowledgeUpdate; - - public override void Initialize() - { - base.Initialize(); - - SubscribeNetworkEvent(OnCharacterKnowledgeEvent); - } - - public void RequestKnowledgeInfo() - { - var entity = _players.LocalEntity; - if (entity is null) - return; - - RaiseNetworkEvent(new CP14RequestKnowledgeInfoEvent(GetNetEntity(entity.Value))); - } - - private void OnCharacterKnowledgeEvent(CP14KnowledgeInfoEvent msg, EntitySessionEventArgs args) - { - var entity = GetEntity(msg.NetEntity); - var data = new KnowledgeData(entity, msg.AllKnowledge); - - OnKnowledgeUpdate?.Invoke(data); - } - - public readonly record struct KnowledgeData( - EntityUid Entity, - HashSet> AllKnowledge - ); -} diff --git a/Content.Client/_CP14/Skill/CP14ClientSkillSystem.cs b/Content.Client/_CP14/Skill/CP14ClientSkillSystem.cs new file mode 100644 index 0000000000..f21dccbccf --- /dev/null +++ b/Content.Client/_CP14/Skill/CP14ClientSkillSystem.cs @@ -0,0 +1,54 @@ +using Content.Shared._CP14.Skill; +using Content.Shared._CP14.Skill.Components; +using Content.Shared._CP14.Skill.Prototypes; +using Robust.Client.Player; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; + +namespace Content.Client._CP14.Skill; + +public sealed partial class CP14ClientSkillSystem : CP14SharedSkillSystem +{ + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + + public event Action? OnSkillUpdate; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAfterAutoHandleState); + } + + private void OnAfterAutoHandleState(Entity ent, ref AfterAutoHandleStateEvent args) + { + OnSkillUpdate?.Invoke(ent.Owner); + } + + public void RequestSkillData() + { + var localPlayer = _playerManager.LocalEntity; + + if (!HasComp(localPlayer)) + return; + + OnSkillUpdate?.Invoke(localPlayer.Value); + } + + public void RequestLearnSkill(EntityUid? target, CP14SkillPrototype? skill) + { + if (skill == null || target == null) + return; + + var netEv = new CP14TryLearnSkillMessage(GetNetEntity(target.Value), skill.ID); + RaiseNetworkEvent(netEv); + + if (_proto.TryIndex(skill.Tree, out var indexedTree)) + { + _audio.PlayGlobal(indexedTree.LearnSound, target.Value, AudioParams.Default.WithVolume(6f)); + } + } +} diff --git a/Content.Client/_CP14/Skill/Ui/CP14SkillTreeButtonControl.xaml b/Content.Client/_CP14/Skill/Ui/CP14SkillTreeButtonControl.xaml new file mode 100644 index 0000000000..5cb6f98560 --- /dev/null +++ b/Content.Client/_CP14/Skill/Ui/CP14SkillTreeButtonControl.xaml @@ -0,0 +1,24 @@ + + + + + + diff --git a/Content.Client/_CP14/Skill/Ui/CP14SkillTreeButtonControl.xaml.cs b/Content.Client/_CP14/Skill/Ui/CP14SkillTreeButtonControl.xaml.cs new file mode 100644 index 0000000000..28b19fd3c3 --- /dev/null +++ b/Content.Client/_CP14/Skill/Ui/CP14SkillTreeButtonControl.xaml.cs @@ -0,0 +1,22 @@ +using Robust.Client.AutoGenerated; +using Robust.Client.Graphics; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client._CP14.Skill.Ui; + +[GenerateTypedNameReferences] +public sealed partial class CP14SkillTreeButtonControl : Control +{ + public event Action? OnPressed; + + public CP14SkillTreeButtonControl(Color color, string label) + { + RobustXamlLoader.Load(this); + + ColorPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = color }; + SkillTreeLabel.Text = label; + + MainButton.OnPressed += args => OnPressed?.Invoke(); + } +} diff --git a/Content.Client/_CP14/Skill/Ui/CP14SkillTreeGraphControl.xaml b/Content.Client/_CP14/Skill/Ui/CP14SkillTreeGraphControl.xaml new file mode 100644 index 0000000000..4ffd0bde07 --- /dev/null +++ b/Content.Client/_CP14/Skill/Ui/CP14SkillTreeGraphControl.xaml @@ -0,0 +1,7 @@ + diff --git a/Content.Client/_CP14/Skill/Ui/CP14SkillTreeGraphControl.xaml.cs b/Content.Client/_CP14/Skill/Ui/CP14SkillTreeGraphControl.xaml.cs new file mode 100644 index 0000000000..a5a55c27f9 --- /dev/null +++ b/Content.Client/_CP14/Skill/Ui/CP14SkillTreeGraphControl.xaml.cs @@ -0,0 +1,218 @@ +using System.Linq; +using System.Numerics; +using Content.Shared._CP14.Skill; +using Content.Shared._CP14.Skill.Components; +using Content.Shared._CP14.Skill.Prototypes; +using Robust.Client.AutoGenerated; +using Robust.Client.Graphics; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Client.Utility; +using Robust.Shared.Input; +using Robust.Shared.Prototypes; + +namespace Content.Client._CP14.Skill.Ui; + +[GenerateTypedNameReferences] +public sealed partial class CP14SkillTreeGraphControl : BoxContainer +{ + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + private readonly CP14SharedSkillSystem _skillSystem; + + private Entity? _player; + + private IEnumerable _allSkills; + + private CP14SkillPrototype? _hoveredNode; + private CP14SkillPrototype? _selectedNode; + public CP14SkillTreePrototype? Tree; + + private bool dragging = false; + private Vector2 _previousMousePosition = Vector2.Zero; + private Vector2 _globalOffset = new (60,60); + + private const float GridSize = 25f; + + public event Action? OnNodeSelected; + public event Action? OnOffsetChanged; + + public CP14SkillTreeGraphControl() + { + IoCManager.InjectDependencies(this); + RobustXamlLoader.Load(this); + + _skillSystem = _entManager.System(); + + _allSkills = _proto.EnumeratePrototypes(); + _proto.PrototypesReloaded += _ => _allSkills = _proto.EnumeratePrototypes().OrderBy(skill => _skillSystem.GetSkillName(skill)).ToList(); + OnOffsetChanged?.Invoke(_globalOffset); + } + + public void UpdateState(Entity? player) + { + _player = player; + OnOffsetChanged?.Invoke(_globalOffset); + } + + protected override void KeyBindDown(GUIBoundKeyEventArgs args) + { + base.KeyBindDown(args); + + if (args.Handled) + return; + + if (args.Function == EngineKeyFunctions.UIClick) + { + dragging = true; + + if (_hoveredNode == null) + return; + + OnNodeSelected?.Invoke(_hoveredNode); + UserInterfaceManager.ClickSound(); + _selectedNode = _hoveredNode; + } + + if (args.Function == EngineKeyFunctions.UIRightClick) + { + _globalOffset = new Vector2(60, 60); // Reset offset on right click + OnOffsetChanged?.Invoke(_globalOffset); + } + } + + protected override void KeyBindUp(GUIBoundKeyEventArgs args) + { + base.KeyBindUp(args); + + if (args.Handled || args.Function != EngineKeyFunctions.UIClick) + return; + + dragging = false; + } + + protected override void ExitedTree() + { + base.ExitedTree(); + + OnNodeSelected?.Invoke(null); + } + + protected override void Draw(DrawingHandleScreen handle) + { + base.Draw(handle); + + _hoveredNode = null; + if (_player == null || Tree == null) + { + return; + } + + var cursor = (UserInterfaceManager.MousePositionScaled.Position * UIScale) - GlobalPixelPosition; + + if (dragging) + { + var delta = cursor - _previousMousePosition; + _globalOffset += delta; + OnOffsetChanged?.Invoke(_globalOffset); + } + + _previousMousePosition = cursor; + + var playerSkills = _player.Value.Comp.LearnedSkills; + + //Draw connection lines + foreach (var skill in _allSkills) + { + if (skill.Tree != Tree) + continue; + + var fromPos = skill.SkillUiPosition * GridSize * UIScale + _globalOffset; + + foreach (var prerequisite in skill.Prerequisites) + { + if (!_proto.TryIndex(prerequisite, out var prerequisiteSkill)) + continue; + + if (prerequisiteSkill.Tree != Tree) + continue; + + var learned = playerSkills.Contains(skill.ID); + + var toPos = prerequisiteSkill.SkillUiPosition * GridSize * UIScale + _globalOffset; + var color = learned ? Color.White : Color.FromSrgb(new Color(0.7f, 0.7f, 0.7f)); + handle.DrawLine(fromPos, toPos, color); + } + } + + //Draw skill icons over lines + foreach (var skill in _allSkills) + { + if (skill.Tree != Tree) + continue; + + //TODO: Not optimized, recalculates every frame. Needs to be calculated on state update and cached. + var learned = playerSkills.Contains(skill.ID); + var available = _skillSystem.CanLearnSkill(_player.Value, skill.ID); + var canBeLearned = learned || skill.Prerequisites.All(prerequisite => playerSkills.Contains(prerequisite)); + var pos = skill.SkillUiPosition * GridSize * UIScale + _globalOffset; + + // Base skill icon + var baseTexture = skill.Icon.Frame0(); + var baseSize = new Vector2(baseTexture.Width, baseTexture.Height) * 2; + var baseQuad = new UIBox2(pos - baseSize / 2, pos + baseSize / 2); + + var hovered = (cursor - pos).LengthSquared() <= (baseSize.X / 2) * (baseSize.X / 2); + + // Frame + var frameTexture = Tree.FrameIcon.Frame0(); + var frameSize = new Vector2(frameTexture.Width, frameTexture.Height) * 2; + var frameQuad = new UIBox2(pos - frameSize / 2, pos + frameSize / 2); + handle.DrawTextureRect(frameTexture, frameQuad, canBeLearned ? Color.White : Color.FromSrgb(new Color(0.7f, 0.7f, 0.7f))); + + // Selected Skill + if (_selectedNode == skill) + { + var selectedTexture = Tree.SelectedIcon.Frame0(); + var selectedSize = new Vector2(selectedTexture.Width, selectedTexture.Height) * 2; + var selectedQuad = new UIBox2(pos - selectedSize / 2, pos + selectedSize / 2); + handle.DrawTextureRect(selectedTexture, selectedQuad, Color.White); + } + + // Hovered Skill + if (hovered) + { + _hoveredNode = skill; + var hoveredTexture = Tree.HoveredIcon.Frame0(); + var hoveredSize = new Vector2(hoveredTexture.Width, hoveredTexture.Height) * 2; + var hoveredQuad = new UIBox2(pos - hoveredSize / 2, pos + hoveredSize / 2); + handle.DrawTextureRect(hoveredTexture, hoveredQuad, Color.White); + } + + // Learned Skill + if (learned) + { + var learnedTexture = Tree.LearnedIcon.Frame0(); + var learnedSize = new Vector2(learnedTexture.Width, learnedTexture.Height) * 2; + var learnedQuad = new UIBox2(pos - learnedSize / 2, pos + learnedSize / 2); + handle.DrawTextureRect(learnedTexture, learnedQuad, Color.White); + } + else if (available) + { + var availableTexture = Tree.AvailableIcon.Frame0(); + var availableSize = new Vector2(availableTexture.Width, availableTexture.Height) * 2; + var availableQuad = new UIBox2(pos - availableSize / 2, pos + availableSize / 2); + handle.DrawTextureRect(availableTexture, availableQuad, Color.White); + } + + var iconColor = Color.White; + if (!learned) + iconColor = Color.FromSrgb(new Color(0.7f, 0.7f, 0.7f)); + if (!canBeLearned && !learned) + iconColor = Color.FromSrgb(new Color(0f, 0f, 0f)); + + handle.DrawTextureRect(baseTexture, baseQuad, iconColor); + } + } +} diff --git a/Content.Client/_CP14/UserInterface/Systems/Knowledge/CP14KnowledgeUIController.cs b/Content.Client/_CP14/UserInterface/Systems/Knowledge/CP14KnowledgeUIController.cs deleted file mode 100644 index 188a10b666..0000000000 --- a/Content.Client/_CP14/UserInterface/Systems/Knowledge/CP14KnowledgeUIController.cs +++ /dev/null @@ -1,158 +0,0 @@ -using Content.Client._CP14.Knowledge; -using Content.Client._CP14.UserInterface.Systems.Knowledge.Windows; -using Content.Client.Gameplay; -using Content.Client.UserInterface.Controls; -using Content.Shared.Input; -using JetBrains.Annotations; -using Robust.Client.Player; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controllers; -using Robust.Client.UserInterface.Controls; -using Robust.Shared.Input.Binding; -using Robust.Shared.Prototypes; -using Robust.Shared.Utility; - -namespace Content.Client.UserInterface.Systems.Character; - -[UsedImplicitly] -public sealed class CP14KnowledgeUIController : UIController, IOnStateEntered, IOnStateExited, - IOnSystemChanged -{ - [Dependency] private readonly IPlayerManager _player = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [UISystemDependency] private readonly ClientCP14KnowledgeSystem _knowledge = default!; - - private CP14KnowledgeWindow? _window; - - private MenuButton? KnowledgeButton => UIManager.GetActiveUIWidgetOrNull()?.CP14KnowledgeButton; - - public void OnStateEntered(GameplayState state) - { - DebugTools.Assert(_window == null); - - _window = UIManager.CreateWindow(); - LayoutContainer.SetAnchorPreset(_window, LayoutContainer.LayoutPreset.CenterTop); - - CommandBinds.Builder - .Bind(ContentKeyFunctions.CP14OpenKnowledgeMenu, - InputCmdHandler.FromDelegate(_ => ToggleWindow())) - .Register(); - } - - public void OnStateExited(GameplayState state) - { - if (_window != null) - { - _window.Dispose(); - _window = null; - } - - CommandBinds.Unregister(); - } - - public void OnSystemLoaded(ClientCP14KnowledgeSystem system) - { - system.OnKnowledgeUpdate += KnowledgeUpdated; - _player.LocalPlayerDetached += CharacterDetached; - } - - public void OnSystemUnloaded(ClientCP14KnowledgeSystem system) - { - system.OnKnowledgeUpdate -= KnowledgeUpdated; - _player.LocalPlayerDetached -= CharacterDetached; - } - - public void UnloadButton() - { - if (KnowledgeButton is null) - return; - - KnowledgeButton.OnPressed -= KnowledgeButtonPressed; - } - - public void LoadButton() - { - if (KnowledgeButton is null) - return; - - KnowledgeButton.OnPressed += KnowledgeButtonPressed; - - if (_window is null) - return; - - _window.OnClose += DeactivateButton; - _window.OnOpen += ActivateButton; - } - - private void DeactivateButton() - { - KnowledgeButton!.Pressed = false; - } - - private void ActivateButton() - { - KnowledgeButton!.Pressed = true; - } - - private void KnowledgeUpdated(ClientCP14KnowledgeSystem.KnowledgeData data) - { - if (_window is null) - return; - - _window.KnowledgeContent.RemoveAllChildren(); - - var (entity, allKnowledge) = data; - - foreach (var knowledge in allKnowledge) - { - if (!_proto.TryIndex(knowledge, out var indexedKnowledge)) - continue; - - var knowledgeButton = new Button() - { - Access = AccessLevel.Public, - Text = Loc.GetString(indexedKnowledge.Name), - ToolTip = Loc.GetString(indexedKnowledge.Desc), - TextAlign = Label.AlignMode.Center, - }; - - _window.KnowledgeContent.AddChild(knowledgeButton); - } - } - - private void CharacterDetached(EntityUid uid) - { - CloseWindow(); - } - - private void KnowledgeButtonPressed(BaseButton.ButtonEventArgs args) - { - ToggleWindow(); - } - - private void CloseWindow() - { - _window?.Close(); - } - - private void ToggleWindow() - { - if (_window == null) - return; - - if (KnowledgeButton != null) - { - KnowledgeButton.SetClickPressed(!_window.IsOpen); - } - - if (_window.IsOpen) - { - CloseWindow(); - } - else - { - _knowledge.RequestKnowledgeInfo(); - _window.Open(); - } - } -} diff --git a/Content.Client/_CP14/UserInterface/Systems/Knowledge/Windows/CP14KnowledgeWindow.xaml b/Content.Client/_CP14/UserInterface/Systems/Knowledge/Windows/CP14KnowledgeWindow.xaml deleted file mode 100644 index 419d0f56ce..0000000000 --- a/Content.Client/_CP14/UserInterface/Systems/Knowledge/Windows/CP14KnowledgeWindow.xaml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - diff --git a/Content.Client/_CP14/UserInterface/Systems/Skill/CP14SkillUIController.cs b/Content.Client/_CP14/UserInterface/Systems/Skill/CP14SkillUIController.cs new file mode 100644 index 0000000000..23f63b182a --- /dev/null +++ b/Content.Client/_CP14/UserInterface/Systems/Skill/CP14SkillUIController.cs @@ -0,0 +1,249 @@ +using System.Linq; +using System.Numerics; +using Content.Client._CP14.Skill; +using Content.Client._CP14.Skill.Ui; +using Content.Client._CP14.UserInterface.Systems.Skill.Window; +using Content.Client.Gameplay; +using Content.Client.UserInterface.Controls; +using Content.Shared._CP14.Skill.Components; +using Content.Shared._CP14.Skill.Prototypes; +using Content.Shared.Input; +using JetBrains.Annotations; +using Robust.Client.Player; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controllers; +using Robust.Client.UserInterface.Controls; +using Robust.Client.Utility; +using Robust.Shared.Input.Binding; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Client.UserInterface.Systems.Character; + +[UsedImplicitly] +public sealed class CP14SkillUIController : UIController, IOnStateEntered, IOnStateExited, + IOnSystemChanged +{ + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [UISystemDependency] private readonly CP14ClientSkillSystem _skill = default!; + + private CP14SkillWindow? _window; + private CP14SkillPrototype? _selectedSkill; + + private MenuButton? SkillButton => UIManager.GetActiveUIWidgetOrNull()?.CP14SkillButton; + + public void OnStateEntered(GameplayState state) + { + DebugTools.Assert(_window == null); + + _window = UIManager.CreateWindow(); + LayoutContainer.SetAnchorPreset(_window, LayoutContainer.LayoutPreset.CenterTop); + + CommandBinds.Builder + .Bind(ContentKeyFunctions.CP14OpenSkillMenu, + InputCmdHandler.FromDelegate(_ => ToggleWindow())) + .Register(); + + _window.LearnButton.OnPressed += _ => _skill.RequestLearnSkill(_player.LocalEntity, _selectedSkill); + _window.GraphControl.OnNodeSelected += SelectNode; + _window.GraphControl.OnOffsetChanged += offset => + { + _window.ParallaxBackground.Offset = -offset * 0.25f + new Vector2(1000,1000); //hardcoding is bad + }; + } + + + public void OnStateExited(GameplayState state) + { + if (_window != null) + { + _window.GraphControl.OnNodeSelected -= SelectNode; + + _window.Dispose(); + _window = null; + } + + CommandBinds.Unregister(); + } + + public void OnSystemLoaded(CP14ClientSkillSystem system) + { + system.OnSkillUpdate += UpdateState; + _player.LocalPlayerDetached += CharacterDetached; + } + + public void OnSystemUnloaded(CP14ClientSkillSystem system) + { + system.OnSkillUpdate -= UpdateState; + _player.LocalPlayerDetached -= CharacterDetached; + } + + public void UnloadButton() + { + if (SkillButton is null) + return; + + SkillButton.OnPressed -= SkillButtonPressed; + } + + public void LoadButton() + { + if (SkillButton is null) + return; + + SkillButton.OnPressed += SkillButtonPressed; + + if (_window is null) + return; + + _window.OnClose += DeactivateButton; + _window.OnOpen += ActivateButton; + } + + private void DeactivateButton() + { + SkillButton!.Pressed = false; + } + + private void ActivateButton() + { + SkillButton!.Pressed = true; + } + + private void SelectNode(CP14SkillPrototype? skill) + { + if (_window is null) + return; + + if (_player.LocalEntity == null) + return; + + _selectedSkill = skill; + + if (skill == null) + { + _window.SkillName.Text = string.Empty; + _window.SkillDescription.Text = string.Empty; + _window.SkillView.Texture = null; + _window.LearnButton.Disabled = true; + } + else + { + _window.SkillName.Text = _skill.GetSkillName(skill); + _window.SkillDescription.SetMessage(GetSkillDescription(skill)); + _window.SkillView.Texture = skill.Icon.Frame0(); + _window.LearnButton.Disabled = !_skill.CanLearnSkill(_player.LocalEntity.Value, skill); + _window.SkillCost.Text = skill.LearnCost.ToString(); + } + } + + private FormattedMessage GetSkillDescription(CP14SkillPrototype skill) + { + var msg = new FormattedMessage(); + + //Description + msg.TryAddMarkup(_skill.GetSkillDescription(skill) + "\n", out _); + + return msg; + } + + private void UpdateState(EntityUid player) + { + if (_window is null) + return; + + if (!EntityManager.TryGetComponent(player, out var storage)) + return; + + _window.GraphControl.UpdateState((player, storage)); + + // Reselect for update state + SelectNode(_selectedSkill); + + //If tree not selected, select the first one + if (_window.GraphControl.Tree == null && storage.Progress.Count > 0) + { + var firstTree = storage.Progress.First().Key; + + if (_proto.TryIndex(firstTree, out var indexedTree)) + { + SelectTree(indexedTree, storage); // Set the first tree from the player's progress + } + } + + // Update the experience points for the selected tree + var playerProgress = storage.Progress; + if (_window.GraphControl.Tree is not null && playerProgress.TryGetValue(_window.GraphControl.Tree, out var skillpoint)) + { + _window.ExpPointLabel.Text = skillpoint.ToString(); + } + + _window.LevelLabel.Text = $"{storage.SkillsSumExperience}/{storage.ExperienceMaxCap}"; + + _window.TreeTabsContainer.RemoveAllChildren(); + foreach (var (tree, progress) in storage.Progress) + { + if (!_proto.TryIndex(tree, out var indexedTree)) + continue; + + var treeButton2 = new CP14SkillTreeButtonControl(indexedTree.Color, Loc.GetString(indexedTree.Name)); + treeButton2.ToolTip = Loc.GetString(indexedTree.Desc ?? string.Empty); + treeButton2.OnPressed += () => + { + SelectTree(indexedTree, storage); + }; + + _window.TreeTabsContainer.AddChild(treeButton2); + } + } + + private void SelectTree(CP14SkillTreePrototype tree, CP14SkillStorageComponent storage) + { + if (_window == null) + return; + + _window.GraphControl.Tree = tree; + _window.ParallaxBackground.ParallaxPrototype = tree.Parallax; + _window.TreeName.Text = Loc.GetString(tree.Name); + + var playerProgress = storage.Progress; + _window.ExpPointLabel.Text = playerProgress.TryGetValue(tree, out var skillpoint) ? skillpoint.ToString() : "0"; + } + + private void CharacterDetached(EntityUid uid) + { + CloseWindow(); + } + + private void SkillButtonPressed(BaseButton.ButtonEventArgs args) + { + ToggleWindow(); + } + + private void CloseWindow() + { + _window?.Close(); + } + + private void ToggleWindow() + { + if (_window == null) + return; + + if (SkillButton != null) + { + SkillButton.SetClickPressed(!_window.IsOpen); + } + + if (_window.IsOpen) + { + CloseWindow(); + } + else + { + _skill.RequestSkillData(); + _window.Open(); + } + } +} diff --git a/Content.Client/_CP14/UserInterface/Systems/Skill/Window/CP14SkillWindow.xaml b/Content.Client/_CP14/UserInterface/Systems/Skill/Window/CP14SkillWindow.xaml new file mode 100644 index 0000000000..275f810555 --- /dev/null +++ b/Content.Client/_CP14/UserInterface/Systems/Skill/Window/CP14SkillWindow.xaml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +