Skill progression system (#1263)

* delete skill trees

* Revert "delete skill trees"

This reverts commit 9d7fae73c4.

* learning refactor

* UI tweaks

* sword mastery skill

* telegraphy

* rapier mastery

* research table ui

* finish studing

* polish UI researching

* pyrokinetic

* more skill tree working

* heat adapt

* alchemist and metamagic update

* skill multiple effects support + metamagic bugg manapool

* impossible 😢

* skimitar gaming

* skimidi

* blacksmithing branch

* remove research restrictions

* remove species magic buff

* fix loc

* Update thaumaturgy.yml

* pip

* Delete skill_tree.yml
This commit is contained in:
Ed
2025-05-14 12:43:43 +03:00
committed by GitHub
parent 9e409a1eaf
commit be495f274c
165 changed files with 2987 additions and 778 deletions

View File

@@ -0,0 +1,14 @@
<Control xmlns="https://spacestation14.io">
<Button Name="Button">
<GridContainer Columns="2">
<TextureRect Name="View"
Stretch="KeepAspectCentered"
Margin="0,0,4,0"
MinSize="48 48"
MaxSize="48 48"
HorizontalAlignment="Center"
VerticalExpand="True" />
<Label Name="Name" />
</GridContainer>
</Button>
</Control>

View File

@@ -0,0 +1,60 @@
using Content.Shared._CP14.Skill;
using Content.Shared._CP14.Skill.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client._CP14.ResearchTable;
[GenerateTypedNameReferences]
public sealed partial class CP14ResearchRecipeControl : Control
{
[Dependency] private readonly IEntityManager _entity = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
public event Action<CP14ResearchUiEntry, CP14SkillPrototype>? OnResearch;
private readonly SpriteSystem _sprite;
private readonly CP14SharedSkillSystem _skillSystem;
private readonly CP14SkillPrototype _skillPrototype;
private readonly bool _craftable;
public CP14ResearchRecipeControl(CP14ResearchUiEntry entry)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_sprite = _entity.System<SpriteSystem>();
_skillSystem = _entity.System<CP14SharedSkillSystem>();
_skillPrototype = _prototype.Index(entry.ProtoId);
_craftable = entry.Craftable;
Button.OnPressed += _ => OnResearch?.Invoke(entry, _skillPrototype);
UpdateColor();
UpdateName();
UpdateView();
}
private void UpdateColor()
{
if (_craftable)
return;
Button.ModulateSelfOverride = Color.FromHex("#302622");
}
private void UpdateName()
{
Name.Text = $"{Loc.GetString(_skillSystem.GetSkillName(_skillPrototype))}" ;
}
private void UpdateView()
{
View.Texture =_sprite.Frame0(_skillPrototype.Icon);
}
}

View File

@@ -0,0 +1,34 @@
using Content.Shared._CP14.Skill;
using Robust.Client.UserInterface;
namespace Content.Client._CP14.ResearchTable;
public sealed class CP14ResearchTableBoundUserInterface : BoundUserInterface
{
private CP14ResearchTableWindow? _window;
public CP14ResearchTableBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
_window = this.CreateWindow<CP14ResearchTableWindow>();
_window.OnResearch += entry => SendMessage(new CP14ResearchMessage(entry.ProtoId));
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
switch (state)
{
case CP14ResearchTableUiState recipesState:
_window?.UpdateState(recipesState);
break;
}
}
}

View File

@@ -0,0 +1,72 @@
<DefaultWindow xmlns="https://spacestation14.io"
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'cp14-research-table-title'}"
SetSize="700 500"
MinSize="700 300">
<BoxContainer Orientation="Horizontal">
<!-- Main -->
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Horizontal">
<!-- Product list (left side UI) -->
<BoxContainer MinWidth="350" HorizontalExpand="True" Orientation="Vertical" Margin="0 0 10 0">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<!-- Search Bar -->
<LineEdit Name="SearchBar" Margin="4" PlaceHolder="Search" HorizontalExpand="True" />
<OptionButton Name="OptionCategories" Access="Public" MinSize="130 0" />
</BoxContainer>
<!-- Crafts container -->
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" MinSize="0 200">
<BoxContainer Name="CraftsContainer" Orientation="Vertical" HorizontalExpand="True" />
</ScrollContainer>
</BoxContainer>
<!-- Craft view (right side UI) -->
<BoxContainer MinWidth="350" Orientation="Vertical" HorizontalExpand="True"
VerticalExpand="True">
<PanelContainer HorizontalExpand="True" VerticalExpand="True">
<!-- Background -->
<PanelContainer.PanelOverride>
<graphics:StyleBoxFlat BackgroundColor="#41332f" />
</PanelContainer.PanelOverride>
<!-- Content -->
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
<!-- Item info -->
<!-- icon -->
<TextureRect Name="ItemView"
Stretch="KeepAspectCentered"
Margin="5"
MinSize="64 64"
MaxSize="64 64"
HorizontalAlignment="Left" />
<!-- name & description -->
<BoxContainer Margin="5 0 0 0" HorizontalExpand="True" VerticalExpand="True"
Orientation="Vertical">
<RichTextLabel Name="ItemName" Margin="5" />
<RichTextLabel Name="ItemDescription" Margin="5" />
</BoxContainer>
<controls:HLine Color="#404040" Thickness="2" Margin="0 5" />
<!-- Required title -->
<Label Margin="5 0 0 0" Text="{Loc 'cp14-research-recipe-list'}" />
<!-- Craft requirements content -->
<!-- Added by code -->
<BoxContainer
Margin="5 0 0 0"
Name="ItemRequirements"
Orientation="Vertical" VerticalExpand="True"
HorizontalExpand="True" />
<controls:HLine Color="#404040" Thickness="5" Margin="0 5" />
<!-- Craft button -->
<Button Name="CraftButton" Text="{Loc 'cp14-research-craft'}" />
</BoxContainer>
</PanelContainer>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</DefaultWindow>

View File

@@ -0,0 +1,235 @@
using Content.Client._CP14.Workbench;
using Content.Shared._CP14.Skill;
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared._CP14.Skill.Restrictions;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client._CP14.ResearchTable;
[GenerateTypedNameReferences]
public sealed partial class CP14ResearchTableWindow : DefaultWindow
{
private const int AllCategoryId = -1;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly ILogManager _log = default!;
[Dependency] private readonly IEntityManager _entity = default!;
private readonly SpriteSystem _sprite;
private readonly CP14SharedSkillSystem _skillSystem;
public event Action<CP14ResearchUiEntry>? OnResearch;
private readonly Dictionary<int, LocId> _categories = new();
private CP14ResearchTableUiState? _cachedState;
private CP14ResearchUiEntry? _selectedEntry;
private string _searchFilter = string.Empty;
private ISawmill Sawmill { get; init; }
public CP14ResearchTableWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_sprite = _entity.System<SpriteSystem>();
_skillSystem = _entity.System<CP14SharedSkillSystem>();
Sawmill = _log.GetSawmill("cp14_research_table_window");
SearchBar.OnTextChanged += OnSearchChanged;
CraftButton.OnPressed += OnCraftPressed;
OptionCategories.OnItemSelected += OnCategoryItemSelected;
}
public void UpdateRecipesVisibility()
{
if (_cachedState is null)
return;
CraftsContainer.RemoveAllChildren();
var recipes = new List<CP14ResearchUiEntry>();
foreach (var entry in _cachedState.Skills)
{
if (!_prototype.TryIndex(entry.ProtoId, out var indexedEntry))
{
Sawmill.Error($"No recipe prototype {entry.ProtoId} retrieved from cache found");
continue;
}
if (!ProcessSearchFilter(indexedEntry))
continue;
if (!ProcessSearchCategoryFilter(indexedEntry))
continue;
recipes.Add(entry);
}
recipes.Sort(CP14ResearchUiEntry.CompareTo);
foreach (var recipe in recipes)
{
var control = new CP14ResearchRecipeControl(recipe);
control.OnResearch += RecipeSelect;
CraftsContainer.AddChild(control);
}
if (_selectedEntry is not null && !recipes.Contains(_selectedEntry.Value))
RecipeSelectNull();
}
public void UpdateState(CP14ResearchTableUiState recipesState)
{
_cachedState = recipesState;
_categories.Clear();
OptionCategories.Clear();
OptionCategories.AddItem(Loc.GetString("cp14-recipe-category-all"), AllCategoryId);
var categories = new List<LocId>();
var count = 0;
foreach (var skill in recipesState.Skills)
{
if (!_prototype.TryIndex(skill.ProtoId, out var indexedSkill))
continue;
if (!_prototype.TryIndex(indexedSkill.Tree, out var indexedTree))
continue;
if (categories.Contains(indexedTree.Name))
continue;
categories.Add(indexedTree.Name);
}
categories.Sort((a, b) => string.Compare(Loc.GetString(a), Loc.GetString(b), StringComparison.Ordinal));
foreach (var category in categories)
{
OptionCategories.AddItem(Loc.GetString(category), count);
_categories.Add(count, category);
count++;
}
UpdateRecipesVisibility();
}
private void OnSearchChanged(LineEdit.LineEditEventArgs _)
{
_searchFilter = SearchBar.Text.Trim().ToLowerInvariant();
UpdateRecipesVisibility();
}
private void OnCraftPressed(BaseButton.ButtonEventArgs _)
{
if (_selectedEntry is null)
return;
OnResearch?.Invoke(_selectedEntry.Value);
}
private void OnCategoryItemSelected(OptionButton.ItemSelectedEventArgs obj)
{
OptionCategories.SelectId(obj.Id);
UpdateRecipesVisibility();
}
private bool ProcessSearchFilter(CP14SkillPrototype indexedEntry)
{
if (_searchFilter == string.Empty)
return true;
return Loc.GetString(_skillSystem.GetSkillName(indexedEntry)).Contains(_searchFilter);
}
private bool ProcessSearchCategoryFilter(CP14SkillPrototype indexedEntry)
{
// If we are searching through all categories, we simply skip the current filter
if (OptionCategories.SelectedId == AllCategoryId)
return true;
if (!_categories.TryGetValue(OptionCategories.SelectedId, out var selectedCategory))
{
Sawmill.Error($"Non-existent {OptionCategories.SelectedId} category id selected. Filter skipped");
return true;
}
if (!_prototype.TryIndex(indexedEntry.Tree, out var indexedTree))
{
Sawmill.Error($"Non-existent {indexedEntry.Tree} category prototype id. Filter skipped");
return true;
}
return indexedTree.Name == selectedCategory;
}
private void RecipeSelect(CP14ResearchTableUiState recipesState)
{
foreach (var skill in recipesState.Skills)
{
RecipeSelect(skill, _prototype.Index(skill.ProtoId));
break;
}
}
private void RecipeSelect(CP14ResearchUiEntry cachedEntry)
{
if (_cachedState is null)
return;
if (_cachedState.Skills.Contains(cachedEntry))
{
Sawmill.Warning($"The selected cache option {cachedEntry} isn't found in recipes");
return;
}
RecipeSelect(cachedEntry, _prototype.Index(cachedEntry.ProtoId));
}
private void RecipeSelect(CP14ResearchUiEntry entry, CP14SkillPrototype skill)
{
_selectedEntry = entry;
ItemView.Texture = _sprite.Frame0(skill.Icon);
ItemName.Text = _skillSystem.GetSkillName(skill);
ItemDescription.Text = _skillSystem.GetSkillDescription(skill);
ItemRequirements.RemoveAllChildren();
foreach (var restriction in skill.Restrictions)
{
switch (restriction)
{
case Researched researched:
foreach (var requirement in researched.Requirements)
{
ItemRequirements.AddChild(new CP14WorkbenchRequirementControl(requirement));
}
break;
}
}
CraftButton.Disabled = !entry.Craftable;
}
private void RecipeSelectNull()
{
_selectedEntry = null;
ItemView.Texture = null;
ItemName.Text = string.Empty;
ItemDescription.Text = string.Empty;
ItemRequirements.RemoveAllChildren();
CraftButton.Disabled = true;
}
}

