Merge branch 'master' into ed-12-05-2025-upstream

This commit is contained in:
Red
2025-05-23 12:58:11 +03:00
committed by GitHub
501 changed files with 8545 additions and 4656 deletions

View File

@@ -1,4 +1,3 @@
using Content.Client._CP14.JoinQueue;
using Content.Shared._CP14.JoinQueue;
using Robust.Client.Audio;
using Robust.Client.Console;

View File

@@ -27,5 +27,5 @@ public sealed partial class CP14LocalizationVisualsComponent : Component
///
/// </summary>
[DataField]
public Dictionary<string, Dictionary<string, string>> MapStates;
public Dictionary<string, Dictionary<string, string>> MapStates = new();
}

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)
@@ -135,7 +136,7 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered<Gamepl
if (_window is null)
return;
if (_playerManager.LocalEntity == null)
if (_targetPlayer == null)
return;
if (node == null)
@@ -158,7 +159,7 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered<Gamepl
if (_window is null)
return;
if (_playerManager.LocalEntity == null)
if (_targetPlayer == null)
return;
_selectedSkill = skill;
@@ -171,8 +172,9 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered<Gamepl
{
_window.SkillName.Text = _skill.GetSkillName(skill);
_window.SkillDescription.SetMessage(GetSkillDescription(skill));
_window.SkillFree.Visible = _skill.HaveFreeSkill(_targetPlayer.Value, skill);
_window.SkillView.Texture = skill.Icon.Frame0();
_window.LearnButton.Disabled = !_skill.CanLearnSkill(_playerManager.LocalEntity.Value, skill);
_window.LearnButton.Disabled = !_skill.CanLearnSkill(_targetPlayer.Value, skill);
_window.SkillCost.Text = skill.LearnCost.ToString();
}
@@ -186,6 +188,7 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered<Gamepl
_window.SkillName.Text = string.Empty;
_window.SkillDescription.Text = string.Empty;
_window.SkillFree.Visible = false;
_window.SkillView.Texture = null;
_window.LearnButton.Disabled = true;
}
@@ -194,7 +197,7 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered<Gamepl
{
var msg = new FormattedMessage();
if (_playerManager.LocalEntity == null)
if (_targetPlayer == null)
return msg;
var sb = new StringBuilder();
@@ -202,12 +205,15 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered<Gamepl
//Description
sb.Append(_skill.GetSkillDescription(skill) + "\n \n");
//Restrictions
foreach (var req in skill.Restrictions)
if (!_skill.HaveSkill(_targetPlayer.Value, skill))
{
var color = req.Check(_entManager, _playerManager.LocalEntity.Value) ? "green" : "red";
//Restrictions
foreach (var req in skill.Restrictions)
{
var color = req.Check(_entManager, _targetPlayer.Value, skill) ? "green" : "red";
sb.Append($"- [color={color}]{req.GetDescription(_entManager, _proto)}[/color]\n");
sb.Append($"- [color={color}]{req.GetDescription(_entManager, _proto)}[/color]\n");
}
}
msg.TryAddMarkup(sb.ToString(), out _);
@@ -284,14 +290,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 +304,28 @@ 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)
{
if (_skill.HaveFreeSkill(_targetPlayer.Value, skillId))
continue;
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 +341,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"
@@ -46,6 +45,9 @@
<BoxContainer HorizontalExpand="True">
<RichTextLabel Name="SkillDescription" HorizontalExpand="True" Access="Public"/>
</BoxContainer>
<BoxContainer HorizontalExpand="True">
<RichTextLabel Name="SkillFree" Text="{Loc 'cp14-skill-menu-free'}" HorizontalExpand="True" Access="Public" Visible="False"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</ScrollContainer>
@@ -76,10 +78,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

@@ -1,10 +0,0 @@
namespace Content.Client._CP14.WorldSprite;
/// <summary>
/// RUN, IF YOU SEE THE CODE TO THIS FUCKING THING, YOU'LL DIE ON THE SPOT.
/// </summary>
[RegisterComponent]
public sealed partial class CP14WorldSpriteComponent : Component
{
public bool Inited;
}

View File

@@ -1,65 +0,0 @@
using Content.Shared._CP14.WorldSprite;
using Content.Shared.Throwing;
using Robust.Client.GameObjects;
using Robust.Shared.Map.Components;
namespace Content.Client._CP14.WorldSprite;
public sealed class CP14WorldSpriteSystem : EntitySystem
{
[Dependency] private readonly AppearanceSystem _appearance = default!;
public override void Initialize()
{
base.Initialize();
#if DEBUG
SubscribeLocalEvent<CP14WorldSpriteComponent, ComponentInit>(OnComponentInit);
#endif
SubscribeLocalEvent<CP14WorldSpriteComponent, EntParentChangedMessage>(OnParentChanged);
SubscribeLocalEvent<CP14WorldSpriteComponent, ThrownEvent>(OnThrown);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<CP14WorldSpriteComponent>();
while (query.MoveNext(out var uid, out var worldSpriteComponent))
{
if (worldSpriteComponent.Inited)
continue;
Update(uid);
}
}
#if DEBUG
private void OnComponentInit(Entity<CP14WorldSpriteComponent> entity, ref ComponentInit args)
{
if (!HasComp<AppearanceComponent>(entity))
Log.Error($"Requires an {nameof(AppearanceComponent)} for {entity}");
}
#endif
private void OnParentChanged(Entity<CP14WorldSpriteComponent> entity, ref EntParentChangedMessage args)
{
Update(entity);
}
private void OnThrown(Entity<CP14WorldSpriteComponent> entity, ref ThrownEvent args)
{
// Idk, but throw don't call reparent
Update(entity, args.User);
}
private void Update(EntityUid entity, EntityUid? parent = null)
{
parent ??= Transform(entity).ParentUid;
var inWorld = HasComp<MapComponent>(parent) || HasComp<MapGridComponent>(parent);
_appearance.SetData(entity, WorldSpriteVisualLayers.Layer, inWorld);
}
}

View File

@@ -64,7 +64,7 @@ public sealed partial class ChatSystem : SharedChatSystem
public const int VoiceRange = 10; // how far voice goes in world units
public const int WhisperClearRange = 2; // how far whisper goes while still being understandable, in world units
public const int WhisperMuffledRange = 5; // how far whisper goes at all, in world units
public const string DefaultAnnouncementSound = "/Audio/Announcements/announce.ogg";
public const string DefaultAnnouncementSound = "/Audio/_CP14/Announce/event_boom.ogg"; //CP14 replaced default sound
private bool _loocEnabled = true;
private bool _deadLoocEnabled;

View File

@@ -5,7 +5,6 @@ namespace Content.Server.Entry
{
public static string[] List => new[] {
"CP14WaveShader", // CP14 Wave shader
"CP14WorldSprite", // CP14 World Sprite
"ConstructionGhost",
"IconSmooth",
"InteractionOutline",

View File

@@ -1,5 +1,4 @@
using Content.Server._CP14.Objectives.Systems;
using Content.Server._CP14.StealArea;
using Content.Server.Objectives.Systems;
using Content.Server.Thief.Systems;
@@ -8,7 +7,7 @@ namespace Content.Server.Objectives.Components;
/// <summary>
/// An abstract component that allows other systems to count adjacent objects as "stolen" when controlling other systems
/// </summary>
[RegisterComponent, Access(typeof(StealConditionSystem), typeof(ThiefBeaconSystem), typeof(CP14CurrencyCollectConditionSystem), typeof(CP14StealAreaAutoJobConnectSystem))] //CP14 add currency condition access
[RegisterComponent, Access(typeof(StealConditionSystem), typeof(ThiefBeaconSystem), typeof(CP14CurrencyCollectConditionSystem))]
public sealed partial class StealAreaComponent : Component
{
[DataField]

View File

@@ -324,7 +324,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
private bool CanLoad(EntityUid uid)
{
return !_ghostQuery.HasComp(uid) || _tags.HasTag(uid, AllowBiomeLoadingTag);
return !_ghostQuery.HasComp(uid);// CP14 - Admin biome loading break mapping || _tags.HasTag(uid, AllowBiomeLoadingTag);
}
public override void Update(float frameTime)

View File

@@ -115,8 +115,6 @@ public sealed partial class CP14DemiplaneSystem
demiplane = ev.Demiplane;
}
_statistic.TrackAdd(generator.Comp.Statistic, 1);
if (demiplane is null)
return;

View File

@@ -1,5 +1,4 @@
using Content.Server._CP14.Demiplane.Components;
using Content.Server._CP14.RoundStatistic;
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
using Content.Server.Flash;
@@ -33,7 +32,6 @@ public sealed partial class CP14DemiplaneSystem : CP14SharedDemiplaneSystem
[Dependency] private readonly FlashSystem _flash = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly CP14RoundStatTrackerSystem _statistic = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly IChatManager _chatManager = default!;

View File

@@ -1,6 +1,5 @@
using Content.Server._CP14.DemiplaneTraveling;
using Content.Shared._CP14.Demiplane.Prototypes;
using Content.Shared._CP14.RoundStatistic;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.Demiplane.Components;
@@ -17,9 +16,6 @@ public sealed partial class CP14DemiplaneDataComponent : Component
[DataField]
public List<ProtoId<CP14DemiplaneModifierPrototype>> SelectedModifiers = new();
[DataField]
public ProtoId<CP14RoundStatTrackerPrototype> Statistic = "DemiplaneOpen";
[DataField]
public List<EntProtoId> AutoRifts = new() { "CP14DemiplaneTimedRadiusPassway", "CP14DemiplanRiftCore" };
}

View File

@@ -236,7 +236,7 @@ public sealed partial class CP14StationDemiplaneMapSystem : CP14SharedStationDem
new Vector2(specialPos.X, specialPos.Y),
false,
locationConfig: special.Location,
modifiers: special.Modifiers
modifiers: [..special.Modifiers]
);
grid[specialPos] = specialNode;
specialPositions.Add(specialPos);
@@ -315,8 +315,9 @@ public sealed partial class CP14StationDemiplaneMapSystem : CP14SharedStationDem
var limits = new Dictionary<ProtoId<CP14DemiplaneModifierCategoryPrototype>, float>
{
{ "Danger", (node.Level + node.AdditionalLevel) * 0.2f },
{ "GhostRoleDanger", node.Level * 0.2f },
{ "GhostRoleDanger", 1f },
{ "Reward", Math.Max(node.Level * 0.2f, 0.5f) },
{ "Ore", Math.Max(node.Level * 0.2f, 0.5f) },
{ "Fun", 1f },
{ "Weather", 1f },
{ "MapLight", 1f },

View File

@@ -42,6 +42,7 @@ public sealed class DiscordAuthManager
"1294276016117911594",
"1278755078315970620",
"1330772249644630157",
"1274951101464051846",
};
public event EventHandler<ICommonSession>? PlayerVerified;

View File

@@ -1,3 +1,4 @@
using Content.Shared._CP14.DayCycle;
using Content.Shared._CP14.Farming.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
@@ -5,6 +6,7 @@ namespace Content.Server._CP14.Farming;
public sealed partial class CP14FarmingSystem
{
[Dependency] private readonly CP14DayCycleSystem _dayCycle = default!;
private void InitializeResources()
{
SubscribeLocalEvent<CP14PlantEnergyFromLightComponent, CP14PlantUpdateEvent>(OnTakeEnergyFromLight);
@@ -16,7 +18,7 @@ public sealed partial class CP14FarmingSystem
private void OnTakeEnergyFromLight(Entity<CP14PlantEnergyFromLightComponent> regeneration, ref CP14PlantUpdateEvent args)
{
var gainEnergy = false;
var daylight = true;//_dayCycle.UnderSunlight(regeneration);
var daylight = _dayCycle.UnderSunlight(regeneration);
if (regeneration.Comp.Daytime && daylight)
gainEnergy = true;

View File

@@ -0,0 +1,114 @@
using System.Linq;
using Content.Server._CP14.GameTicking.Rules.Components;
using Content.Server.Antag;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking.Rules;
using Content.Server.Popups;
using Content.Server.Station.Components;
using Content.Server.Stunnable;
using Content.Shared._CP14.BloodMoon;
using Content.Shared._CP14.DayCycle;
using Content.Shared.Actions;
using Content.Shared.Examine;
using Content.Shared.GameTicking.Components;
using Content.Shared.Mobs;
using Content.Shared.Popups;
using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
namespace Content.Server._CP14.GameTicking.Rules;
public sealed class CP14BloodMoonCurseRule : GameRuleSystem<CP14BloodMoonCurseRuleComponent>
{
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly StunSystem _stun = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedActionsSystem _action = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14StartDayEvent>(OnStartDay);
SubscribeLocalEvent<CP14BloodMoonCurseRuleComponent, AfterAntagEntitySelectedEvent>(AfterAntagEntitySelected);
SubscribeLocalEvent<CP14BloodMoonCurseComponent, ExaminedEvent>(CurseExamined);
}
private void CurseExamined(Entity<CP14BloodMoonCurseComponent> ent, ref ExaminedEvent args)
{
args.PushMarkup(Loc.GetString("cp14-bloodmoon-curse-examined"));
}
private void AfterAntagEntitySelected(Entity<CP14BloodMoonCurseRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
{
SpawnAttachedTo(ent.Comp.CurseEffect, Transform(args.EntityUid).Coordinates);
var curseComp = EnsureComp<CP14BloodMoonCurseComponent>(args.EntityUid);
var effect = SpawnAttachedTo(curseComp.CurseEffect, Transform(args.EntityUid).Coordinates);
curseComp.SpawnedEffect = effect;
curseComp.CurseRule = ent;
_transform.SetParent(effect, args.EntityUid);
_action.AddAction(args.EntityUid, ref curseComp.ActionEntity, curseComp.Action);
}
protected override void Started(EntityUid uid,
CP14BloodMoonCurseRuleComponent component,
GameRuleComponent gameRule,
GameRuleStartedEvent args)
{
Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame);
_chatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(component.StartAnnouncement), colorOverride: component.AnnouncementColor);
_audio.PlayGlobal(component.GlobalSound, allPlayersInGame, true);
}
protected override void Ended(EntityUid uid,
CP14BloodMoonCurseRuleComponent component,
GameRuleComponent gameRule,
GameRuleEndedEvent args)
{
Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame);
_chatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(component.EndAnnouncement), colorOverride: component.AnnouncementColor);
var aliveAntags = _antag.GetAliveAntags(uid);
foreach (var antag in aliveAntags)
{
SpawnAttachedTo(component.CurseEffect, Transform(antag).Coordinates);
ClearCurse(antag);
}
GameTicker.EndRound();
}
private void OnStartDay(CP14StartDayEvent ev)
{
if (!HasComp<BecomesStationComponent>(ev.Map))
return;
var query = QueryActiveRules();
while (query.MoveNext(out var uid, out _, out var comp, out _))
{
ForceEndSelf(uid);
}
}
private void ClearCurse(Entity<CP14BloodMoonCurseComponent?> ent)
{
if (!Resolve(ent.Owner, ref ent.Comp, false))
return;
_stun.TryParalyze(ent, ent.Comp.EndStunDuration, true);
_popup.PopupEntity(Loc.GetString("cp14-bloodmoon-curse-removed"), ent, PopupType.SmallCaution);
if (TryComp<CP14BloodMoonCurseComponent>(ent, out var curseComp))
{
QueueDel(curseComp.SpawnedEffect);
RemCompDeferred<CP14BloodMoonCurseComponent>(ent);
}
_action.RemoveAction(ent.Comp.ActionEntity);
}
}