View File

@@ -7,13 +7,9 @@
VerticalExpand="True"
StyleClasses="OpenRight"
Margin="0 0 -1 0">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<BoxContainer Orientation="Vertical"
VerticalExpand="True"
HorizontalExpand="True"
Margin="-5 0 0 0">
<Label Name="SkillTreeLabel" />
</BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" HorizontalAlignment="Right">
<TextureRect Name="SkillPointImage" VerticalAlignment="Center" Visible="False" HorizontalAlignment="Center" Margin="0 0 8 0" TextureScale="2, 2" TexturePath="/Textures/_CP14/Interface/Misc/skillpoint.png"/>
<Label Name="SkillTreeLabel" Margin="-5 0 0 0"/>
</BoxContainer>
</Button>
<PanelContainer Name="ColorPanel"

View File

@@ -10,12 +10,21 @@ public sealed partial class CP14SkillTreeButtonControl : Control
{
public event Action? OnPressed;
public CP14SkillTreeButtonControl(Color color, string label)
public CP14SkillTreeButtonControl(Color color, string label, float skillpoints)
{
RobustXamlLoader.Load(this);
ColorPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = color };
SkillTreeLabel.Text = label;
if (skillpoints > 0)
{
SkillPointImage.Visible = true;
SkillTreeLabel.Text = $"{skillpoints} {label}";
}
else
{
SkillPointImage.Visible = false;
SkillTreeLabel.Text = $"{label}";
}
MainButton.OnPressed += args => OnPressed?.Invoke();
}

View File

@@ -36,6 +36,7 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered<Gamepl
private EntityUid? _targetPlayer;
private IEnumerable<CP14SkillPrototype> _allSkills = [];
private IEnumerable<CP14SkillTreePrototype> _allTrees = [];
private CP14SkillPrototype? _selectedSkill;
private CP14SkillTreePrototype? _selectedSkillTree;
@@ -70,9 +71,9 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered<Gamepl
private void CacheSkillProto()
{
_allSkills = _proto.EnumeratePrototypes<CP14SkillPrototype>();
_allTrees = _proto.EnumeratePrototypes<CP14SkillTreePrototype>().OrderBy(tree => Loc.GetString(tree.Name));
}
public void OnStateExited(GameplayState state)
{
if (_window != null)
@@ -205,7 +206,7 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered<Gamepl
//Restrictions
foreach (var req in skill.Restrictions)
{
var color = req.Check(_entManager, _playerManager.LocalEntity.Value) ? "green" : "red";
var color = req.Check(_entManager, _playerManager.LocalEntity.Value, skill) ? "green" : "red";
sb.Append($"- [color={color}]{req.GetDescription(_entManager, _proto)}[/color]\n");
}
@@ -284,14 +285,11 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered<Gamepl
return;
//If tree not selected, select the first one
if (_selectedSkillTree == null && storage.Progress.Count > 0)
if (_selectedSkillTree == null)
{
var firstTree = storage.Progress.First().Key;
var firstTree = _allTrees.First();
if (_proto.TryIndex(firstTree, out var indexedTree))
{
SelectTree(indexedTree, storage); // Set the first tree from the player's progress
}
SelectTree(firstTree, storage); // Set the first tree from the player's progress
}
if (_selectedSkillTree == null)
@@ -301,26 +299,26 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered<Gamepl
SelectNode(_selectedSkill);
UpdateGraphControl();
// Update the experience points for the selected tree
var playerProgress = storage.Progress;
if (playerProgress.TryGetValue(_selectedSkillTree, out var skillPoint))
{
_window.ExpPointLabel.Text = skillPoint.ToString();
}
_window.LevelLabel.Text = $"{storage.SkillsSumExperience}/{storage.ExperienceMaxCap}";
_window.TreeTabsContainer.RemoveAllChildren();
foreach (var (tree, _) in storage.Progress)
foreach (var tree in _allTrees)
{
if (!_proto.TryIndex(tree, out var indexedTree))
continue;
float learnedPoints = 0;
foreach (var skillId in storage.LearnedSkills)
{
//TODO: Loop indexing each skill is bad
if (_proto.TryIndex(skillId, out var skill) && skill.Tree == tree)
{
learnedPoints += skill.LearnCost;
}
}
var treeButton2 = new CP14SkillTreeButtonControl(indexedTree.Color, Loc.GetString(indexedTree.Name));
treeButton2.ToolTip = Loc.GetString(indexedTree.Desc ?? string.Empty);
var treeButton2 = new CP14SkillTreeButtonControl(tree.Color, Loc.GetString(tree.Name), learnedPoints);
treeButton2.ToolTip = Loc.GetString(tree.Desc ?? string.Empty);
treeButton2.OnPressed += () =>
{
SelectTree(indexedTree, storage);
SelectTree(tree, storage);
};
_window.TreeTabsContainer.AddChild(treeButton2);
@@ -336,9 +334,6 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered<Gamepl
_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";
UpdateGraphControl();
}

View File

@@ -4,7 +4,6 @@
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:parallax="clr-namespace:Content.Client.Parallax"
xmlns:ui1="clr-namespace:Content.Client._CP14.Skill.Ui"
xmlns:nodeTree="clr-namespace:Content.Client._CP14.UserInterface.Systems.NodeTree"
Title="{Loc 'cp14-skill-info-title'}"
MinSize="700 350"
@@ -76,10 +75,6 @@
</BoxContainer>
<!-- Experience Data -->
<BoxContainer Margin="10 10 10 10" VerticalAlignment="Bottom" HorizontalAlignment="Center" Orientation="Horizontal" VerticalExpand="True">
<RichTextLabel VerticalAlignment="Center" HorizontalAlignment="Left" Text="{Loc 'cp14-skill-menu-skillpoints'}" StyleClasses="LabelKeyText"/>
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2, 2" TexturePath="/Textures/_CP14/Interface/Misc/skillpoint.png"/>
<RichTextLabel Margin="0 0 20 0" Name="ExpPointLabel" VerticalAlignment="Center" HorizontalAlignment="Left" Text="0" StyleClasses="LabelKeyText" Access="Public"/>
<RichTextLabel VerticalAlignment="Center" HorizontalAlignment="Left" Text="{Loc 'cp14-skill-menu-level'}" StyleClasses="LabelKeyText"/>
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2, 2" TexturePath="/Textures/_CP14/Interface/Misc/skillpoint.png"/>
<RichTextLabel Margin="0 0 0 0" Name="LevelLabel" VerticalAlignment="Center" HorizontalAlignment="Left" Text="0" StyleClasses="LabelKeyText" Access="Public"/>

View File

@@ -4,6 +4,7 @@
*/
using Content.Shared._CP14.Workbench;
using Content.Shared._CP14.Workbench.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
@@ -13,37 +14,52 @@ using Robust.Shared.Prototypes;
namespace Content.Client._CP14.Workbench;
[GenerateTypedNameReferences]
public sealed partial class CP14WorkbenchRequirementControl : Control
public sealed partial class CP14WorkbenchRecipeControl : Control
{
[Dependency] private readonly IEntityManager _entity = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
public event Action<CP14WorkbenchUiRecipesEntry, CP14WorkbenchRecipePrototype>? OnSelect;
private readonly SpriteSystem _sprite;
public CP14WorkbenchRequirementControl()
private readonly CP14WorkbenchRecipePrototype _recipePrototype;
private readonly bool _craftable;
public CP14WorkbenchRecipeControl(CP14WorkbenchUiRecipesEntry entry)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_sprite = _entity.System<SpriteSystem>();
_recipePrototype = _prototype.Index(entry.ProtoId);
_craftable = entry.Craftable;
Button.OnPressed += _ => OnSelect?.Invoke(entry, _recipePrototype);
UpdateColor();
UpdateName();
UpdateView();
}
public CP14WorkbenchRequirementControl(CP14WorkbenchCraftRequirement requirement) : this()
private void UpdateColor()
{
Name.Text = requirement.GetRequirementTitle(_proto);
if (_craftable)
return;
var texture = requirement.GetRequirementTexture(_proto);
if (texture is not null)
{
View.Visible = true;
View.Texture = _sprite.Frame0(texture);
}
Button.ModulateSelfOverride = Color.FromHex("#302622");
}
var entityView = requirement.GetRequirementEntityView(_proto);
if (entityView is not null)
{
EntityView.Visible = true;
EntityView.SetPrototype(entityView);
}
private void UpdateName()
{
var result = _prototype.Index(_recipePrototype.Result);
var counter = _recipePrototype.ResultCount > 1 ? $" x{_recipePrototype.ResultCount}" : "";
Name.Text = $"{Loc.GetString(result.Name)} {counter}" ;
}
private void UpdateView()
{
View.SetPrototype(_recipePrototype.Result);
}
}

View File

@@ -0,0 +1,49 @@
/*
* This file is sublicensed under MIT License
* https://github.com/space-wizards/space-station-14/blob/master/LICENSE.TXT
*/
using Content.Shared._CP14.Workbench;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client._CP14.Workbench;
[GenerateTypedNameReferences]
public sealed partial class CP14WorkbenchRequirementControl : Control
{
[Dependency] private readonly IEntityManager _entity = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
private readonly SpriteSystem _sprite;
public CP14WorkbenchRequirementControl()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_sprite = _entity.System<SpriteSystem>();
}
public CP14WorkbenchRequirementControl(CP14WorkbenchCraftRequirement requirement) : this()
{
Name.Text = requirement.GetRequirementTitle(_proto);
var texture = requirement.GetRequirementTexture(_proto);
if (texture is not null)
{
View.Visible = true;
View.Texture = _sprite.Frame0(texture);
}
var entityView = requirement.GetRequirementEntityView(_proto);
if (entityView is not null)
{
EntityView.Visible = true;
EntityView.SetPrototype(entityView);
}
}
}

View File

@@ -1,65 +0,0 @@
/*
* This file is sublicensed under MIT License
* https://github.com/space-wizards/space-station-14/blob/master/LICENSE.TXT
*/
using Content.Shared._CP14.Workbench;
using Content.Shared._CP14.Workbench.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client._CP14.Workbench;
[GenerateTypedNameReferences]
public sealed partial class CP14WorkbenchRecipeControl : Control
{
[Dependency] private readonly IEntityManager _entity = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
public event Action<CP14WorkbenchUiRecipesEntry, CP14WorkbenchRecipePrototype>? OnSelect;
private readonly SpriteSystem _sprite;
private readonly CP14WorkbenchRecipePrototype _recipePrototype;
private readonly bool _craftable;
public CP14WorkbenchRecipeControl(CP14WorkbenchUiRecipesEntry entry)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_sprite = _entity.System<SpriteSystem>();
_recipePrototype = _prototype.Index(entry.ProtoId);
_craftable = entry.Craftable;
Button.OnPressed += _ => OnSelect?.Invoke(entry, _recipePrototype);
UpdateColor();
UpdateName();
UpdateView();
}
private void UpdateColor()
{
if (_craftable)
return;
Button.ModulateSelfOverride = Color.FromHex("#302622");
}
private void UpdateName()
{
var result = _prototype.Index(_recipePrototype.Result);
var counter = _recipePrototype.ResultCount > 1 ? $" x{_recipePrototype.ResultCount}" : "";
Name.Text = $"{Loc.GetString(result.Name)} {counter}" ;
}
private void UpdateView()
{
View.SetPrototype(_recipePrototype.Result);
}
}

View File

@@ -35,9 +35,9 @@
<!-- icon -->
<EntityPrototypeView Name="ItemView"
Scale="2,2"
Margin="0,0,4,0"
MinSize="64 64"
MaxSize="64 64"
Margin="8"
MinSize="96 96"
MaxSize="96 96"
HorizontalAlignment="Left" />
<!-- name & description -->

View File

@@ -0,0 +1,199 @@
using Content.Server.DoAfter;
using Content.Shared._CP14.ResearchTable;
using Content.Shared._CP14.Skill;
using Content.Shared._CP14.Skill.Components;
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared._CP14.Skill.Restrictions;
using Content.Shared.DoAfter;
using Content.Shared.UserInterface;
using Robust.Server.Audio;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.ResearchTable;
public sealed class CP14ResearchSystem : CP14SharedResearchSystem
{
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!;
[Dependency] private readonly AudioSystem _audio = default!;
private IEnumerable<CP14SkillPrototype> _allSkills = [];
public override void Initialize()
{
base.Initialize();
_allSkills = _proto.EnumeratePrototypes<CP14SkillPrototype>();
SubscribeLocalEvent<CP14ResearchTableComponent, BeforeActivatableUIOpenEvent>(OnBeforeUIOpen);
SubscribeLocalEvent<CP14ResearchTableComponent, CP14ResearchMessage>(OnResearch);
SubscribeLocalEvent<CP14ResearchTableComponent, CP14ResearchDoAfterEvent>(OnResearchEnd);
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnReloadPrototypes);
}
private void OnReloadPrototypes(PrototypesReloadedEventArgs ev)
{
_allSkills = _proto.EnumeratePrototypes<CP14SkillPrototype>();
}
private void OnResearchEnd(Entity<CP14ResearchTableComponent> table, ref CP14ResearchDoAfterEvent args)
{
if (args.Cancelled || args.Handled)
return;
if (!_proto.TryIndex(args.Skill, out var indexedSkill))
return;
var placedEntities = _lookup.GetEntitiesInRange(Transform(table).Coordinates,
table.Comp.ResearchRadius,
LookupFlags.Uncontained);
if (!CanResearch(indexedSkill, placedEntities, args.User))
return;
if (!TryComp<CP14SkillStorageComponent>(args.User, out var storage))
return;
if (storage.ResearchedSkills.Contains(args.Skill) || storage.LearnedSkills.Contains(args.Skill))
return;
storage.ResearchedSkills.Add(args.Skill);
Dirty(args.User, storage);
foreach (var restriction in indexedSkill.Restrictions)
{
switch (restriction)
{
case Researched researched:
foreach (var req in researched.Requirements)
{
req.PostCraft(EntityManager, _proto, placedEntities, args.User);
}
break;
}
}
_audio.PlayPvs(table.Comp.ResearchSound, table);
UpdateUI(table, args.User);
args.Handled = true;
}
private void OnResearch(Entity<CP14ResearchTableComponent> ent, ref CP14ResearchMessage args)
{
if (!TryComp<CP14SkillStorageComponent>(args.Actor, out var storage))
return;
if (storage.ResearchedSkills.Contains(args.Skill) || storage.LearnedSkills.Contains(args.Skill))
return;
if (!_proto.TryIndex(args.Skill, out var indexedSkill))
return;
StartResearch(ent, args.Actor, indexedSkill);
}
private void OnBeforeUIOpen(Entity<CP14ResearchTableComponent> ent, ref BeforeActivatableUIOpenEvent args)
{
UpdateUI(ent, args.User);
}
private void UpdateUI(Entity<CP14ResearchTableComponent> entity, EntityUid user)
{
var placedEntities = _lookup.GetEntitiesInRange(Transform(entity).Coordinates, entity.Comp.ResearchRadius);
if (!TryComp<CP14SkillStorageComponent>(user, out var storage))
return;
var researches = new List<CP14ResearchUiEntry>();
foreach (var skill in _allSkills)
{
var researchable = false;
var canCraft = true;
var hidden = false;
foreach (var restriction in skill.Restrictions)
{
if (storage.ResearchedSkills.Contains(skill) || storage.LearnedSkills.Contains(skill))
continue;
switch (restriction)
{
case SpeciesWhitelist speciesWhitelist: //We cant change species of our character, so hide it
if (!speciesWhitelist.Check(EntityManager, user, skill))
hidden = true;
break;
case NeedPrerequisite prerequisite:
if (!storage.ResearchedSkills.Contains(prerequisite.Prerequisite))
hidden = true;
break;
case Researched researched:
researchable = true;
foreach (var req in researched.Requirements)
{
if (!req.CheckRequirement(EntityManager, _proto, placedEntities, user))
{
canCraft = false;
}
}
break;
}
}
if (!researchable || hidden)
continue;
var entry = new CP14ResearchUiEntry(skill, canCraft);
researches.Add(entry);
}
_userInterface.SetUiState(entity.Owner, CP14ResearchTableUiKey.Key, new CP14ResearchTableUiState(researches));
}
private void StartResearch(Entity<CP14ResearchTableComponent> table, EntityUid user, CP14SkillPrototype skill)
{
var researchDoAfter = new CP14ResearchDoAfterEvent()
{
Skill = skill
};
var doAfterArgs = new DoAfterArgs(EntityManager,
user,
TimeSpan.FromSeconds(table.Comp.ResearchSpeed),
researchDoAfter,
table,
table)
{
BreakOnMove = true,
BreakOnDamage = true,
NeedHand = true,
};
_doAfter.TryStartDoAfter(doAfterArgs);
_audio.PlayPvs(table.Comp.ResearchSound, table);
}
private bool CanResearch(CP14SkillPrototype skill, HashSet<EntityUid> entities, EntityUid user)
{
foreach (var restriction in skill.Restrictions)
{
switch (restriction)
{
case Researched researched:
foreach (var req in researched.Requirements)
{
if (!req.CheckRequirement(EntityManager, _proto, entities, user))
return false;
}
break;
}
}
return true;
}
}

View File

@@ -35,10 +35,11 @@ public sealed partial class CP14WorkbenchSystem
foreach (var requirement in indexedRecipe.Requirements)
{
if (!requirement.CheckRequirement(EntityManager, _proto, placedEntities, user, indexedRecipe))
if (!requirement.CheckRequirement(EntityManager, _proto, placedEntities, user))
{
canCraft = false;
hidden = requirement.HideRecipe;
if (requirement.HideRecipe)
hidden = true;
}
}

View File

@@ -20,7 +20,7 @@ using Robust.Shared.Random;
namespace Content.Server._CP14.Workbench;
public sealed partial class CP14WorkbenchSystem : SharedCP14WorkbenchSystem
public sealed partial class CP14WorkbenchSystem : CP14SharedWorkbenchSystem
{
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
@@ -139,7 +139,7 @@ public sealed partial class CP14WorkbenchSystem : SharedCP14WorkbenchSystem
{
foreach (var req in recipe.Requirements)
{
if (!req.CheckRequirement(EntityManager, _proto, entities, user, recipe))
if (!req.CheckRequirement(EntityManager, _proto, entities, user))
return false;
}

View File

@@ -57,5 +57,5 @@ public sealed partial class LoadoutPrototype : IPrototype, IEquipmentLoadout
/// CP14 - it is possible to give skill trees to players who have taken this loadout
/// </summary>
[DataField]
public Dictionary<ProtoId<CP14SkillTreePrototype>, FixedPoint2> SkillTree = new();
public HashSet<ProtoId<CP14SkillPrototype>> Skills = new();
}

View File

@@ -102,9 +102,9 @@ public abstract class SharedStationSpawningSystem : EntitySystem
_action.AddAction(entity, action);
}
foreach (var tree in loadout.SkillTree)
foreach (var skill in loadout.Skills)
{
_skill.TryAddExperience(entity, tree.Key, tree.Value);
_skill.TryAddSkill(entity, skill);
}
}

View File

@@ -58,6 +58,19 @@ public abstract class SharedCP14MagicEnergySystem : EntitySystem
_ambient.SetAmbience(ent, args.Powered);
}
private void UpdateMagicAlert(Entity<CP14MagicEnergyContainerComponent> ent)
{
if (ent.Comp.MagicAlert is null)
return;
var level = ContentHelpers.RoundToLevels(
MathF.Max(0f, (float) ent.Comp.Energy),
(float) ent.Comp.MaxEnergy,
_alerts.GetMaxSeverity(ent.Comp.MagicAlert.Value));
_alerts.ShowAlert(ent, ent.Comp.MagicAlert.Value, (short) level);
}
public void ChangeEnergy(Entity<CP14MagicEnergyContainerComponent?> ent,
FixedPoint2 energy,
out FixedPoint2 deltaEnergy,
@@ -154,17 +167,14 @@ public abstract class SharedCP14MagicEnergySystem : EntitySystem
("color", color));
}
private void UpdateMagicAlert(Entity<CP14MagicEnergyContainerComponent> ent)
public void ChangeMaximumEnergy(Entity<CP14MagicEnergyContainerComponent?> ent, FixedPoint2 energy)
{
if (ent.Comp.MagicAlert is null)
if (!Resolve(ent, ref ent.Comp, false))
return;
var level = ContentHelpers.RoundToLevels(
MathF.Max(0f, (float) ent.Comp.Energy),
(float) ent.Comp.MaxEnergy,
_alerts.GetMaxSeverity(ent.Comp.MagicAlert.Value));
ent.Comp.MaxEnergy += energy;
_alerts.ShowAlert(ent, ent.Comp.MagicAlert.Value, (short) level);
ChangeEnergy(ent, energy, out _, out _);
}
}

View File

@@ -15,4 +15,7 @@ public sealed partial class CP14MagicManacostModifyComponent : Component
[DataField]
public FixedPoint2 GlobalModifier = 1f;
[DataField]
public bool Examinable = false;
}

View File