View File

@@ -0,0 +1,54 @@
using Content.Server._CP14.GameTicking.Rules.Components;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking.Rules;
using Content.Server.Station.Components;
using Content.Server.StationEvents.Events;
using Content.Shared._CP14.DayCycle;
using Content.Shared.GameTicking.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
namespace Content.Server._CP14.GameTicking.Rules;
public sealed class CP14BloodMoonRule : GameRuleSystem<CP14BloodMoonRuleComponent>
{
[Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14StartNightEvent>(OnStartNight);
}
protected override void Started(EntityUid uid,
CP14BloodMoonRuleComponent component,
GameRuleComponent gameRule,
GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame);
_chatSystem.DispatchFilteredAnnouncement(
allPlayersInGame,
message: Loc.GetString(component.StartAnnouncement),
colorOverride: component.AnnouncementColor);
_audio.PlayGlobal(component.AnnounceSound, allPlayersInGame, true);
}
private void OnStartNight(CP14StartNightEvent ev)
{
if (!HasComp<BecomesStationComponent>(ev.Map))
return;
var query = QueryActiveRules();
while (query.MoveNext(out var uid, out _, out var comp, out _))
{
var ruleEnt = GameTicker.AddGameRule(comp.CurseRule);
GameTicker.StartGameRule(ruleEnt);
ForceEndSelf(uid);
}
}
}

View File

@@ -0,0 +1,26 @@
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.GameTicking.Rules.Components;
[RegisterComponent, Access(typeof(CP14BloodMoonCurseRule))]
public sealed partial class CP14BloodMoonCurseRuleComponent : Component
{
[DataField]
public LocId StartAnnouncement = "cp14-bloodmoon-start";
[DataField]
public LocId EndAnnouncement = "cp14-bloodmoon-end";
[DataField]
public Color? AnnouncementColor;
[DataField]
public EntProtoId CurseEffect = "CP14ImpactEffectMagicSplitting";
[DataField]
public SoundSpecifier GlobalSound = new SoundPathSpecifier("/Audio/_CP14/Ambience/blood_moon_raise.ogg")
{
Params = AudioParams.Default.WithVolume(-2f)
};
}

View File

@@ -0,0 +1,20 @@
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.GameTicking.Rules.Components;
[RegisterComponent, Access(typeof(CP14BloodMoonRule))]
public sealed partial class CP14BloodMoonRuleComponent : Component
{
[DataField]
public EntProtoId CurseRule = "CP14BloodMoonCurseRule";
[DataField]
public LocId StartAnnouncement = "cp14-bloodmoon-raising";
[DataField]
public Color? AnnouncementColor = Color.FromHex("#e32759");
[DataField]
public SoundSpecifier? AnnounceSound;
}

View File