@@ -2,6 +2,7 @@
using Content.Shared._CP14.MagicRitual.Prototypes;
using Content.Shared._CP14.MagicSpell.Events;
using Content.Shared.Examine;
using Content.Shared.FixedPoint;
using Content.Shared.Inventory;
using Content.Shared.Verbs;
using Robust.Shared.Prototypes;
@@ -24,10 +25,10 @@ public sealed partial class CP14MagicManacostModifySystem : EntitySystem
private void OnVerbExamine(Entity<CP14MagicManacostModifyComponent> ent, ref GetVerbsEvent<ExamineVerb> args)
{
if (!args.CanInteract || !args.CanAccess)
if (!args.CanInteract || !args.CanAccess || !ent.Comp.Examinable)
return;
var markup = GetMagicClothingExamine(ent.Comp);
var markup = GetManacostModifyMessage(ent.Comp.GlobalModifier, ent.Comp.Modifiers);
_examine.AddDetailedExamineVerb(
args,
ent.Comp,
@@ -37,21 +38,21 @@ public sealed partial class CP14MagicManacostModifySystem : EntitySystem
Loc.GetString("cp14-magic-examinable-verb-message"));
}
private FormattedMessage GetMagicClothingExamine(CP14MagicManacostModifyComponent comp)
public FormattedMessage GetManacostModifyMessage(FixedPoint2 global, Dictionary<ProtoId<CP14MagicTypePrototype>, FixedPoint2> modifiers)
{
var msg = new FormattedMessage();
msg.AddMarkupOrThrow(Loc.GetString("cp14-clothing-magic-examine"));
if (comp.GlobalModifier != 1)
if (global != 1)
{
msg.PushNewline();
var plus = (float)comp.GlobalModifier > 1 ? "+" : "";
var plus = (float)global > 1 ? "+" : "";
msg.AddMarkupOrThrow(
$"{Loc.GetString("cp14-clothing-magic-global")}: {plus}{MathF.Round((float)(comp.GlobalModifier - 1) * 100, MidpointRounding.AwayFromZero)}%");
$"{Loc.GetString("cp14-clothing-magic-global")}: {plus}{MathF.Round((float)(global - 1) * 100, MidpointRounding.AwayFromZero)}%");
}
foreach (var modifier in comp.Modifiers)
foreach (var modifier in modifiers)
{
if (modifier.Value == 1)
continue;

View File

@@ -0,0 +1,17 @@
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
namespace Content.Shared._CP14.ResearchTable;
[RegisterComponent, NetworkedComponent]
public sealed partial class CP14ResearchTableComponent : Component
{
[DataField]
public float ResearchSpeed = 3f;
[DataField]
public float ResearchRadius = 0.5f;
[DataField]
public SoundSpecifier ResearchSound = new SoundCollectionSpecifier("PaperScribbles");
}

View File

@@ -0,0 +1,19 @@
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared.DoAfter;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared._CP14.ResearchTable;
public abstract class CP14SharedResearchSystem : EntitySystem
{
}
[Serializable, NetSerializable]
public sealed partial class CP14ResearchDoAfterEvent : DoAfterEvent
{
[DataField(required: true)]
public ProtoId<CP14SkillPrototype> Skill = default!;
public override DoAfterEvent Clone() => this;
}

View File

@@ -0,0 +1,22 @@
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared.Roles;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill;
public sealed partial class CP14LearnSkillsSpecial : JobSpecial
{
[DataField]
public HashSet<ProtoId<CP14SkillPrototype>> Skills { get; private set; } = new();
public override void AfterEquip(EntityUid mob)
{
var entMan = IoCManager.Resolve<IEntityManager>();
var skillSys = entMan.System<CP14SharedSkillSystem>();
foreach (var skill in Skills)
{
skillSys.TryAddSkill(mob, skill);
}
}
}

View File

@@ -0,0 +1,61 @@
using Content.Shared._CP14.Skill.Prototypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared._CP14.Skill;
[Serializable, NetSerializable]
public enum CP14ResearchTableUiKey
{
Key,
}
[Serializable, NetSerializable]
public sealed class CP14ResearchMessage(ProtoId<CP14SkillPrototype> skill) : BoundUserInterfaceMessage
{
public readonly ProtoId<CP14SkillPrototype> Skill = skill;
}
[Serializable, NetSerializable]
public sealed class CP14ResearchTableUiState(List<CP14ResearchUiEntry> skills) : BoundUserInterfaceState
{
public readonly List<CP14ResearchUiEntry> Skills = skills;
}
[Serializable, NetSerializable]
public readonly struct CP14ResearchUiEntry(ProtoId<CP14SkillPrototype> protoId, bool craftable) : IEquatable<CP14ResearchUiEntry>
{
public readonly ProtoId<CP14SkillPrototype> ProtoId = protoId;
public readonly bool Craftable = craftable;
public int CompareTo(CP14ResearchUiEntry other)
{
return Craftable.CompareTo(other.Craftable);
}
public override bool Equals(object? obj)
{
return obj is CP14ResearchUiEntry other && Equals(other);
}
public bool Equals(CP14ResearchUiEntry other)
{
return ProtoId.Id == other.ProtoId.Id;
}
public override int GetHashCode()
{
return HashCode.Combine(ProtoId, Craftable);
}
public override string ToString()
{
return $"{ProtoId} ({Craftable})";
}
public static int CompareTo(CP14ResearchUiEntry left, CP14ResearchUiEntry right)
{
return right.CompareTo(left);
}
}

View File

@@ -1,3 +1,5 @@
using System.Linq;
using System.Text;
using Content.Shared._CP14.Skill.Components;
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared.FixedPoint;
@@ -7,12 +9,16 @@ namespace Content.Shared._CP14.Skill;
public abstract partial class CP14SharedSkillSystem : EntitySystem
{
private EntityQuery<CP14SkillStorageComponent> _skillStorageQuery = default!;
public override void Initialize()
{
base.Initialize();
_skillStorageQuery = GetEntityQuery<CP14SkillStorageComponent>();
InitializeAdmin();
InitializeChecks();
}
/// <summary>
@@ -31,15 +37,19 @@ public abstract partial class CP14SharedSkillSystem : EntitySystem
if (!_proto.TryIndex(skill, out var indexedSkill))
return false;
if (indexedSkill.Effect is not null)
foreach (var effect in indexedSkill.Effects)
{
indexedSkill.Effect.AddSkill(EntityManager, target);
effect.AddSkill(EntityManager, target);
}
component.SkillsSumExperience += indexedSkill.LearnCost;
component.LearnedSkills.Add(skill);
Dirty(target, component);
var learnEv = new CP14SkillLearnedEvent(skill, target);
RaiseLocalEvent(target, ref learnEv);
return true;
}
@@ -59,9 +69,9 @@ public abstract partial class CP14SharedSkillSystem : EntitySystem
if (!_proto.TryIndex(skill, out var indexedSkill))
return false;
if (indexedSkill.Effect is not null)
foreach (var effect in indexedSkill.Effects)
{
indexedSkill.Effect.RemoveSkill(EntityManager, target);
effect.RemoveSkill(EntityManager, target);
}
component.SkillsSumExperience -= indexedSkill.LearnCost;
@@ -83,55 +93,6 @@ public abstract partial class CP14SharedSkillSystem : EntitySystem
return component.LearnedSkills.Contains(skill);
}
/// <summary>
/// Adds experience to the specified skill tree for the player.
/// </summary>
public bool TryAddExperience(EntityUid target,
ProtoId<CP14SkillTreePrototype> tree,
FixedPoint2 exp,
CP14SkillStorageComponent? component = null)
{
if (!Resolve(target, ref component, false))
return false;
if (component.Progress.TryGetValue(tree, out var currentExp))
{
// If the tree already exists, add experience to it
component.Progress[tree] = currentExp + exp;
}
else
{
// If the tree doesn't exist, initialize it with the experience
component.Progress[tree] = exp;
}
Dirty(target, component);
return true;
}
/// <summary>
/// Removes experience from the specified skill tree for the player.
/// </summary>
public bool TryRemoveExperience(EntityUid target,
ProtoId<CP14SkillTreePrototype> tree,
FixedPoint2 exp,
CP14SkillStorageComponent? component = null)
{
if (!Resolve(target, ref component, false))
return false;
if (!component.Progress.TryGetValue(tree, out var currentExp))
return false;
if (currentExp < exp)
return false;
component.Progress[tree] = FixedPoint2.Max(0, component.Progress[tree] - exp);
Dirty(target, component);
return true;
}
/// <summary>
/// Checks if the player can learn the specified skill.
/// </summary>
@@ -158,12 +119,6 @@ public abstract partial class CP14SharedSkillSystem : EntitySystem
if (!AllowedToLearn(target, skill, component))
return false;
//Experience check
if (!component.Progress.TryGetValue(skill.Tree, out var currentExp))
return false;
if (currentExp < skill.LearnCost)
return false;
return true;
}
@@ -182,13 +137,13 @@ public abstract partial class CP14SharedSkillSystem : EntitySystem
return false;
//Check max cap
if (component.SkillsSumExperience + skill.LearnCost >= component.ExperienceMaxCap)
if (component.SkillsSumExperience + skill.LearnCost > component.ExperienceMaxCap)
return false;
//Restrictions check
foreach (var req in skill.Restrictions)
{
if (!req.Check(EntityManager, target))
if (!req.Check(EntityManager, target, skill))
return false;
}
@@ -205,15 +160,9 @@ public abstract partial class CP14SharedSkillSystem : EntitySystem
if (!Resolve(target, ref component, false))
return false;
if (!_proto.TryIndex(skill, out var indexedSkill))
return false;
if (!CanLearnSkill(target, skill, component))
return false;
if (!TryRemoveExperience(target, indexedSkill.Tree, indexedSkill.LearnCost, component))
return false;
if (!TryAddSkill(target, skill, component))
return false;
@@ -228,11 +177,11 @@ public abstract partial class CP14SharedSkillSystem : EntitySystem
if (!_proto.TryIndex(skill, out var indexedSkill))
return string.Empty;
if (indexedSkill.Name != null)
if (indexedSkill.Name is not null)
return Loc.GetString(indexedSkill.Name);
if (indexedSkill.Effect != null)
return indexedSkill.Effect.GetName(EntityManager, _proto) ?? string.Empty;
if (indexedSkill.Effects.Count > 0)
return indexedSkill.Effects.First().GetName(EntityManager, _proto) ?? string.Empty;
return string.Empty;
}
@@ -245,12 +194,19 @@ public abstract partial class CP14SharedSkillSystem : EntitySystem
if (!_proto.TryIndex(skill, out var indexedSkill))
return string.Empty;
if (indexedSkill.Desc != null)
if (indexedSkill.Desc is not null)
return Loc.GetString(indexedSkill.Desc);
if (indexedSkill.Effect != null)
return indexedSkill.Effect.GetDescription(EntityManager, _proto) ?? string.Empty;
var sb = new StringBuilder();
return string.Empty;
foreach (var effect in indexedSkill.Effects)
{
sb.Append(effect.GetDescription(EntityManager, _proto, skill) + "\n");
}
return sb.ToString();
}
}
[ByRefEvent]
public record struct CP14SkillLearnedEvent(ProtoId<CP14SkillPrototype> Skill, EntityUid User);

View File

@@ -50,27 +50,6 @@ public abstract partial class CP14SharedSkillSystem
var target = args.Target;
//Add skill points
foreach (var tree in _allTrees)
{
FixedPoint2 current = 0;
ent.Comp.Progress.TryGetValue(tree, out current);
var name = Loc.GetString(tree.Name);
args.Verbs.Add(new Verb
{
Text = name,
Message = $"{name} EXP {current} -> {current + 1}",
Category = VerbCategory.CP14AdminSkillAdd,
Icon = tree.Icon,
Act = () =>
{
TryAddExperience(target, tree.ID, 1);
},
Priority = 2,
});
}
//Add Skill
foreach (var skill in _allSkills)
{
@@ -91,30 +70,6 @@ public abstract partial class CP14SharedSkillSystem
});
}
//Remove skill points
foreach (var tree in _allTrees)
{
FixedPoint2 current = 0;
ent.Comp.Progress.TryGetValue(tree, out current);
if (current < 1)
continue;
var name = Loc.GetString(tree.Name);
args.Verbs.Add(new Verb
{
Text = name,
Message = $"{name} EXP {current} -> {current - 1}",
Category = VerbCategory.CP14AdminSkillRemove,
Icon = tree.Icon,
Act = () =>
{
TryRemoveExperience(target, tree.ID, 1);
},
Priority = 2,
});
}
//Remove Skill
foreach (var skill in ent.Comp.LearnedSkills)
{

View File

@@ -0,0 +1,75 @@
using System.Text;
using Content.Shared._CP14.Skill.Components;
using Content.Shared.Damage;
using Content.Shared.Examine;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Popups;
using Content.Shared.Throwing;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.Network;
using Robust.Shared.Random;
namespace Content.Shared._CP14.Skill;
public abstract partial class CP14SharedSkillSystem
{
[Dependency] private readonly ThrowingSystem _throwing = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
private void InitializeChecks()
{
SubscribeLocalEvent<CP14MeleeWeaponSkillRequiredComponent, MeleeHitEvent>(OnMeleeAttack);
SubscribeLocalEvent<CP14MeleeWeaponSkillRequiredComponent, ExaminedEvent>(OnExamined);
}
private void OnExamined(Entity<CP14MeleeWeaponSkillRequiredComponent> ent, ref ExaminedEvent args)
{
var sb = new StringBuilder();
sb.Append(Loc.GetString("cp14-skill-issue-title") + "\n");
foreach (var skill in ent.Comp.Skills)
{
if (!_proto.TryIndex(skill, out var indexedSkill))
continue;
if (indexedSkill.Name is null)
continue;
var color = HaveSkill(args.Examiner, skill) ? Color.LimeGreen.ToHex() : Color.Red.ToHex();
sb.Append($"[color={color}] - {Loc.GetString(indexedSkill.Name)} [/color]\n");
}
args.PushMarkup(sb.ToString());
}
private void OnMeleeAttack(Entity<CP14MeleeWeaponSkillRequiredComponent> ent, ref MeleeHitEvent args)
{
if (!_skillStorageQuery.TryComp(args.User, out var skillStorage))
return;
var passed = true;
foreach (var reqSkill in ent.Comp.Skills)
{
if (!skillStorage.LearnedSkills.Contains(reqSkill))
{
passed = false;
break;
}
}
args.BonusDamage *= ent.Comp.DamageMultiplier;
if (_net.IsClient)
return;
if (passed || !_random.Prob(ent.Comp.DropProbability))
return;
_hands.TryDrop(args.User, ent);
_throwing.TryThrow(ent, _random.NextAngle().ToWorldVec() * 2, 2f, args.User);
_damageable.TryChangeDamage(args.User, args.BaseDamage);
_popup.PopupEntity(Loc.GetString("cp14-skill-issue"), args.User, args.User, PopupType.Medium);
}
}

View File

@@ -1,48 +0,0 @@
using Content.Shared._CP14.Skill.Components;
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared.Bed.Sleep;
using Content.Shared.Examine;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill;
public abstract partial class CP14SharedSkillSystem
{
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly ExamineSystemShared _examine = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
public void GiveExperienceInRadius(EntityCoordinates position, ProtoId<CP14SkillTreePrototype> tree, FixedPoint2 points, float radius = 5)
{
var entities = _lookup.GetEntitiesInRange<CP14SkillStorageComponent>(position, radius, LookupFlags.Uncontained);
foreach (var ent in entities)
{
//Cant learn if the position is not in range or obstructed
if (!_examine.InRangeUnOccluded(ent, position, radius))
continue;
//Cant learn when dead
if (TryComp<MobStateComponent>(ent, out var mobState) && !_mobState.IsAlive(ent, mobState))
continue;
//Cant learn if the entity is sleeping
if (HasComp<SleepingComponent>(ent))
continue;
TryAddExperience(ent, tree, points);
}
}
public void GiveExperienceInRadius(EntityUid uid,
ProtoId<CP14SkillTreePrototype> tree,
FixedPoint2 points,
float radius = 5)
{
GiveExperienceInRadius(Transform(uid).Coordinates, tree, points, radius);
}
}

View File

@@ -0,0 +1,31 @@
using Content.Shared._CP14.Skill.Prototypes;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill.Components;
/// <summary>
/// Component that stores the skills learned by a player and their progress in the skill trees.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
[Access(typeof(CP14SharedSkillSystem))]
public sealed partial class CP14MeleeWeaponSkillRequiredComponent : Component
{
/// <summary>
/// What skills does a character have to have to use this weapon?
/// </summary>
[DataField, AutoNetworkedField]
public HashSet<ProtoId<CP14SkillPrototype>> Skills = new();
/// <summary>
/// The chances of dropping a weapon from your hands if the required skills are not learned by the character.
/// </summary>
[DataField, AutoNetworkedField]
public float DropProbability = 0.5f;
/// <summary>
/// Reduces outgoing damage if the required skills are not learned by the character
/// </summary>
[DataField, AutoNetworkedField]
public float DamageMultiplier = 0.5f;
}

View File

@@ -1,3 +1,4 @@
using Content.Shared._CP14.ResearchTable;
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Shared.GameStates;
@@ -10,29 +11,29 @@ namespace Content.Shared._CP14.Skill.Components;
/// Component that stores the skills learned by a player and their progress in the skill trees.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
[Access(typeof(CP14SharedSkillSystem))]
[Access(typeof(CP14SharedSkillSystem), typeof(CP14SharedResearchSystem))]
public sealed partial class CP14SkillStorageComponent : Component
{
[DataField, AutoNetworkedField]
public List<ProtoId<CP14SkillPrototype>> LearnedSkills = new();
/// <summary>
/// skills that the player has learned on the research table, but has not yet learned in the skill tree.
/// </summary>
[DataField, AutoNetworkedField]
public List<ProtoId<CP14SkillPrototype>> ResearchedSkills = new();
/// <summary>
/// The number of experience points spent on skills. Technically this could be calculated via LearnedSkills, but this is a cached value for optimization.
/// </summary>
[DataField, AutoNetworkedField]
public FixedPoint2 SkillsSumExperience = 0;
/// <summary>
/// Keeps track of progress points in the knowledge areas available to the player. Important: The absence of a specific area means that the player CANNOT progress in that area.
/// </summary>
[DataField, AutoNetworkedField]
public Dictionary<ProtoId<CP14SkillTreePrototype>, FixedPoint2> Progress = new();
/// <summary>
/// The maximum ceiling of experience points that can be spent on learning skills. Not tied to a category.
/// </summary>
[DataField, AutoNetworkedField]
public FixedPoint2 ExperienceMaxCap = 10;
public FixedPoint2 ExperienceMaxCap = 5;
}
/// <summary>

View File

@@ -1,3 +1,4 @@
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared.Actions;
using Robust.Shared.Prototypes;
@@ -39,7 +40,7 @@ public sealed partial class AddAction : CP14SkillEffect
return !protoManager.TryIndex(Action, out var indexedAction) ? string.Empty : indexedAction.Name;
}
public override string? GetDescription(IEntityManager entMagager, IPrototypeManager protoManager)
public override string? GetDescription(IEntityManager entMagager, IPrototypeManager protoManager, ProtoId<CP14SkillPrototype> skill)
{
return !protoManager.TryIndex(Action, out var indexedAction) ? string.Empty : indexedAction.Description;
}

View File

@@ -0,0 +1,31 @@
using Content.Shared._CP14.Skill.Prototypes;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill.Effects;
public sealed partial class AddComponents : CP14SkillEffect
{
[DataField(required: true)]
public ComponentRegistry Components = new();
public override void AddSkill(IEntityManager entManager, EntityUid target)
{
entManager.AddComponents(target, Components);
}
public override void RemoveSkill(IEntityManager entManager, EntityUid target)
{
entManager.RemoveComponents(target, Components);
}
public override string? GetName(IEntityManager entMagager, IPrototypeManager protoManager)
{
return null;
}
public override string? GetDescription(IEntityManager entMagager, IPrototypeManager protoManager, ProtoId<CP14SkillPrototype> skill)
{
return null;
}
}

View File

@@ -0,0 +1,33 @@
using Content.Shared._CP14.MagicEnergy;
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill.Effects;
public sealed partial class AddManaMax : CP14SkillEffect
{
[DataField]
public FixedPoint2 AdditionalMana = 0;
public override void AddSkill(IEntityManager entManager, EntityUid target)
{
var magicSystem = entManager.System<SharedCP14MagicEnergySystem>();
magicSystem.ChangeMaximumEnergy(target, AdditionalMana);
}
public override void RemoveSkill(IEntityManager entManager, EntityUid target)
{
var magicSystem = entManager.System<SharedCP14MagicEnergySystem>();
magicSystem.ChangeMaximumEnergy(target, -AdditionalMana);
}
public override string? GetName(IEntityManager entMagager, IPrototypeManager protoManager)
{
return null;
}
public override string? GetDescription(IEntityManager entMagager, IPrototypeManager protoManager, ProtoId<CP14SkillPrototype> skill)
{
return Loc.GetString("cp14-skill-desc-add-mana", ("mana", AdditionalMana.ToString()));
}
}

View File

@@ -1,3 +1,4 @@
using Content.Shared._CP14.Skill.Prototypes;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
@@ -13,5 +14,5 @@ public abstract partial class CP14SkillEffect
public abstract string? GetName(IEntityManager entMagager, IPrototypeManager protoManager);
public abstract string? GetDescription(IEntityManager entMagager, IPrototypeManager protoManager);
public abstract string? GetDescription(IEntityManager entMagager, IPrototypeManager protoManager, ProtoId<CP14SkillPrototype> skill);
}

View File

@@ -0,0 +1,78 @@
using System.Text;
using Content.Shared._CP14.MagicManacostModify;
using Content.Shared._CP14.MagicRitual.Prototypes;
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill.Effects;
public sealed partial class ModifyManacost : CP14SkillEffect
{
[DataField]
public FixedPoint2 Global = 0f;
[DataField]
public Dictionary<ProtoId<CP14MagicTypePrototype>, FixedPoint2> Modifiers = new();
public override void AddSkill(IEntityManager entManager, EntityUid target)
{
entManager.EnsureComponent<CP14MagicManacostModifyComponent>(target, out var magicEffectManaCost);
foreach (var (magicType, modifier) in Modifiers)
{
if (!magicEffectManaCost.Modifiers.ContainsKey(magicType))
magicEffectManaCost.Modifiers.Add(magicType, 1 + modifier);
else
magicEffectManaCost.Modifiers[magicType] += modifier;
}
magicEffectManaCost.GlobalModifier += Global;
}
public override void RemoveSkill(IEntityManager entManager, EntityUid target)
{
entManager.EnsureComponent<CP14MagicManacostModifyComponent>(target, out var magicEffectManaCost);
foreach (var (magicType, modifier) in Modifiers)
{
if (!magicEffectManaCost.Modifiers.ContainsKey(magicType))
continue;
magicEffectManaCost.Modifiers[magicType] -= modifier;
if (magicEffectManaCost.Modifiers[magicType] <= 0)
magicEffectManaCost.Modifiers.Remove(magicType);
}
magicEffectManaCost.GlobalModifier -= Global;
}
public override string? GetName(IEntityManager entMagager, IPrototypeManager protoManager)
{
return null;
}
public override string? GetDescription(IEntityManager entMagager, IPrototypeManager protoManager, ProtoId<CP14SkillPrototype> skill)
{
var sb = new StringBuilder();
sb.Append(Loc.GetString("cp14-clothing-magic-examine")+"\n");
if (Global != 0)
{
var plus = (float)Global > 0 ? "+" : "";
sb.Append(
$"{Loc.GetString("cp14-clothing-magic-global")}: {plus}{MathF.Round((float)Global * 100, MidpointRounding.AwayFromZero)}%\n");
}
foreach (var modifier in Modifiers)
{
if (modifier.Value == 0)
continue;
var plus = modifier.Value > 1 ? "+" : "";
var indexedType = protoManager.Index(modifier.Key);
sb.Append($"- [color={indexedType.Color.ToHex()}]{Loc.GetString(indexedType.Name)}[/color]: {plus}{modifier.Value*100}%");
}
return sb.ToString();
}
}

View File

@@ -1,3 +1,4 @@
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared.Actions;
using Robust.Shared.Prototypes;
@@ -62,7 +63,7 @@ public sealed partial class ReplaceAction : CP14SkillEffect
return !protoManager.TryIndex(NewAction, out var indexedAction) ? string.Empty : indexedAction.Name;
}
public override string? GetDescription(IEntityManager entMagager, IPrototypeManager protoManager)
public override string? GetDescription(IEntityManager entMagager, IPrototypeManager protoManager, ProtoId<CP14SkillPrototype> skill)
{
return !protoManager.TryIndex(NewAction, out var indexedAction) ? string.Empty : indexedAction.Description;
}

View File

@@ -0,0 +1,65 @@
using System.Text;
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared._CP14.Workbench.Prototypes;
using Content.Shared._CP14.Workbench.Requirements;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill.Effects;
/// <summary>
/// This effect only exists for parsing the description.
/// </summary>
public sealed partial class UnlockRecipes : CP14SkillEffect
{
public override void AddSkill(IEntityManager entManager, EntityUid target)
{
//
}
public override void RemoveSkill(IEntityManager entManager, EntityUid target)
{
//
}
public override string? GetName(IEntityManager entMagager, IPrototypeManager protoManager)
{
return null;
}
public override string? GetDescription(IEntityManager entMagager, IPrototypeManager protoManager, ProtoId<CP14SkillPrototype> skill)
{
var allRecipes = protoManager.EnumeratePrototypes<CP14WorkbenchRecipePrototype>();
var sb = new StringBuilder();
sb.Append(Loc.GetString("cp14-skill-desc-unlock-recipes") + "\n");
var affectedRecipes = new List<CP14WorkbenchRecipePrototype>();
foreach (var recipe in allRecipes)
{
foreach (var req in recipe.Requirements)
{
switch (req)
{
case SkillRequired skillReq:
foreach (var skillReqSkill in skillReq.Skills)
{
if (skillReqSkill == skill)
{
affectedRecipes.Add(recipe);
break;
}
}
break;
}
}
}
foreach (var recipe in affectedRecipes)
{
if (!protoManager.TryIndex(recipe.Result, out var indexedResult))
continue;
sb.Append("- " + indexedResult.Name + "\n");
}
return sb.ToString();
}
}