@@ -1,15 +1,16 @@
using Content.Server._CP14.MagicEnergy.Components;
using Content.Shared._CP14.DayCycle;
using Content.Shared._CP14.MagicEnergy.Components;
using Content.Shared.Damage;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Robust.Shared.Map.Components;
namespace Content.Server._CP14.MagicEnergy;
public partial class CP14MagicEnergySystem
{
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly CP14DayCycleSystem _dayCycle = default!;
private void InitializeDraw()
{
@@ -75,16 +76,7 @@ public partial class CP14MagicEnergySystem
draw.NextUpdateTime = _gameTiming.CurTime + TimeSpan.FromSeconds(draw.Delay);
var daylight = false;
//if (TryComp<MapLightComponent>(Transform(uid).MapUid, out var mapLight))
//{
// var color = mapLight.AmbientLightColor;
// var medium = (color.R + color.G + color.B) / 3f;
//
// if (medium > draw.LightThreshold)
// daylight = true;
//}
var daylight = _dayCycle.UnderSunlight(uid);
ChangeEnergy((uid, magicContainer), daylight ? draw.DaylightEnergy : draw.DarknessEnergy, out _, out _, true);
}

View File

@@ -1,25 +0,0 @@
using Content.Server._CP14.Objectives.Systems;
using Content.Shared.Roles;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Server._CP14.Objectives.Components;
/// <summary>
/// The player must be the richest among the players among the specified list of roles
/// </summary>
[RegisterComponent, Access(typeof(CP14RichestJobConditionSystem))]
public sealed partial class CP14RichestJobConditionComponent : Component
{
[DataField(required: true)]
public ProtoId<JobPrototype> Job;
[DataField(required: true)]
public LocId ObjectiveText;
[DataField(required: true)]
public LocId ObjectiveDescription;
[DataField(required: true)]
public SpriteSpecifier ObjectiveSprite;
}

View File

@@ -1,26 +0,0 @@
using Content.Server._CP14.Objectives.Systems;
using Content.Shared._CP14.RoundStatistic;
using Content.Shared.Destructible.Thresholds;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Server._CP14.Objectives.Components;
[RegisterComponent, Access(typeof(CP14StatisticRangeConditionSystem))]
public sealed partial class CP14StatisticRangeConditionComponent : Component
{
[DataField(required: true)]
public ProtoId<CP14RoundStatTrackerPrototype> Statistic;
[DataField(required: true)]
public MinMax Range;
[DataField(required: true)]
public LocId ObjectiveText;
[DataField(required: true)]
public LocId ObjectiveDescription;
[DataField(required: true)]
public SpriteSpecifier? ObjectiveSprite;
}

View File

@@ -1,74 +0,0 @@
using Content.Server._CP14.Objectives.Components;
using Content.Shared._CP14.Currency;
using Content.Shared.Mind;
using Content.Shared.Objectives.Components;
using Content.Shared.Objectives.Systems;
using Content.Shared.Roles.Jobs;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.Objectives.Systems;
public sealed class CP14RichestJobConditionSystem : EntitySystem
{
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
[Dependency] private readonly CP14SharedCurrencySystem _currency = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedJobSystem _job = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14RichestJobConditionComponent, ObjectiveAfterAssignEvent>(OnCollectAfterAssign);
SubscribeLocalEvent<CP14RichestJobConditionComponent, ObjectiveGetProgressEvent>(OnCollectGetProgress);
}
private void OnCollectAfterAssign(Entity<CP14RichestJobConditionComponent> condition, ref ObjectiveAfterAssignEvent args)
{
if (!_proto.TryIndex(condition.Comp.Job, out var indexedJob))
return;
_metaData.SetEntityName(condition.Owner, Loc.GetString(condition.Comp.ObjectiveText), args.Meta);
_metaData.SetEntityDescription(condition.Owner, Loc.GetString(condition.Comp.ObjectiveDescription), args.Meta);
_objectives.SetIcon(condition.Owner, condition.Comp.ObjectiveSprite);
}
private void OnCollectGetProgress(Entity<CP14RichestJobConditionComponent> condition, ref ObjectiveGetProgressEvent args)
{
args.Progress = GetProgress(args.MindId, args.Mind, condition);
}
private float GetProgress(EntityUid mindId, MindComponent mind, CP14RichestJobConditionComponent condition)
{
if (mind.OwnedEntity is null)
return 0;
var ourValue = _currency.GetTotalCurrencyRecursive(mind.OwnedEntity.Value);
var otherMaxValue = 0;
var allHumans = _mind.GetAliveHumans(mindId);
if (allHumans.Count == 0)
return 1; // No one to compare to, so we're the richest.
foreach (var otherHuman in allHumans)
{
if (!_job.MindTryGetJob(otherHuman, out var otherJob))
continue;
if (otherJob != condition.Job)
continue;
if (otherHuman.Comp.OwnedEntity is null)
continue;
var otherValue = _currency.GetTotalCurrencyRecursive(otherHuman.Comp.OwnedEntity.Value);
if (otherValue > otherMaxValue)
otherMaxValue = otherValue;
}
// if several players have the same amount of money, no one wins.
return ourValue == otherMaxValue ? 0.99f : Math.Clamp(ourValue / (float)otherMaxValue, 0, 1);
}
}

View File

@@ -1,54 +0,0 @@
using Content.Server._CP14.Objectives.Components;
using Content.Server._CP14.RoundStatistic;
using Content.Shared.Objectives.Components;
using Content.Shared.Objectives.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server._CP14.Objectives.Systems;
public sealed class CP14StatisticRangeConditionSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
[Dependency] private readonly CP14RoundStatTrackerSystem _statistic = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14StatisticRangeConditionComponent, ObjectiveAfterAssignEvent>(OnAfterAssign);
SubscribeLocalEvent<CP14StatisticRangeConditionComponent, ObjectiveGetProgressEvent>(OnGetProgress);
}
private void OnAfterAssign(Entity<CP14StatisticRangeConditionComponent> condition, ref ObjectiveAfterAssignEvent args)
{
var title = Loc.GetString(condition.Comp.ObjectiveText,
("min", condition.Comp.Range.Min),
("max", condition.Comp.Range.Max));
var description = Loc.GetString(condition.Comp.ObjectiveDescription,
("min", condition.Comp.Range.Min),
("max", condition.Comp.Range.Max));
_metaData.SetEntityName(condition.Owner, title, args.Meta);
_metaData.SetEntityDescription(condition.Owner, description, args.Meta);
if (condition.Comp.ObjectiveSprite is not null)
_objectives.SetIcon(condition.Owner, condition.Comp.ObjectiveSprite, args.Objective);
}
private void OnGetProgress(Entity<CP14StatisticRangeConditionComponent> ent, ref ObjectiveGetProgressEvent args)
{
var statValue = _statistic.GetTrack(ent.Comp.Statistic);
if (statValue is null || statValue > ent.Comp.Range.Max || statValue < ent.Comp.Range.Min)
{
args.Progress = 0;
return;
}
args.Progress = 1;
}
}

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

@@ -1,6 +0,0 @@
namespace Content.Server._CP14.RoleSalary;
[RegisterComponent, Access(typeof(CP14SalarySystem))]
public sealed partial class CP14SalarySpawnerComponent : Component
{
}

View File

@@ -1,118 +0,0 @@
using System.Text;
using Content.Server._CP14.Cargo;
using Content.Server._CP14.Currency;
using Content.Server.Station.Events;
using Content.Shared.Paper;
using Content.Shared.Station.Components;
using Content.Shared.Storage.EntitySystems;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server._CP14.RoleSalary;
/// <summary>
/// A system that periodically sends paychecks to certain roles through the cargo ship system
/// </summary>
public sealed partial class CP14SalarySystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly CP14CargoSystem _cargo = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly PaperSystem _paper = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly CP14CurrencySystem _currency = default!;
[Dependency] private readonly SharedStorageSystem _storage = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14StationSalaryComponent, StationPostInitEvent>(OnStationPostInit);
SubscribeLocalEvent<CP14SalarySpawnerComponent, MapInitEvent>(OnSalaryInit);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
//var query = EntityQueryEnumerator<CP14StationSalaryComponent, CP14StationTravelingStoreShipTargetComponent>();
//while (query.MoveNext(out var uid, out var salary, out var store))
//{
// if (_timing.CurTime < salary.NextSalaryTime)
// continue;
//
// salary.NextSalaryTime = _timing.CurTime + salary.SalaryFrequency;
// _cargo.AddBuyQueue((uid, store), new List<EntProtoId> {salary.SalaryProto});
//}
}
private void OnSalaryInit(Entity<CP14SalarySpawnerComponent> ent, ref MapInitEvent args)
{
GenerateSalary(ent);
QueueDel(ent);
}
private void GenerateSalary(Entity<CP14SalarySpawnerComponent> ent)
{
//Hardcode warning! ^-^
var xform = Transform(ent);
//First we need found a station
if (!TryComp<StationMemberComponent>(xform.GridUid, out var member))
return;
if (!TryComp<CP14StationSalaryComponent>(member.Station, out var stationSalaryComponent))
return;
var paper = Spawn("CP14Paper"); //TODO Special named paper
_transform.PlaceNextTo(paper, (ent, xform));
if (TryComp<PaperComponent>(paper, out var paperComp))
{
paperComp.Content = GenerateSalaryText((member.Station, stationSalaryComponent)) ?? "";
_paper.TryStamp((paper, paperComp),
new StampDisplayInfo
{
StampedColor = Color.Red,
StampedName = Loc.GetString("cp14-stamp-salary"),
},
"red_on_paper");
}
var wallet = Spawn("CP14Wallet");
_transform.PlaceNextTo(wallet, (ent, xform));
foreach (var salary in stationSalaryComponent.Salary)
{
var coins = _currency.GenerateMoney(salary.Value, xform.Coordinates);
foreach (var coin in coins)
{
_storage.Insert(wallet, coin, out _);
}
}
}
private string? GenerateSalaryText(Entity<CP14StationSalaryComponent> station)
{
var sb = new StringBuilder();
sb.Append(Loc.GetString("cp14-salary-title") + "\n");
foreach (var salary in station.Comp.Salary)
{
sb.Append("\n");
if (!_proto.TryIndex(salary.Key, out var indexedDep))
continue;
var name = Loc.GetString(indexedDep.Name);
sb.Append(Loc.GetString("cp14-salary-entry",
("dep", name),
("total", _currency.GetCurrencyPrettyString(salary.Value))));
}
return sb.ToString();
}
private void OnStationPostInit(Entity<CP14StationSalaryComponent> ent, ref StationPostInitEvent args)
{
ent.Comp.NextSalaryTime = _timing.CurTime + ent.Comp.SalaryFrequency;
}
}

View File

@@ -1,23 +0,0 @@
using Content.Shared.Roles;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.RoleSalary;
[RegisterComponent, Access(typeof(CP14SalarySystem)), AutoGenerateComponentPause]
public sealed partial class CP14StationSalaryComponent : Component
{
/// <summary>
/// listing all the departments and their salaries
/// </summary>
[DataField]
public Dictionary<ProtoId<DepartmentPrototype>, int> Salary = new();
[DataField, AutoPausedField]
public TimeSpan NextSalaryTime = TimeSpan.Zero;
[DataField]
public TimeSpan SalaryFrequency = TimeSpan.FromMinutes(18);
[DataField]
public EntProtoId SalaryProto = "CP14SalarySpawner";
}

View File

@@ -1,5 +1,6 @@
using Content.Server.GameTicking;
using Content.Shared.CCVar;
using Robust.Server;
using Robust.Shared.Audio;
using Robust.Shared.Console;
@@ -9,9 +10,10 @@ public sealed partial class CP14RoundEndSystem
{
[Dependency] private readonly IConsoleHost _consoleHost = default!;
[Dependency] private readonly GameTicker _ticker = default!;
[Dependency] private readonly IBaseServer _baseServer = default!;
private TimeSpan _nextUpdateTime = TimeSpan.Zero;
private readonly TimeSpan _updateFrequency = TimeSpan.FromSeconds(60f);
private readonly TimeSpan _updateFrequency = TimeSpan.FromSeconds(50f);
private bool _enabled;
@@ -24,7 +26,8 @@ public sealed partial class CP14RoundEndSystem
}
// Вы можете сказать: Эд, ты ебанулся? Это же лютый щиткод!
// И я вам отвечу: Да. Но сама система ограничения времени работы сервера - временная штука на этап разработки, которая будет удалена. Мне просто лень каждый раз запускать и выключать сервер ручками.
// И я вам отвечу: Да. Но сама система ограничения времени работы сервера - временная штука на этап разработки, которая будет удалена.
// Мне просто лень каждый раз запускать и выключать сервер ручками.
private void UpdateCbt(float _)
{
if (!_enabled || _timing.CurTime < _nextUpdateTime)
@@ -33,16 +36,16 @@ public sealed partial class CP14RoundEndSystem
_nextUpdateTime = _timing.CurTime + _updateFrequency;
var now = DateTime.UtcNow.AddHours(3); // Moscow time
OpenWeekendRule(now);
EnglishDayRule(now);
OpenSaturdayRule(now);
LanguageRule(now);
LimitPlaytimeRule(now);
ApplyAnnouncements(now);
}
private void OpenWeekendRule(DateTime now)
private void OpenSaturdayRule(DateTime now)
{
var curWhitelist = _cfg.GetCVar(CCVars.WhitelistEnabled);
var isOpenWeened = now.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday;
var isOpenWeened = now.DayOfWeek is DayOfWeek.Saturday;
if (isOpenWeened && curWhitelist)
{
@@ -54,22 +57,14 @@ public sealed partial class CP14RoundEndSystem
}
}
private void EnglishDayRule(DateTime now)
private void LanguageRule(DateTime now)
{
var curLang = _cfg.GetCVar(CCVars.Language);
var englishDay = now.DayOfWeek == DayOfWeek.Saturday;
if (englishDay && curLang != "en-US")
{
_cfg.SetCVar(CCVars.Language, "en-US");
var ruHalfDay = now.Hour < 19 && now.Hour >= 9;
_chatSystem.DispatchGlobalAnnouncement(
"WARNING: The server changes its language to English. For the changes to apply to your device, reconnect to the server.",
announcementSound: new SoundPathSpecifier("/Audio/Effects/beep1.ogg"),
sender: "Server"
);
}
else if (!englishDay && curLang != "ru-RU")
if (ruHalfDay && curLang != "ru-RU")
{
_cfg.SetCVar(CCVars.Language, "ru-RU");
@@ -79,11 +74,21 @@ public sealed partial class CP14RoundEndSystem
sender: "Server"
);
}
else if (!ruHalfDay && curLang != "en-US")
{
_cfg.SetCVar(CCVars.Language, "en-US");
_chatSystem.DispatchGlobalAnnouncement(
"WARNING: The server changes its language to English. For the changes to apply to your device, reconnect to the server.",
announcementSound: new SoundPathSpecifier("/Audio/Effects/beep1.ogg"),
sender: "Server"
);
}
}
private void LimitPlaytimeRule(DateTime now)
{
var playtime = now.Hour is >= 18 and < 21;
var playtime = (now.Hour is >= 15 and < 19) || (now.Hour is >= 20 and < 24);
if (playtime)
{
@@ -104,7 +109,7 @@ public sealed partial class CP14RoundEndSystem
{
var timeMap = new (int Hour, int Minute, Action Action)[]
{
(20, 45, () =>
(18, 45, () =>
{
_chatSystem.DispatchGlobalAnnouncement(
Loc.GetString("cp14-cbt-close-15m"),
@@ -112,7 +117,19 @@ public sealed partial class CP14RoundEndSystem
sender: "Server"
);
}),
(21, 2, () =>
(19, 2, () =>
{
_baseServer.Shutdown("Русский ОБТ подошел к концу. Следующие 3 часа будет английский ОБТ. Просьба не мешать англоязычным ребятам играть в свое время :)");
}),
(23, 45, () =>
{
_chatSystem.DispatchGlobalAnnouncement(
Loc.GetString("cp14-cbt-close-15m"),
announcementSound: new SoundPathSpecifier("/Audio/Effects/beep1.ogg"),
sender: "Server"
);
}),
(00, 2, () =>
{
_consoleHost.ExecuteCommand("golobby");
}),

View File

@@ -51,7 +51,7 @@ public sealed partial class CP14RoundEndSystem : EntitySystem
_demiplane.DeleteAllDemiplanes(safe: false);
_chatSystem.DispatchGlobalAnnouncement(Loc.GetString("cp14-round-end"),
announcementSound: new SoundPathSpecifier("/Audio/_CP14/Ambience/event_boom.ogg"));
announcementSound: new SoundPathSpecifier("/Audio/_CP14/Announce/event_boom.ogg"));
_roundEnd.EndRound();
}
@@ -63,7 +63,7 @@ public sealed partial class CP14RoundEndSystem : EntitySystem
{
_chatSystem.DispatchGlobalAnnouncement(
Loc.GetString("cp14-round-end-monolith-50"),
announcementSound: new SoundPathSpecifier("/Audio/_CP14/Ambience/event_boom.ogg"));
announcementSound: new SoundPathSpecifier("/Audio/_CP14/Announce/event_boom.ogg"));
}
//We initiate round end timer
@@ -99,7 +99,7 @@ public sealed partial class CP14RoundEndSystem : EntitySystem
"cp14-round-end-monolith-discharged",
("time", time),
("units", Loc.GetString(unitsLocString))),
announcementSound: new SoundPathSpecifier("/Audio/_CP14/Ambience/event_boom.ogg"));
announcementSound: new SoundPathSpecifier("/Audio/_CP14/Announce/event_boom.ogg"));
}
private void CancelRoundEndTimer()
@@ -107,6 +107,6 @@ public sealed partial class CP14RoundEndSystem : EntitySystem
_roundEndMoment = TimeSpan.Zero;
_chatSystem.DispatchGlobalAnnouncement(Loc.GetString("cp14-round-end-monolith-recharged"),
announcementSound: new SoundPathSpecifier("/Audio/_CP14/Ambience/event_boom.ogg"));
announcementSound: new SoundPathSpecifier("/Audio/_CP14/Announce/event_boom.ogg"));
}
}

View File