View File

@@ -18,13 +18,13 @@ public sealed partial class CP14SkillPrototype : IPrototype
/// Skill Title. If you leave null, the name will try to generate from Effect.GetName()
/// </summary>
[DataField]
public LocId? Name;
public LocId? Name = null;
/// <summary>
/// Skill Description. If you leave null, the description will try to generate from Effect.GetDescription()
/// </summary>
[DataField]
public LocId? Desc;
public LocId? Desc = null;
/// <summary>
/// The tree this skill belongs to. This is used to group skills together in the UI.
@@ -55,7 +55,7 @@ public sealed partial class CP14SkillPrototype : IPrototype
/// But the presence of the skill itself can affect some systems that check for the presence of certain skills.
/// </summary>
[DataField]
public CP14SkillEffect? Effect;
public List<CP14SkillEffect> Effects = new();
/// <summary>
/// Skill restriction. Limiters on learning. Any reason why a player cannot learn this skill.

View File

@@ -1,3 +1,4 @@
using Content.Shared._CP14.Skill.Prototypes;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
@@ -7,7 +8,7 @@ namespace Content.Shared._CP14.Skill.Restrictions;
[MeansImplicitUse]
public abstract partial class CP14SkillRestriction
{
public abstract bool Check(IEntityManager entManager, EntityUid target);
public abstract bool Check(IEntityManager entManager, EntityUid target, CP14SkillPrototype skill);
public abstract string GetDescription(IEntityManager entManager, IPrototypeManager protoManager);
}

View File

@@ -0,0 +1,18 @@
using Content.Shared._CP14.Skill.Components;
using Content.Shared._CP14.Skill.Prototypes;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill.Restrictions;
public sealed partial class Impossible : CP14SkillRestriction
{
public override bool Check(IEntityManager entManager, EntityUid target, CP14SkillPrototype skill)
{
return false;
}
public override string GetDescription(IEntityManager entManager, IPrototypeManager protoManager)
{
return Loc.GetString("cp14-skill-req-impossible");
}
}

View File

@@ -9,7 +9,7 @@ public sealed partial class NeedPrerequisite : CP14SkillRestriction
[DataField(required: true)]
public ProtoId<CP14SkillPrototype> Prerequisite = new();
public override bool Check(IEntityManager entManager, EntityUid target)
public override bool Check(IEntityManager entManager, EntityUid target, CP14SkillPrototype skill)
{
if (!entManager.TryGetComponent<CP14SkillStorageComponent>(target, out var skillStorage))
return false;

View File

@@ -0,0 +1,26 @@
using Content.Shared._CP14.Skill.Components;
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared._CP14.Workbench;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill.Restrictions;
public sealed partial class Researched : CP14SkillRestriction
{
[DataField(required: true)]
public List<CP14WorkbenchCraftRequirement> Requirements = new();
public override bool Check(IEntityManager entManager, EntityUid target, CP14SkillPrototype skill)
{
if (!entManager.TryGetComponent<CP14SkillStorageComponent>(target, out var skillStorage))
return false;
var learned = skillStorage.ResearchedSkills;
return learned.Contains(skill);
}
public override string GetDescription(IEntityManager entManager, IPrototypeManager protoManager)
{
return Loc.GetString("cp14-skill-req-researched");
}
}

View File

@@ -1,3 +1,4 @@
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
using Robust.Shared.Prototypes;
@@ -9,7 +10,7 @@ public sealed partial class SpeciesWhitelist : CP14SkillRestriction
[DataField(required: true)]
public ProtoId<SpeciesPrototype> Species = new();
public override bool Check(IEntityManager entManager, EntityUid target)
public override bool Check(IEntityManager entManager, EntityUid target, CP14SkillPrototype skill)
{
if (!entManager.TryGetComponent<HumanoidAppearanceComponent>(target, out var appearance))
return false;

View File

@@ -10,7 +10,7 @@ using Robust.Shared.Serialization;
namespace Content.Shared._CP14.Workbench;
public abstract class SharedCP14WorkbenchSystem : EntitySystem
public abstract class CP14SharedWorkbenchSystem : EntitySystem
{
}

View File

@@ -26,8 +26,7 @@ public sealed partial class MaterialResource : CP14WorkbenchCraftRequirement
EntityManager entManager,
IPrototypeManager protoManager,
HashSet<EntityUid> placedEntities,
EntityUid user,
CP14WorkbenchRecipePrototype recipe)
EntityUid user)
{
var count = 0;
foreach (var ent in placedEntities)

View File

@@ -22,8 +22,7 @@ public sealed partial class ProtoIdResource : CP14WorkbenchCraftRequirement
public override bool CheckRequirement(EntityManager entManager,
IPrototypeManager protoManager,
HashSet<EntityUid> placedEntities,
EntityUid user,
CP14WorkbenchRecipePrototype recipe)
EntityUid user)
{
var indexedIngredients = IndexIngredients(entManager, placedEntities);

View File

@@ -16,8 +16,7 @@ public sealed partial class SkillRequired : CP14WorkbenchCraftRequirement
public override bool CheckRequirement(EntityManager entManager,
IPrototypeManager protoManager,
HashSet<EntityUid> placedEntities,
EntityUid user,
CP14WorkbenchRecipePrototype recipe)
EntityUid user)
{
var knowledgeSystem = entManager.System<CP14SharedSkillSystem>();

View File

@@ -23,8 +23,7 @@ public sealed partial class StackGroupResource : CP14WorkbenchCraftRequirement
public override bool CheckRequirement(EntityManager entManager,
IPrototypeManager protoManager,
HashSet<EntityUid> placedEntities,
EntityUid user,
CP14WorkbenchRecipePrototype recipe)
EntityUid user)
{
if (!protoManager.TryIndex(Group, out var indexedGroup))
return false;

View File

@@ -23,8 +23,7 @@ public sealed partial class StackResource : CP14WorkbenchCraftRequirement
public override bool CheckRequirement(EntityManager entManager,
IPrototypeManager protoManager,
HashSet<EntityUid> placedEntities,
EntityUid user,
CP14WorkbenchRecipePrototype recipe)
EntityUid user)
{
var count = 0;
foreach (var ent in placedEntities)

View File

@@ -30,8 +30,7 @@ public sealed partial class TagResource : CP14WorkbenchCraftRequirement
EntityManager entManager,
IPrototypeManager protoManager,
HashSet<EntityUid> placedEntities,
EntityUid user,
CP14WorkbenchRecipePrototype recipe)
EntityUid user)
{
var tagSystem = entManager.System<TagSystem>();

View File

@@ -26,8 +26,7 @@ public abstract partial class CP14WorkbenchCraftRequirement
public abstract bool CheckRequirement(EntityManager entManager,
IPrototypeManager protoManager,
HashSet<EntityUid> placedEntities,
EntityUid user,
CP14WorkbenchRecipePrototype recipe);
EntityUid user);
/// <summary>
/// An event that is triggered after crafting. This is the place to put important things like removing items, spending stacks or other things.

View File

@@ -1 +1,2 @@
cp14-recipe-title-meat = various pieces of raw meat
cp14-recipe-title-energy-crystal = energycrystals

View File

@@ -1,2 +1,4 @@
cp14-skill-req-prerequisite = Skill "{$name}" must be learned.
cp14-skill-req-species = Available only to species "{$name}"
cp14-skill-req-prerequisite = Skill "{$name}" must be learned
cp14-skill-req-species = You must be the race of “{$name}
cp14-skill-req-researched = A study needs to be done on the research table
cp14-skill-req-impossible = Unable to explore during a round at the current moment

View File

@@ -0,0 +1,3 @@
cp14-skill-issue-title = In order to use these weapons effectively, you need to have skill:
cp14-skill-issue = Skill issue!

View File

@@ -0,0 +1,34 @@
cp14-skill-mastery-desc = Mastering the art of this weapon, you will no longer clumsily drop it or cut yourself.
cp14-skill-sword-mastery-name = Sword mastery
cp14-skill-parier-mastery-name = Rapier mastery
cp14-skill-skimitar-mastery-name = Skimitar mastery
cp14-skill-pyro-t1-name = Basic pyrokinetics
cp14-skill-pyro-t2-name = Advanced pyrokinetics
cp14-skill-pyro-t3-name = Expert pyrokinetics
cp14-skill-illusion-t1-name = Basic illusion
cp14-skill-illusion-t2-name = Advanced illusion
cp14-skill-illusion-t3-name = Expert illusion
cp14-skill-water-t1-name = Basic hydrosophistry
cp14-skill-water-t2-name = Advanced hydrosophistry
cp14-skill-water-t3-name = Expert hydrosophistry
cp14-skill-life-t1-name = Basic lifecation
cp14-skill-life-t2-name = Advanced lifecation
cp14-skill-life-t3-name = Expert lifecation
cp14-skill-meta-t1-name = Basic metamagic
cp14-skill-meta-t2-name = Advanced metamagic
cp14-skill-meta-t3-name = Expert metamagic
cp14-skill-alchemy-vision-name = Alchemist's Vision
cp14-skill-alchemy-vision-desc = You are able to understand what liquids are in containers by visually analyzing them.
cp14-skill-copper-melt-name = Copper melting
cp14-skill-iron-melt-name = Iron melting
cp14-skill-gold-melt-name = Gold melting
cp14-skill-mithril-melt-name = Mithril melting
cp14-skill-glass-melt-name = Glasswork

View File

@@ -1,25 +1,36 @@
cp14-skill-tree-blaksmithing-name = Blacksmithing
cp14-skill-tree-blaksmithing-desc = Explore and create new items from metal.
cp14-skill-tree-pyrokinetic-name = Pyrokinetic
cp14-skill-tree-pyrokinetic-name = Pyrokinesis
cp14-skill-tree-pyrokinetic-desc = Master the magic of fire, allowing you to warm, illuminate, and destroy.
cp14-skill-tree-hydrosophistry-name = Hydrosophistry
cp14-skill-tree-hydrosophistry-name = Hydrosophy
cp14-skill-tree-hydrosophistry-desc = Master the magic of water and frost to create items from water and ice, and freeze enemies.
cp14-skill-tree-metamagic-name = Metamagic
cp14-skill-tree-metamagic-desc = Explore ways to subtly manipulate magic to affect spells and items.
cp14-skill-tree-illusion-name = Illusoriness
cp14-skill-tree-illusion-name = Illusion
cp14-skill-tree-illusion-desc = Explore the nature of light to create illusions, light sources and shadows.
cp14-skill-tree-healing-name = Lifecation
cp14-skill-tree-healing-name = Vivification
cp14-skill-tree-healing-desc = Explore the ways in which magic affects living creatures.
cp14-skill-tree-dimension-name = Dimensium
cp14-skill-tree-dimension-name = Dimensionomy
cp14-skill-tree-dimension-desc = Immerse yourself in the nature of space and void.
# Body
cp14-skill-tree-atlethic-name = Atlethic
cp14-skill-tree-atlethic-name = Athletic
cp14-skill-tree-atlethic-desc = Develop your body by pushing the boundaries of what is available.
cp14-skill-tree-martial-name = Martial arts
cp14-skill-tree-martial-desc = Master the secrets of deadly weapons, or make your own body a weapon.
# Job
cp14-skill-tree-thaumaturgy-name = Alchemy
cp14-skill-tree-thaumaturgy-desc = The art of creating magical potions that can kill, raise from the dead, or turn creatures into sheep.
cp14-skill-tree-blacksmithing-name = Blacksmithing
cp14-skill-tree-blacksmithing-desc = The art of turning metal into various useful things.

View File