@@ -94,7 +94,7 @@ public sealed class CP14RoundLeaveSystem : EntitySystem
_adminLog.Add(LogType.Action,
LogImpact.High,
$"{ToPrettyString(ent):player} was leave the round by ghosting into mist");
$"{ToPrettyString(ent):player} left the round by ghosting into mist");
LeaveRound(ent, userId.Value, args.Mind);
}

View File

@@ -1,8 +0,0 @@
namespace Content.Server._CP14.RoundRemoveShuttle;
[RegisterComponent]
public sealed partial class CP14RoundRemoveShuttleComponent : Component
{
[DataField]
public EntityUid Station;
}

View File

@@ -1,101 +0,0 @@
using System.Globalization;
using System.Linq;
using Content.Server.Chat.Systems;
using Content.Server.Mind;
using Content.Server.Shuttles.Events;
using Content.Server.Station.Systems;
using Content.Server.StationRecords;
using Content.Server.StationRecords.Systems;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Mind.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.StationRecords;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.RoundRemoveShuttle;
public sealed partial class CP14RoundRemoveShuttleSystem : EntitySystem
{
[Dependency] private readonly ISharedAdminLogManager _adminLog = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly StationJobsSystem _stationJobs = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly StationRecordsSystem _stationRecords = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
private readonly HashSet<Entity<MindContainerComponent>> _mindSet = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14RoundRemoveShuttleComponent, FTLStartedEvent>(OnFTL);
}
private void OnFTL(Entity<CP14RoundRemoveShuttleComponent> ent, ref FTLStartedEvent args)
{
_mindSet.Clear();
_lookup.GetChildEntities(ent, _mindSet);
if (!TryComp<StationRecordsComponent>(ent.Comp.Station, out var stationRecords))
return;
HashSet<EntityUid> toDelete = new();
var query = EntityQueryEnumerator<MindContainerComponent>();
while (query.MoveNext(out var uid, out var mindContainer))
{
if (Transform(uid).GridUid != ent)
continue;
if (!_mind.TryGetMind(uid, out _, out var mindComp, container: mindContainer))
continue;
var name = Name(uid);
var recordId = _stationRecords.GetRecordByName(ent.Comp.Station, name);
if (recordId is null)
return;
var key = new StationRecordKey(recordId.Value, ent.Comp.Station);
if (!_stationRecords.TryGetRecord<GeneralStationRecord>(key, out var entry, stationRecords))
return;
_stationRecords.RemoveRecord(key, stationRecords);
//Trying return all jobs roles
var userId = mindComp.UserId;
string? jobName = entry.JobTitle;
if (userId is not null)
{
_stationJobs.TryAdjustJobSlot(ent.Comp.Station, entry.JobPrototype, 1, clamp: true);
if (_proto.TryIndex(entry.JobPrototype, out var indexedJob))
{
jobName = Loc.GetString(indexedJob.Name);
}
}
_adminLog.Add(LogType.Action,
LogImpact.High,
$"{ToPrettyString(uid):player} was leave the round on traveling merchant ship");
_chatSystem.DispatchStationAnnouncement(ent.Comp.Station,
Loc.GetString(
_mobState.IsDead(uid) ? "cp14-earlyleave-ship-announcement-dead" : "cp14-earlyleave-ship-announcement",
("character", mindComp.CharacterName ?? "Unknown"),
("job", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(jobName ?? "Unknown"))
),
Loc.GetString("cp14-ship-sender"),
playDefaultSound: false
);
toDelete.Add(uid);
}
while (toDelete.Count > 0)
{
var r = toDelete.First();
toDelete.Remove(r);
QueueDel(r);
}
}
}

View File

@@ -1,71 +0,0 @@
using System.Text;
using Content.Server.GameTicking;
using Content.Shared._CP14.RoundStatistic;
using Content.Shared.GameTicking;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.RoundStatistic;
public sealed partial class CP14RoundStatTrackerSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
private readonly Dictionary<ProtoId<CP14RoundStatTrackerPrototype>, int> _tracking = new();
public override void Initialize()
{
base.Initialize();
InitializeDemiplaneDeath();
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundReset);
SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRoundEndTextAppend);
ClearStatistic();
}
private void OnRoundReset(RoundRestartCleanupEvent ev)
{
ClearStatistic();
}
private void OnRoundEndTextAppend(RoundEndTextAppendEvent ev)
{
//TODO: Move to separate UI Text block
var sb = new StringBuilder();
sb.Append($"[head=3]{Loc.GetString("cp14-tracker-header")}[/head] \n");
foreach (var pair in _tracking)
{
if (!_proto.TryIndex(pair.Key, out var indexedTracker))
continue;
sb.Append($"- {Loc.GetString(indexedTracker.Text)}: {pair.Value}\n");
}
ev.AddLine(sb.ToString());
}
private void ClearStatistic()
{
_tracking.Clear();
foreach (var statTracker in _proto.EnumeratePrototypes<CP14RoundStatTrackerPrototype>())
{
_tracking.Add(statTracker.ID, 0);
}
}
public void TrackAdd(ProtoId<CP14RoundStatTrackerPrototype> proto, int dif)
{
_tracking[proto] += Math.Max(dif, 0);
}
public int? GetTrack(ProtoId<CP14RoundStatTrackerPrototype> proto)
{
if (!_tracking.TryGetValue(proto, out var stat))
{
Log.Error($"Failed to get round statistic: {proto}");
return null;
}
return stat;
}
}

View File

@@ -1,14 +0,0 @@
using Content.Shared._CP14.RoundStatistic;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.RoundStatistic.DemiplaneDeath;
/// <summary>
/// Tracks the destruction or full-blown death of this entity.
/// </summary>
[RegisterComponent]
public sealed partial class CP14DeathDemiplaneStatisticComponent : Component
{
[DataField]
public ProtoId<CP14RoundStatTrackerPrototype> Statistic = "DemiplaneDeaths";
}

View File

@@ -1,35 +0,0 @@
using Content.Server._CP14.Demiplane;
using Content.Server._CP14.RoundStatistic.DemiplaneDeath;
using Content.Shared._CP14.Demiplane.Components;
using Content.Shared.GameTicking;
namespace Content.Server._CP14.RoundStatistic;
public sealed partial class CP14RoundStatTrackerSystem
{
private void InitializeDemiplaneDeath()
{
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnSpawnComplete);
SubscribeLocalEvent<CP14DeathDemiplaneStatisticComponent, EntityTerminatingEvent>(OnEntityTerminated);
SubscribeLocalEvent<CP14DeathDemiplaneStatisticComponent, CP14DemiplaneUnsafeExit>(OnDemiplaneUnsafeExit);
}
private void OnSpawnComplete(PlayerSpawnCompleteEvent ev)
{
EnsureComp<CP14DeathDemiplaneStatisticComponent>(ev.Mob);
}
private void OnDemiplaneUnsafeExit(Entity<CP14DeathDemiplaneStatisticComponent> ent, ref CP14DemiplaneUnsafeExit args)
{
TrackAdd(ent.Comp.Statistic, 1);
}
//For round remove variants, like gibs or chasm falls
private void OnEntityTerminated(Entity<CP14DeathDemiplaneStatisticComponent> ent, ref EntityTerminatingEvent args)
{
if (!HasComp<CP14DemiplaneComponent>(Transform(ent).MapUid))
return;
TrackAdd(ent.Comp.Statistic, 1);
}
}

View File

@@ -1,20 +0,0 @@
using Content.Shared.Roles;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.StealArea;
/// <summary>
///
/// </summary>
[RegisterComponent, AutoGenerateComponentPause]
public sealed partial class CP14StealAreaAutoJobConnectComponent : Component
{
[DataField]
public HashSet<ProtoId<JobPrototype>> Jobs = new();
[DataField]
public HashSet<ProtoId<DepartmentPrototype>> Departments = new();
[DataField]
public bool Stations = true;
}

View File

@@ -1,104 +0,0 @@
using Content.Server._CP14.StationCommonObjectives;
using Content.Server.Mind;
using Content.Server.Objectives.Components;
using Content.Shared.GameTicking;
using Content.Shared.Roles.Jobs;
using Robust.Server.Player;
using Robust.Shared.Prototypes;
namespace Content.Server._CP14.StealArea;
public sealed class CP14StealAreaAutoJobConnectSystem : EntitySystem
{
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly SharedJobSystem _job = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14StealAreaAutoJobConnectComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnPlayerSpawning);
}
private void OnMapInit(Entity<CP14StealAreaAutoJobConnectComponent> autoConnect, ref MapInitEvent args)
{
if (!TryComp<StealAreaComponent>(autoConnect, out var stealArea))
return;
if (autoConnect.Comp.Stations)
{
var query = EntityQueryEnumerator<CP14StationCommonObjectivesComponent>();
while (query.MoveNext(out var uid, out _))
{
stealArea.Owners.Add(uid);
}
}
foreach (var player in _playerManager.Sessions)
{
if (!_mind.TryGetMind(player.UserId, out var playerMind))
continue;
if (!_job.MindTryGetJob(playerMind, out var playerJob))
continue;
if (stealArea.Owners.Contains(playerMind.Value))
continue;
if (autoConnect.Comp.Jobs.Contains(playerJob))
{
stealArea.Owners.Add(playerMind.Value);
break;
}
foreach (var depObj in autoConnect.Comp.Departments)
{
if (!_proto.TryIndex(depObj, out var indexedDepart))
continue;
if (!indexedDepart.Roles.Contains(playerJob))
continue;
stealArea.Owners.Add(playerMind.Value);
break;
}
}
}
private void OnPlayerSpawning(PlayerSpawnCompleteEvent ev)
{
if (!_mind.TryGetMind(ev.Player.UserId, out var mind))
return;
var query = EntityQueryEnumerator<StealAreaComponent, CP14StealAreaAutoJobConnectComponent>();
while (query.MoveNext(out var uid, out var stealArea, out var autoConnect))
{
if (stealArea.Owners.Contains(mind.Value))
continue;
if (ev.JobId is null)
continue;
if (autoConnect.Jobs.Contains(ev.JobId))
{
stealArea.Owners.Add(mind.Value);
break;
}
foreach (var depObj in autoConnect.Departments)
{
if (!_proto.TryIndex(depObj, out var indexedDepart))
continue;
if (!indexedDepart.Roles.Contains(ev.JobId))
continue;
stealArea.Owners.Add(mind.Value);
break;
}
}
}
}

View File

@@ -24,7 +24,7 @@ public sealed partial class CP14WorkbenchComponent : Component
public float CraftSpeed = 1f;
[DataField]
public float WorkbenchRadius = 0.5f;
public float WorkbenchRadius = 1.5f;
/// <summary>
/// List of recipes available for crafting on this type of workbench

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

@@ -1,3 +1,4 @@
using Content.Shared._CP14.DayCycle;
using Content.Shared.Light.Components;
using Robust.Shared.Map.Components;
@@ -14,6 +15,10 @@ public abstract class SharedLightCycleSystem : EntitySystem
protected virtual void OnCycleMapInit(Entity<LightCycleComponent> ent, ref MapInitEvent args)
{
//CP14DayCycleSystem
EnsureComp<CP14DayCycleComponent>(ent);
//CP14DayCycleSystem end
if (TryComp(ent.Owner, out MapLightComponent? mapLight))
{
ent.Comp.OriginalColor = mapLight.AmbientLightColor;

View File

@@ -48,14 +48,8 @@ public sealed partial class LoadoutPrototype : IPrototype, IEquipmentLoadout
public Dictionary<string, List<EntProtoId>> Storage { get; set; } = new();
/// <summary>
/// CP14 - it is possible to give action spells or spells to players who have taken this loadout
/// CP14 - it is possible to give free skills to players
/// </summary>
[DataField]
public List<EntProtoId> Actions { get; set; } = new();
/// <summary>
/// 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

@@ -97,14 +97,9 @@ public abstract class SharedStationSpawningSystem : EntitySystem
private void CP14EquipStartingActions(EntityUid entity, LoadoutPrototype loadout)
{
foreach (var action in loadout.Actions)
foreach (var skill in loadout.Skills)
{
_action.AddAction(entity, action);
}
foreach (var tree in loadout.SkillTree)
{
_skill.TryAddExperience(entity, tree.Key, tree.Value);
_skill.TryAddSkill(entity, skill, free: true);
}
}

View File

@@ -0,0 +1,27 @@
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.BloodMoon;
[RegisterComponent, NetworkedComponent]
public sealed partial class CP14BloodMoonCurseComponent : Component
{
[DataField]
public EntityUid? CurseRule;
[DataField]
public EntProtoId CurseEffect = "CP14BloodMoonCurseEffect";
[DataField]
public EntityUid? SpawnedEffect;
[DataField]
public TimeSpan EndStunDuration = TimeSpan.FromSeconds(60f);
[DataField]
public EntProtoId Action = "CP14ActionSpellBloodlust";
[DataField]
public EntityUid? ActionEntity;
}

View File

@@ -0,0 +1,11 @@
namespace Content.Shared._CP14.DayCycle;
[RegisterComponent]
public sealed partial class CP14DayCycleComponent : Component
{
public float LastLightLevel = 0f;
[DataField]
public float Threshold = 0.6f;
}

View File

@@ -0,0 +1,130 @@
using Content.Shared.GameTicking;
using Content.Shared.Light.Components;
using Content.Shared.Storage.Components;
using Content.Shared.Weather;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Timing;
namespace Content.Shared._CP14.DayCycle;
/// <summary>
/// This is an add-on to the LightCycle system that helps you determine what time of day it is on the map
/// </summary>
public sealed class CP14DayCycleSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly SharedGameTicker _ticker = default!;
[Dependency] private readonly SharedMapSystem _maps = default!;
[Dependency] private readonly SharedWeatherSystem _weather = default!;
private EntityQuery<MapGridComponent> _mapGridQuery;
private EntityQuery<InsideEntityStorageComponent> _storageQuery;
public override void Initialize()
{
base.Initialize();
_mapGridQuery = GetEntityQuery<MapGridComponent>();
_storageQuery = GetEntityQuery<InsideEntityStorageComponent>();
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<LightCycleComponent, CP14DayCycleComponent, MapComponent>();
while (query.MoveNext(out var uid, out var lightCycle, out var dayCycle, out var map))
{
var oldLightLevel = dayCycle.LastLightLevel;
var newLightLevel = GetLightLevel((uid, lightCycle));
// Going into darkness
if (oldLightLevel > newLightLevel)
{
if (oldLightLevel > dayCycle.Threshold)
{
if (newLightLevel < dayCycle.Threshold)
{
var ev = new CP14StartNightEvent(uid);
RaiseLocalEvent(ev);
}
}
}
// Going into light
if (oldLightLevel < newLightLevel)
{
if (oldLightLevel < dayCycle.Threshold)
{
if (newLightLevel > dayCycle.Threshold)
{
var ev = new CP14StartDayEvent(uid);
RaiseLocalEvent(ev);
}
}
}
dayCycle.LastLightLevel = newLightLevel;
}
}
public float GetLightLevel(Entity<LightCycleComponent?> map)
{
if (!Resolve(map.Owner, ref map.Comp, false))
return 0;
var time = (float)_timing.CurTime
.Add(map.Comp.Offset)
.Subtract(_ticker.RoundStartTimeSpan)
.Subtract(_metaData.GetPauseTime(map))
.TotalSeconds;
var normalizedTime = time % map.Comp.Duration.TotalSeconds;
var lightLevel = Math.Sin((normalizedTime / map.Comp.Duration.TotalSeconds) * MathF.PI);
return (float)lightLevel;
}
/// <summary>
/// Checks to see if the specified entity is on the map where it's daytime.
/// </summary>
/// <param name="target">An entity being tested to see if it is in daylight</param>
public bool UnderSunlight(EntityUid target)
{
if (_storageQuery.HasComp(target))
return false;
var xform = Transform(target);
if (xform.MapUid is null || xform.GridUid is null)
return false;
var day = GetLightLevel(xform.MapUid.Value) > 0.5f;
var grid = xform.GridUid;
if (grid is null)
return day;
if (!_mapGridQuery.TryComp(grid, out var gridComp))
return day;
if (!_weather.CanWeatherAffect(grid.Value,
gridComp,
_maps.GetTileRef(xform.GridUid.Value, gridComp, xform.Coordinates)))
return false;
return day;
}
}
public sealed class CP14StartNightEvent(EntityUid map) : EntityEventArgs
{
public EntityUid Map = map;
}
public sealed class CP14StartDayEvent(EntityUid map) : EntityEventArgs
{
public EntityUid Map = map;
}

View File

@@ -1,18 +0,0 @@
using Content.Shared.DoAfter;
using Robust.Shared.Map;
using Robust.Shared.Serialization;
namespace Content.Shared._CP14.Decals;
[Serializable, NetSerializable]
public sealed partial class CP14DecalCleanerDoAfterEvent : DoAfterEvent
{
public NetCoordinates ClickLocation;
public CP14DecalCleanerDoAfterEvent(NetCoordinates click)
{
ClickLocation = click;
}
public override DoAfterEvent Clone() => this;
}

View File

@@ -29,5 +29,5 @@ public sealed partial class CP14SpecialDemiplanePrototype : IPrototype
/// Modifiers that will be automatically added to the demiplane when it is generated.
/// </summary>
[DataField]
public List<ProtoId<CP14DemiplaneModifierPrototype>>? Modifiers = new();
public List<ProtoId<CP14DemiplaneModifierPrototype>> Modifiers = new();
}

View File

@@ -1,16 +0,0 @@
namespace Content.Shared._CP14.MagicAttuning;
/// <summary>
/// Reflects the fact that this subject can be focused on (Magical attune as a mechanic from DnD.)
/// </summary>
[RegisterComponent, Access(typeof(CP14SharedMagicAttuningSystem))]
public sealed partial class CP14MagicAttuningItemComponent : Component
{
/// <summary>
/// how long it takes to focus on that object
/// </summary>
[DataField]
public TimeSpan FocusTime = TimeSpan.FromSeconds(5f);
public Entity<CP14MagicAttuningMindComponent>? Link = null;
}

View File