@@ -5,6 +5,12 @@ cp14-skill-info-title = Skills
cp14-game-hud-open-skill-menu-button-tooltip = Skill tree
cp14-skill-menu-learn-button = Learn skill
cp14-skill-menu-learncost = [color=yellow]Points required:[/color]
cp14-skill-menu-skillpoints = Experience points:
cp14-skill-menu-level = Level:
cp14-skill-menu-learncost = [color=yellow]Memory required:[/color]
cp14-skill-menu-level = Memory:
cp14-research-table-title = Research table
cp14-research-recipe-list = Research costs:
cp14-research-craft = Research
cp14-skill-desc-add-mana = Increases your character's mana amount by {$mana}.
cp14-skill-desc-unlock-recipes = Opens up the possibility of crafting:

View File

@@ -1 +1,2 @@
cp14-recipe-title-meat = разные кусочки сырого мяса
cp14-recipe-title-energy-crystal = энергокристаллы

View File

@@ -1,2 +1,4 @@
cp14-skill-req-prerequisite = Навык "{$name}" должен быть изучен.
cp14-skill-req-species = Доступно только расе "{$name}"
cp14-skill-req-prerequisite = Навык "{$name}" должен быть изучен
cp14-skill-req-species = Вы должны быть расы "{$name}"
cp14-skill-req-researched = Необходимо провести исследование на исследовательском столе
cp14-skill-req-impossible = Невозможно изучить во время раунда на текузий момент

View File

@@ -0,0 +1,3 @@
cp14-skill-issue-title = Чтобы эффективно пользовать этим оружием, необходимо владеть навыками:
cp14-skill-issue = Проблемы с навыком!

View File

@@ -0,0 +1,34 @@
cp14-skill-mastery-desc = Овладев искусством этого оружия, вы больше не будете неуклюже ронять его, или резать сами себя.
cp14-skill-sword-mastery-name = Владение мечом
cp14-skill-parier-mastery-name = Владение рапирой
cp14-skill-skimitar-mastery-name = Владение скимитаром
cp14-skill-pyro-t1-name = Базовая пирокинетика
cp14-skill-pyro-t2-name = Продвинутая пирокинетика
cp14-skill-pyro-t3-name = Экспертная пирокинетика
cp14-skill-illusion-t1-name = Базовая иллюзия
cp14-skill-illusion-t2-name = Продвинутая иллюзия
cp14-skill-illusion-t3-name = Экспертная иллюзия
cp14-skill-water-t1-name = Базовая гидрософистика
cp14-skill-water-t2-name = Продвинутая гидрософистика
cp14-skill-water-t3-name = Экспертная гидрософистика
cp14-skill-life-t1-name = Базовое животворение
cp14-skill-life-t2-name = Продвинутое животворение
cp14-skill-life-t3-name = Экспертное животворение
cp14-skill-meta-t1-name = Базовая метамагия
cp14-skill-meta-t2-name = Продвинутая метамагия
cp14-skill-meta-t3-name = Экспертная метамагия
cp14-skill-alchemy-vision-name = Взор алхимика
cp14-skill-alchemy-vision-desc = Вы способны понимать какие именно жидкости находятся в емкостях, при помощи визуального анализа.
cp14-skill-copper-melt-name = Плавка меди
cp14-skill-iron-melt-name = Плавка железа
cp14-skill-gold-melt-name = Плавка золота
cp14-skill-mithril-melt-name = Плавка мифрила
cp14-skill-glass-melt-name = Работа со стеклом

View File

@@ -25,3 +25,14 @@ cp14-skill-tree-dimension-desc = Погрузитесь в природу про
cp14-skill-tree-atlethic-name = Атлетика
cp14-skill-tree-atlethic-desc = Развивайте свое тело, расширяя границы доступного.
cp14-skill-tree-martial-name = Боевые исскуства
cp14-skill-tree-martial-desc = Овладейте секретами смертельного оружия, или сделайте оружием свое собственное тело.
# Job
cp14-skill-tree-thaumaturgy-name = Алхимия
cp14-skill-tree-thaumaturgy-desc = Исскуство создания волшебных зелий, способных убивать, воскрешать из мертвых или превращать существ в овечек.
cp14-skill-tree-blacksmithing-name = Кузнечное дело
cp14-skill-tree-blacksmithing-desc = Исскуство превращения металла в различные полезные вещи.

View File

@@ -5,6 +5,12 @@ cp14-skill-info-title = Навыки
cp14-game-hud-open-skill-menu-button-tooltip = Деревья навыков
cp14-skill-menu-learn-button = Изучить навык
cp14-skill-menu-learncost = [color=yellow]Требуется очков:[/color]
cp14-skill-menu-skillpoints = Очков опыта:
cp14-skill-menu-level = Уровень:
cp14-skill-menu-learncost = [color=yellow]Требуется памяти:[/color]
cp14-skill-menu-level = Память:
cp14-research-table-title = Стол исследований
cp14-research-recipe-list = Затраты на исследование:
cp14-research-craft = Исследовать
cp14-skill-desc-add-mana = Увеличивает объем маны вашего персонажа на {$mana}.
cp14-skill-desc-unlock-recipes = Открывает возможность создания:

View File

@@ -51511,13 +51511,6 @@ entities:
rot: 1.5707963267948966 rad
pos: 102.332535,53.33797
parent: 1
- proto: CP14SpellScrollFireRune
entities:
- uid: 8072
components:
- type: Transform
pos: -100.13099,21.66595
parent: 1
- proto: CP14SpellScrollFlameCreation
entities:
- uid: 8073

View File

@@ -6,7 +6,6 @@
- type: StorageFill
contents:
- id: CP14Lighter
- id: CP14ManaOperationGlove
- id: CP14Syringe
amount: 2
- id: CP14Cauldron

View File

@@ -6,10 +6,6 @@
- type: StorageFill
contents:
- id: CP14GuidebookAlchemy
- id: CP14ClothingEyesAlchemyGlasses
prob: 0.5
- id: CP14ClothingEyesAlchemyMonocle
prob: 0.2
- id: CP14ClothingCloakAlchemist
prob: 0.6
- id: CP14ClothingHeadAlchemistBeret

View File

@@ -1,98 +0,0 @@
- type: entity
id: CP14ActionSpellFireRune
name: Fire rune
description: You create an area where a scalding stream of fire occurs with little delay.
components:
- type: Sprite
sprite: _CP14/Actions/Spells/fire.rsi
state: tiefling_revenge
- type: CP14MagicEffectCastSlowdown
speedMultiplier: 0.5
- type: CP14MagicEffectManaCost
manaCost: 15
- type: CP14MagicEffect
telegraphyEffects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:
- CP14TelegraphyFireRune
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:
- CP14AreaEntityEffectFireRune
- type: CP14MagicEffectCastingVisual
proto: CP14RuneEarthWall
- type: CP14MagicEffectPacifiedBlock
- type: EntityWorldTargetAction
range: 10
itemIconStyle: BigAction
sound: !type:SoundPathSpecifier
path: /Audio/Magic/rumble.ogg
icon:
sprite: _CP14/Actions/Spells/fire.rsi
state: tiefling_revenge
event: !type:CP14DelayedEntityWorldTargetActionEvent
cooldown: 10
- type: entity
id: CP14TelegraphyFireRune
parent: CP14BaseMagicImpact
categories: [ HideSpawnMenu ]
save: false
components:
- type: PointLight
color: "#eea911"
- type: TimedDespawn
lifetime: 0.8
- type: Sprite
noRot: true
drawdepth: BelowFloor
sprite: _CP14/Effects/Magic/area_impact.rsi
layers:
- state: area_impact_in
color: "#eea911"
scale: 2, 2
shader: unshaded
- type: entity
id: CP14AreaEntityEffectFireRune
parent: CP14BaseMagicImpact
categories: [ HideSpawnMenu ]
save: false
components:
- type: PointLight
color: "#eea911"
- type: Sprite
noRot: true
drawdepth: BelowFloor
sprite: _CP14/Effects/Magic/area_impact.rsi
layers:
- state: area_impact_out
color: "#eea911"
scale: 2, 2
shader: unshaded
- type: TimedDespawn
lifetime: 0.8
- type: CP14AreaEntityEffect
range: 1
whitelist:
components:
- Damageable
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:
- CP14ImpactEffectTieflingRevenge
- !type:CP14SpellApplyEntityEffect
effects:
- !type:HealthChange
damage:
types:
Heat: 10
- type: entity
parent: CP14BaseSpellScrollFire
id: CP14SpellScrollFireRune
name: fire rune spell scroll
components:
- type: CP14SpellStorage
spells:
- CP14ActionSpellFireRune

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectManaCost
manaCost: 20
- type: CP14MagicEffect
magicType: Fire
effects:
- !type:CP14SpellSpawnEntityOnUser
spawns:
@@ -36,7 +37,7 @@
state: fireball
event: !type:CP14DelayedEntityWorldTargetActionEvent
cooldown: 25
castDelay: 1.5
castDelay: 2.5
breakOnMove: false
- type: entity

View File

@@ -9,6 +9,7 @@
- type: CP14MagicEffectManaCost
manaCost: 5
- type: CP14MagicEffect
magicType: Fire
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectManaCost
manaCost: 1
- type: CP14MagicEffect
magicType: Fire
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectCastingVisual
proto: CP14RuneTieflingRevenge
- type: CP14MagicEffect
magicType: Fire
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectManaCost
manaCost: 10
- type: CP14MagicEffect
magicType: Life
telegraphyEffects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectManaCost
manaCost: 10
- type: CP14MagicEffect
magicType: Life
telegraphyEffects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectManaCost
manaCost: 10
- type: CP14MagicEffect
magicType: Life
telegraphyEffects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectManaCost
manaCost: 1
- type: CP14MagicEffect
magicType: Life
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectManaCost
manaCost: 3
- type: CP14MagicEffect
magicType: Life
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectManaCost
manaCost: 2
- type: CP14MagicEffect
magicType: Life
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectManaCost
manaCost: 5
- type: CP14MagicEffect
magicType: Life
telegraphyEffects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -13,6 +13,7 @@
- type: CP14MagicEffectAliveTargetRequired
inverted: true
- type: CP14MagicEffect
magicType: Life
telegraphyEffects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectManaCost
manaCost: 30
- type: CP14MagicEffect
magicType: Life
telegraphyEffects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectManaCost
manaCost: 1
- type: CP14MagicEffect
magicType: Life
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -9,6 +9,7 @@
- type: CP14MagicEffectManaCost
manaCost: 10
- type: CP14MagicEffect
magicType: Light
telegraphyEffects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -7,6 +7,7 @@
- type: CP14MagicEffectManaCost
manaCost: 5
- type: CP14MagicEffect
magicType: Light
- type: CP14MagicEffectSomaticAspect
- type: InstantAction
itemIconStyle: BigAction

View File

@@ -9,6 +9,7 @@
- type: CP14MagicEffectManaCost
manaCost: 10
- type: CP14MagicEffect
magicType: Light
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -9,6 +9,7 @@
- type: CP14MagicEffectManaCost
manaCost: 20
- type: CP14MagicEffect
magicType: Water
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectManaCost
manaCost: 7
- type: CP14MagicEffect
magicType: Water
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -7,8 +7,9 @@
sprite: _CP14/Actions/Spells/water.rsi
state: ice_arrow
- type: CP14MagicEffectManaCost
manaCost: 15
manaCost: 7
- type: CP14MagicEffect
magicType: Water
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:
@@ -30,7 +31,7 @@
sprite: _CP14/Actions/Spells/water.rsi
state: ice_arrow
event: !type:CP14DelayedInstantActionEvent
cooldown: 5
cooldown: 10
breakOnMove: false
- type: entity
@@ -78,7 +79,7 @@
onlyCollideWhenShot: true
damage:
types:
Piercing: 17
Piercing: 15
Cold: 5
- type: Damageable
- type: Destructible
@@ -93,7 +94,7 @@
- !type:DoActsBehavior
acts: ["Destruction"]
- type: TimedDespawn
lifetime: 60
lifetime: 240
- type: DamageOnLand
damage:
types:

View File

@@ -7,8 +7,9 @@
sprite: _CP14/Actions/Spells/water.rsi
state: ice_dagger
- type: CP14MagicEffectManaCost
manaCost: 15
manaCost: 25
- type: CP14MagicEffect
magicType: Water
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:
@@ -30,7 +31,7 @@
sprite: _CP14/Actions/Spells/water.rsi
state: ice_dagger
event: !type:CP14DelayedInstantActionEvent
cooldown: 20
cooldown: 10
breakOnMove: false
- type: entity

View File

@@ -11,6 +11,7 @@
- type: CP14MagicEffectManaCost
manaCost: 5
- type: CP14MagicEffect
magicType: Water
effects:
- !type:CP14SpellProjectile
prototype: CP14IceShard

View File

@@ -9,6 +9,7 @@
- type: CP14MagicEffectManaCost
manaCost: 10
- type: CP14MagicEffect
magicType: Water
effects:
- !type:CP14SpellSpawnEntityOnTarget
spawns:

View File

@@ -42,9 +42,6 @@
- type: CP14NightVision #Night vision
- type: FootstepModifier
footstepSoundCollection: null # Silent footstep
- type: CP14SkillStorage
progress:
Atlethic: 1
- type: Inventory
templateId: CP14Carcat # Cant wear shoes
speciesId: carcat

View File

@@ -35,9 +35,6 @@
requiredLegs: 2
- type: Bloodstream
bloodReagent: CP14BloodElf
- type: CP14SkillStorage
progress:
Metamagic: 1
- type: CP14MagicEnergyContainer #Increased mana container
maxEnergy: 200
energy: 200

View File

@@ -76,9 +76,6 @@
#- type: CP14MagicEnergyPhotosynthesis # Silva special feature #Disabled until sunlight fixed
#- type: CP14MagicEnergyDraw #Enabled default mana regen until sunlight fixed
# enable: false
- type: CP14SkillStorage
progress:
Healing: 1
- type: Body
prototype: CP14Silva
requiredLegs: 2

View File

@@ -48,9 +48,6 @@
globalModifier: 1.2
modifiers:
Fire: 0.5
- type: CP14SkillStorage
progress:
Pyrokinetic: 1
- type: CP14SpellStorage
#grantAccessToSelf: true
#spells:

View File

@@ -0,0 +1,78 @@
- type: entity
parent: BaseItem
id: CP14ModularBladeSkimitarBase
categories: [ ForkFiltered ]
abstract: true
description: A skimitar blade without a hilt. A blacksmith can use it as a spare part to create a weapon.
components:
- type: Item
storedRotation: 45
shape:
- 0,0,0,1
storedOffset: 0, 10
- type: entity
parent: CP14ModularBladeSkimitarBase
id: CP14ModularBladeIronSkimitar
name: iron skimitar blade
components:
- type: Sprite
sprite: _CP14/Objects/ModularTools/Blade/Skimitar/metall_skimitar.rsi
state: icon
- type: CP14ModularCraftPart
possibleParts:
- BladeIronSkimitar
- type: CP14Material
materials:
CP14Iron: 10
- type: entity
parent: CP14ModularBladeSkimitarBase
id: CP14ModularBladeGoldSkimitar
name: golden skimitar blade
components:
- type: Sprite
sprite: _CP14/Objects/ModularTools/Blade/Skimitar/metall_skimitar.rsi
layers:
- state: icon
color: "#ffe269"
- type: CP14ModularCraftPart
possibleParts:
- BladeGoldSkimitar
- type: CP14Material
materials:
CP14Gold: 10
- type: entity
parent: CP14ModularBladeSkimitarBase
id: CP14ModularBladeCopperSkimitar
name: copper skimitar blade
components:
- type: Sprite
sprite: _CP14/Objects/ModularTools/Blade/Skimitar/metall_skimitar.rsi
layers:
- state: icon
color: "#e28f08"
- type: CP14ModularCraftPart
possibleParts:
- BladeCopperSkimitar
- type: CP14Material
materials:
CP14Copper: 10
- type: entity
parent: CP14ModularBladeSkimitarBase
id: CP14ModularBladeMithrilSkimitar
name: mithril skimitar blade
components:
- type: Sprite
sprite: _CP14/Objects/ModularTools/Blade/Skimitar/metall_skimitar.rsi
layers:
- state: icon
color: "#38f0b3"
- type: CP14ModularCraftPart
possibleParts:
- BladeMithrilSkimitar
- type: CP14Material
materials:
CP14Mithril: 10

View File

@@ -12,6 +12,9 @@
sprite: _CP14/Objects/Specific/Thaumaturgy/crystal.rsi
- type: CP14MagicEnergyContainer
- type: CP14MagicEnergyExaminable
- type: Tag
tags:
- CP14EnergyCrystal
- type: entity
id: CP14EnergyCrystalMedium

View File

@@ -0,0 +1,57 @@
- type: entity
parent:
- BaseStructure
id: CP14ResearchTable
categories: [ ForkFiltered ]
name: research table
description: A place of research, experimentation and discovery that allows you to be smarter.
components:
- type: Sprite
snapCardinals: true
sprite: _CP14/Structures/Furniture/workbench.rsi
state: research_table
- type: Icon
sprite: _CP14/Structures/Furniture/workbench.rsi
state: research_table
- type: ActivatableUI
key: enum.CP14ResearchTableUiKey.Key
requiresComplex: true
singleUser: true
- type: Climbable
- type: Clickable
- type: CP14ResearchTable
- type: InteractionOutline
- type: PlaceableSurface
- type: UserInterface
interfaces:
enum.CP14ResearchTableUiKey.Key:
type: CP14ResearchTableBoundUserInterface
- type: Damageable
damageContainer: Inorganic
damageModifierSet: Wood
- type: Destructible
thresholds:
- trigger:
!type:DamageTypeTrigger
damageType: Heat
damage: 40
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
- !type:PlaySoundBehavior
sound:
collection: WoodDestroy
- trigger:
!type:DamageTrigger
damage: 60
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
- !type:PlaySoundBehavior
sound:
collection: WoodDestroy
- !type:SpawnEntitiesBehavior
spawn:
CP14WoodenPlanks1:
min: 1
max: 2

View File

@@ -1,7 +1,6 @@
- type: roleLoadout
id: JobCP14Adventurer
groups:
- CP14SkillTree
- CP14GeneralHead
- CP14GeneralOuterClothing
- CP14GeneralEyes
@@ -18,7 +17,6 @@
- type: roleLoadout
id: JobCP14Apprentice
groups:
- CP14SkillTree
- CP14GeneralHead
- CP14GeneralOuterClothing
- CP14GeneralEyes
@@ -33,7 +31,6 @@
- type: roleLoadout
id: JobCP14Alchemist
groups:
- CP14SkillTree
- CP14AlchemistHead #
- CP14GeneralOuterClothing
- CP14AlchemistEyes #
@@ -49,7 +46,6 @@
- type: roleLoadout
id: JobCP14Innkeeper
groups:
- CP14SkillTree
- CP14GeneralHead
- CP14GeneralOuterClothing
- CP14GeneralEyes
@@ -64,7 +60,6 @@
- type: roleLoadout
id: JobCP14Blacksmith
groups:
- CP14SkillTree
- CP14GeneralHead
- CP14GeneralOuterClothing
- CP14GeneralEyes
@@ -79,7 +74,6 @@
- type: roleLoadout
id: JobCP14GuardCommander
groups:
- CP14SkillTree
- CP14GuardHead
- CP14GeneralOuterClothing
- CP14GeneralEyes
@@ -95,7 +89,6 @@
- type: roleLoadout
id: JobCP14Guard
groups:
- CP14SkillTree
- CP14GuardHead
- CP14GeneralOuterClothing
- CP14GeneralEyes
@@ -111,7 +104,6 @@
- type: roleLoadout
id: JobCP14Commandant
groups:
- CP14SkillTree
- CP14MerchantOuterClothing
- CP14GeneralEyes
- CP14MerchantShirt
@@ -123,7 +115,6 @@
- type: roleLoadout
id: JobCP14Guildmaster
groups:
- CP14SkillTree
- CP14GuildmasterOuterClothing
- CP14GeneralEyes
- CP14GuildmasterHead
@@ -138,7 +129,6 @@
- type: roleLoadout
id: JobCP14Merchant
groups:
- CP14SkillTree
- CP14MerchantHead
- CP14MerchantOuterClothing
- CP14GeneralEyes

View File

@@ -1,119 +0,0 @@
- type: loadoutGroup
id: CP14SkillTree
name: cp14-loadout-skill-tree
minLimit: 2
maxLimit: 2
loadouts:
- CP14SkillTreeMetamagic
- CP14SkillTreeHydrosophistry
- CP14SkillTreePyrokinetic
- CP14SkillTreeIllusion
- CP14SkillTreeHealing
- CP14SkillTreeAtlethic
#- CP14SkillTreeDimension Disabled until teleport & grab collider fix
- type: entity
id: CP14SkillTreePyrokineticLoadoutDummy
name: Pyrokinetic
categories: [ HideSpawnMenu ]
components:
- type: Sprite
sprite: _CP14/Actions/skill_tree.rsi
state: pyro
- type: loadout
id: CP14SkillTreePyrokinetic
dummyEntity: CP14SkillTreePyrokineticLoadoutDummy
skillTree:
Pyrokinetic: 2
- type: entity
id: CP14SkillTreeHydrosophistryLoadoutDummy
name: Hydrosophistry
categories: [ HideSpawnMenu ]
components:
- type: Sprite
sprite: _CP14/Actions/skill_tree.rsi
state: water
- type: loadout
id: CP14SkillTreeHydrosophistry
dummyEntity: CP14SkillTreeHydrosophistryLoadoutDummy
skillTree:
Hydrosophistry: 2
- type: entity
id: CP14SkillTreeIllusionLoadoutDummy
name: Illusion
categories: [ HideSpawnMenu ]
components:
- type: Sprite
sprite: _CP14/Actions/skill_tree.rsi
state: light
- type: loadout
id: CP14SkillTreeIllusion
dummyEntity: CP14SkillTreeIllusionLoadoutDummy
skillTree:
Illusion: 2
- type: entity
id: CP14SkillTreeMetamagicLoadoutDummy
name: Metamagic
categories: [ HideSpawnMenu ]
components:
- type: Sprite
sprite: _CP14/Actions/skill_tree.rsi
state: meta
- type: loadout
id: CP14SkillTreeMetamagic
dummyEntity: CP14SkillTreeMetamagicLoadoutDummy
skillTree:
Metamagic: 2
- type: entity
id: CP14SkillTreeHealingLoadoutDummy
name: Healing
categories: [ HideSpawnMenu ]
components:
- type: Sprite
sprite: _CP14/Actions/skill_tree.rsi
state: heal
- type: loadout
id: CP14SkillTreeHealing
dummyEntity: CP14SkillTreeHealingLoadoutDummy
skillTree:
Healing: 2
- type: entity
id: CP14SkillTreeAtlethicLoadoutDummy
name: Atlethic
categories: [ HideSpawnMenu ]
components:
- type: Sprite
sprite: _CP14/Actions/skill_tree.rsi
state: atlethic
- type: loadout
id: CP14SkillTreeAtlethic
dummyEntity: CP14SkillTreeAtlethicLoadoutDummy
skillTree:
Atlethic: 2
- type: entity
id: CP14SkillTreeDimensionLoadoutDummy
name: Dimension
categories: [ HideSpawnMenu ]
components:
- type: Sprite
sprite: _CP14/Actions/skill_tree.rsi
state: dimension
- type: loadout
id: CP14SkillTreeDimension
dummyEntity: CP14SkillTreeDimensionLoadoutDummy
skillTree:
Dimension: 2

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