@@ -1,25 +0,0 @@
using Robust.Shared.GameStates;
namespace Content.Shared._CP14.MagicAttuning;
/// <summary>
/// A mind that can focus on objects
/// </summary>
[RegisterComponent, NetworkedComponent]
[Access(typeof(CP14SharedMagicAttuningSystem))]
public sealed partial class CP14MagicAttuningMindComponent : Component
{
[DataField]
public int MaxAttuning = 3;
/// <summary>
/// The entities that this being is focused on
/// </summary>
[DataField]
public List<EntityUid> AttunedTo = new();
/// <summary>
/// cheat: if added to an entity with MindContainer, automatically copied to the mind, removing it from the body. This is to make it easy to add the component to prototype creatures.
/// </summary>
[DataField]
public bool AutoCopyToMind = false;
}

View File

@@ -1,238 +0,0 @@
using Content.Shared.DoAfter;
using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using Robust.Shared.Serialization;
namespace Content.Shared._CP14.MagicAttuning;
/// <summary>
/// This system controls the customization to magic items by the players.
/// </summary>
public sealed partial class CP14SharedMagicAttuningSystem : EntitySystem
{
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14MagicAttuningItemComponent, GetVerbsEvent<InteractionVerb>>(OnInteractionVerb);
SubscribeLocalEvent<CP14MagicAttuningMindComponent, CP14MagicAttuneDoAfterEvent>(OnAttuneDoAfter);
SubscribeLocalEvent<CP14MagicAttuningMindComponent, MindAddedMessage>(OnMindAdded);
}
private void OnMindAdded(Entity<CP14MagicAttuningMindComponent> ent, ref MindAddedMessage args)
{
if (!ent.Comp.AutoCopyToMind)
return;
if (HasComp<MindComponent>(ent))
return;
if (!_mind.TryGetMind(ent, out var mindId, out var mind))
return;
if (!HasComp<CP14MagicAttuningMindComponent>(mindId))
{
var attuneMind = AddComp<CP14MagicAttuningMindComponent>(mindId);
attuneMind.MaxAttuning = ent.Comp.MaxAttuning;
}
}
public bool IsAttunedTo(EntityUid mind, EntityUid item)
{
if (!TryComp<CP14MagicAttuningItemComponent>(item, out var attuningItem))
return false;
if (!TryComp<CP14MagicAttuningMindComponent>(mind, out var attuningMind))
return false;
return attuningMind.AttunedTo.Contains(item);
}
private void OnInteractionVerb(Entity<CP14MagicAttuningItemComponent> attuningItem, ref GetVerbsEvent<InteractionVerb> args)
{
if (!args.CanAccess || !args.CanInteract)
return;
if (!_mind.TryGetMind(args.User, out var mindId, out var mind))
return;
if (!TryComp<CP14MagicAttuningMindComponent>(mindId, out var attumingMind))
return;
var user = args.User;
if (attumingMind.AttunedTo.Contains(args.Target))
{
args.Verbs.Add(new()
{
Act = () =>
{
RemoveAttune((mindId, attumingMind), attuningItem);
},
Text = Loc.GetString("cp14-magic-deattuning-verb-text"),
Message = Loc.GetString("cp14-magic-attuning-verb-message"),
});
}
else
{
args.Verbs.Add(new()
{
Act = () =>
{
TryStartAttune(user, attuningItem);
},
Text = Loc.GetString("cp14-magic-attuning-verb-text"),
Message = Loc.GetString("cp14-magic-attuning-verb-message"),
});
}
}
public bool TryStartAttune(EntityUid user, Entity<CP14MagicAttuningItemComponent> item)
{
if (!_mind.TryGetMind(user, out var mindId, out var mind))
return false;
if (!TryComp<CP14MagicAttuningMindComponent>(mindId, out var attuningMind))
return false;
if (attuningMind.MaxAttuning <= 0)
return false;
//if there's an overabundance of ties, we report that the oldest one is torn.
if (attuningMind.AttunedTo.Count >= attuningMind.MaxAttuning)
{
var oldestAttune = attuningMind.AttunedTo[0];
_popup.PopupEntity(Loc.GetString("cp14-magic-attune-oldest-forgot", ("item", MetaData(oldestAttune).EntityName)), user, user);
}
//we notify the current owner of the item that someone is cutting ties.
if (item.Comp.Link is not null &&
item.Comp.Link.Value.Owner != mindId &&
TryComp<MindComponent>(item.Comp.Link.Value.Owner, out var ownerMind) &&
ownerMind.OwnedEntity is not null)
{
_popup.PopupEntity(Loc.GetString("cp14-magic-attune-oldest-forgot", ("item", MetaData(item).EntityName)), ownerMind.OwnedEntity.Value, ownerMind.OwnedEntity.Value);
}
var doAfterArgs = new DoAfterArgs(EntityManager,
user,
item.Comp.FocusTime,
new CP14MagicAttuneDoAfterEvent(),
mindId,
item)
{
BreakOnDamage = true,
BreakOnMove = true,
DistanceThreshold = 2f,
BlockDuplicate = true,
};
_doAfter.TryStartDoAfter(doAfterArgs);
return true;
}
private void OnAttuneDoAfter(Entity<CP14MagicAttuningMindComponent> ent, ref CP14MagicAttuneDoAfterEvent args)
{
if (args.Cancelled || args.Handled || args.Target is null)
return;
if (ent.Comp.AttunedTo.Count >= ent.Comp.MaxAttuning)
{
var oldestAttune = ent.Comp.AttunedTo[0];
RemoveAttune(ent, oldestAttune);
}
AddAttune(ent, args.Target.Value);
}
private void RemoveAttune(Entity<CP14MagicAttuningMindComponent> attuningMind, EntityUid item)
{
if (!attuningMind.Comp.AttunedTo.Contains(item))
return;
attuningMind.Comp.AttunedTo.Remove(item);
if (!TryComp<CP14MagicAttuningItemComponent>(item, out var attuningItem))
return;
if (!TryComp<MindComponent>(attuningMind, out var mind))
return;
attuningItem.Link = null;
var ev = new RemovedAttuneFromMindEvent(attuningMind, mind.OwnedEntity, item);
RaiseLocalEvent(attuningMind, ev);
RaiseLocalEvent(item, ev);
if (mind.OwnedEntity is not null)
{
_popup.PopupEntity(Loc.GetString("cp14-magic-attune-oldest-forgot-end", ("item", MetaData(item).EntityName)), mind.OwnedEntity.Value, mind.OwnedEntity.Value);
}
}
private void AddAttune(Entity<CP14MagicAttuningMindComponent> attuningMind, EntityUid item)
{
if (attuningMind.Comp.AttunedTo.Contains(item))
return;
if (!TryComp<CP14MagicAttuningItemComponent>(item, out var attuningItem))
return;
if (!TryComp<MindComponent>(attuningMind, out var mind))
return;
if (attuningItem.Link is not null)
RemoveAttune(attuningItem.Link.Value, item);
attuningMind.Comp.AttunedTo.Add(item);
attuningItem.Link = attuningMind;
var ev = new AddedAttuneToMindEvent(attuningMind, mind.OwnedEntity, item);
RaiseLocalEvent(attuningMind, ev);
RaiseLocalEvent(item, ev);
}
}
[Serializable, NetSerializable]
public sealed partial class CP14MagicAttuneDoAfterEvent : SimpleDoAfterEvent
{
}
/// <summary>
/// is evoked on both the item and the mind when a new connection between them appears.
/// </summary>
public sealed class AddedAttuneToMindEvent : EntityEventArgs
{
public readonly EntityUid Mind;
public readonly EntityUid? User;
public readonly EntityUid Item;
public AddedAttuneToMindEvent(EntityUid mind, EntityUid? user, EntityUid item)
{
Mind = mind;
User = user;
Item = item;
}
}
/// <summary>
/// is evoked on both the item and the mind when the connection is broken
/// </summary>
public sealed class RemovedAttuneFromMindEvent : EntityEventArgs
{
public readonly EntityUid Mind;
public readonly EntityUid? User;
public readonly EntityUid Item;
public RemovedAttuneFromMindEvent(EntityUid mind, EntityUid? user, EntityUid item)
{
Mind = mind;
User = user;
Item = item;
}
}

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,52 @@
using Content.Shared.Whitelist;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.MagicSpell.Spells;
public sealed partial class CP14SpellPointer : CP14SpellEffect
{
[DataField(required: true)]
public EntProtoId PointerEntity;
[DataField(required: true)]
public EntityWhitelist? Whitelist;
[DataField]
public EntityWhitelist? Blacklist = null;
[DataField(required: true)]
public float SearchRange = 30f;
public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args)
{
if (args.User is null)
return;
var lookup = entManager.System<EntityLookupSystem>();
var whitelistSys = entManager.System<EntityWhitelistSystem>();
var transform = entManager.System<SharedTransformSystem>();
var originPosition = transform.GetWorldPosition(args.User.Value);
var originEntPosition = transform.GetMoverCoordinates(args.User.Value);
var entitiesInRange = lookup.GetEntitiesInRange<TransformComponent>(originEntPosition, SearchRange);
foreach (var ent in entitiesInRange)
{
if (ent.Owner == args.User.Value)
continue;
if (!whitelistSys.CheckBoth(ent, Blacklist, Whitelist))
continue;
var targetPosition = transform.GetWorldPosition(ent.Comp);
//Calculate the rotation
Angle angle = new(targetPosition - originPosition);
var pointer = entManager.Spawn(PointerEntity, new MapCoordinates(originPosition, transform.GetMapId(originEntPosition)));
transform.SetWorldRotation(pointer, angle + Angle.FromDegrees(90));
}
}
}

View File

@@ -0,0 +1,47 @@
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.MagicSpell.Spells;
public sealed partial class CP14SpellPointerToAlive : CP14SpellEffect
{
[DataField(required: true)]
public EntProtoId PointerEntity;
[DataField(required: true)]
public float SearchRange = 30f;
public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args)
{
if (args.User is null)
return;
var lookup = entManager.System<EntityLookupSystem>();
var mobStateSys = entManager.System<MobStateSystem>();
var transform = entManager.System<SharedTransformSystem>();
var originPosition = transform.GetWorldPosition(args.User.Value);
var originEntPosition = transform.GetMoverCoordinates(args.User.Value);
var entitiesInRange = lookup.GetEntitiesInRange<MobStateComponent>(originEntPosition, SearchRange);
foreach (var ent in entitiesInRange)
{
if (ent.Owner == args.User.Value)
continue;
if (mobStateSys.IsDead(ent))
continue;
var targetPosition = transform.GetWorldPosition(ent);
//Calculate the rotation
Angle angle = new(targetPosition - originPosition);
var pointer = entManager.Spawn(PointerEntity, new MapCoordinates(originPosition, transform.GetMapId(originEntPosition)));
transform.SetWorldRotation(pointer, angle + Angle.FromDegrees(90));
}
}
}

View File

@@ -1,7 +1,5 @@
using Content.Shared._CP14.MagicAttuning;
using Content.Shared._CP14.MagicSpellStorage.Components;
using Content.Shared.Clothing;
using Content.Shared.Clothing.Components;
using Content.Shared.Hands;
namespace Content.Shared._CP14.MagicSpellStorage;
@@ -11,9 +9,7 @@ public sealed partial class CP14SpellStorageSystem
private void InitializeAccess()
{
SubscribeLocalEvent<CP14SpellStorageAccessHoldingComponent, GotEquippedHandEvent>(OnEquippedHand);
SubscribeLocalEvent<CP14SpellStorageAccessHoldingComponent, AddedAttuneToMindEvent>(OnHandAddedAttune);
SubscribeLocalEvent<CP14SpellStorageAccessWearingComponent, AddedAttuneToMindEvent>(OnClothingAddedAttune);
SubscribeLocalEvent<CP14SpellStorageAccessWearingComponent, ClothingGotEquippedEvent>(OnClothingEquipped);
SubscribeLocalEvent<CP14SpellStorageAccessWearingComponent, ClothingGotUnequippedEvent>(OnClothingUnequipped);
}
@@ -26,40 +22,6 @@ public sealed partial class CP14SpellStorageSystem
TryGrantAccess((ent, spellStorage), args.User);
}
private void OnHandAddedAttune(Entity<CP14SpellStorageAccessHoldingComponent> ent, ref AddedAttuneToMindEvent args)
{
if (!TryComp<CP14SpellStorageComponent>(ent, out var spellStorage))
return;
if (args.User is null)
return;
if (!_hands.IsHolding(args.User.Value, ent))
return;
TryGrantAccess((ent, spellStorage), args.User.Value);
}
private void OnClothingAddedAttune(Entity<CP14SpellStorageAccessWearingComponent> ent, ref AddedAttuneToMindEvent args)
{
if (!ent.Comp.Wearing)
return;
if (!TryComp<CP14SpellStorageComponent>(ent, out var spellStorage))
return;
if (args.User is null)
return;
if (!TryComp<ClothingComponent>(ent, out var clothing))
return;
if (Transform(ent).ParentUid != args.User)
return;
TryGrantAccess((ent, spellStorage), args.User.Value);
}
private void OnClothingEquipped(Entity<CP14SpellStorageAccessWearingComponent> ent, ref ClothingGotEquippedEvent args)
{
ent.Comp.Wearing = true;

View File

@@ -1,4 +1,3 @@
using Content.Shared._CP14.MagicAttuning;
using Content.Shared._CP14.MagicSpell.Components;
using Content.Shared._CP14.MagicSpell.Events;
using Content.Shared._CP14.MagicSpellStorage.Components;
@@ -18,7 +17,6 @@ public sealed partial class CP14SpellStorageSystem : EntitySystem
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly CP14SharedMagicAttuningSystem _attuning = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
@@ -31,7 +29,6 @@ public sealed partial class CP14SpellStorageSystem : EntitySystem
SubscribeLocalEvent<CP14SpellStorageComponent, ComponentShutdown>(OnMagicStorageShutdown);
SubscribeLocalEvent<CP14SpellStorageUseDamageComponent, CP14SpellFromSpellStorageUsedEvent>(OnSpellUsed);
SubscribeLocalEvent<CP14SpellStorageRequireAttuneComponent, RemovedAttuneFromMindEvent>(OnRemovedAttune);
}
private void OnSpellUsed(Entity<CP14SpellStorageUseDamageComponent> ent, ref CP14SpellFromSpellStorageUsedEvent args)
@@ -81,21 +78,7 @@ public sealed partial class CP14SpellStorageSystem : EntitySystem
if (mind.OwnedEntity is null)
return false;
if (TryComp<CP14SpellStorageRequireAttuneComponent>(storage, out var reqAttune))
{
if (!_attuning.IsAttunedTo(mindId, storage))
return false;
}
_actions.GrantActions(user, storage.Comp.SpellEntities, storage);
return true;
}
private void OnRemovedAttune(Entity<CP14SpellStorageRequireAttuneComponent> ent, ref RemovedAttuneFromMindEvent args)
{
if (args.User is null)
return;
_actions.RemoveProvidedActions(args.User.Value, ent);
}
}

View File

@@ -1,9 +0,0 @@
namespace Content.Shared._CP14.MagicSpellStorage.Components;
/// <summary>
/// The ability to access spellcasting is limited by the attuning requirement
/// </summary>
[RegisterComponent, Access(typeof(CP14SpellStorageSystem))]
public sealed partial class CP14SpellStorageRequireAttuneComponent : Component
{
}

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

@@ -1,12 +0,0 @@
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.RoundStatistic;
[Prototype("statisticTracker")]
public sealed partial class CP14RoundStatTrackerPrototype : IPrototype
{
[IdDataField] public string ID { get; } = default!;
[DataField(required: true)]
public LocId Text;
}

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, free: true);
}
}
}

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,39 @@ 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>();
SubscribeLocalEvent<CP14SkillStorageComponent, MapInitEvent>(OnMapInit);
InitializeAdmin();
InitializeChecks();
}
private void OnMapInit(Entity<CP14SkillStorageComponent> ent, ref MapInitEvent args)
{
//If at initialization we have any skill records, we automatically give them to this entity
var free = ent.Comp.FreeLearnedSkills.ToList();
var learned = ent.Comp.LearnedSkills.ToList();
ent.Comp.FreeLearnedSkills.Clear();
ent.Comp.LearnedSkills.Clear();
foreach (var skill in free)
{
TryAddSkill(ent.Owner, skill, ent.Comp, true);
}
foreach (var skill in learned)
{
TryAddSkill(ent.Owner, skill, ent.Comp);
}
}
/// <summary>
@@ -20,7 +49,8 @@ public abstract partial class CP14SharedSkillSystem : EntitySystem
/// </summary>
public bool TryAddSkill(EntityUid target,
ProtoId<CP14SkillPrototype> skill,
CP14SkillStorageComponent? component = null)
CP14SkillStorageComponent? component = null,
bool free = false)
{
if (!Resolve(target, ref component, false))
return false;
@@ -31,15 +61,22 @@ 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;
if (free)
component.FreeLearnedSkills.Add(skill);
else
component.SkillsSumExperience += indexedSkill.LearnCost;
component.LearnedSkills.Add(skill);
Dirty(target, component);
var learnEv = new CP14SkillLearnedEvent(skill, target);
RaiseLocalEvent(target, ref learnEv);
return true;
}
@@ -59,12 +96,13 @@ 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;
if (!component.FreeLearnedSkills.Remove(skill))
component.SkillsSumExperience -= indexedSkill.LearnCost;
Dirty(target, component);
return true;
@@ -83,53 +121,14 @@ 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,
public bool HaveFreeSkill(EntityUid target,
ProtoId<CP14SkillPrototype> skill,
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;
return component.FreeLearnedSkills.Contains(skill);
}
/// <summary>
@@ -158,12 +157,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 +175,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 +198,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 +215,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 +232,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,36 @@ 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
{
/// <summary>
/// Tracks skills that are learned without spending memory points.
/// the skills that are here are DUBLED in the LearnedSkills,
/// </summary>
[DataField, AutoNetworkedField]
public List<ProtoId<CP14SkillPrototype>> FreeLearnedSkills = new();
[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)
@@ -76,12 +75,14 @@ public sealed partial class MaterialResource : CP14WorkbenchCraftRequirement
if (mat.Key != Material)
continue;
if (requiredCount <= 0)
return;
if (stack is null)
{
var value = (int)MathF.Min(requiredCount, mat.Value);
requiredCount -= value;
entManager.DeleteEntity(placedEntity);
continue;
}
else
{

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>();

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