Skill requirements (#1085)

* prerequites refactor

* tiefling only spells

* metamagic update

* sort trying

* goblin speed port
This commit is contained in:
Ed
2025-03-29 16:50:14 +03:00
committed by GitHub
parent 2248e2ad5c
commit fddbb010cf
32 changed files with 378 additions and 89 deletions

View File

@@ -138,11 +138,43 @@ public abstract partial class CP14SharedSkillSystem : EntitySystem
public bool CanLearnSkill(EntityUid target,
ProtoId<CP14SkillPrototype> skill,
CP14SkillStorageComponent? component = null)
{
if (!_proto.TryIndex(skill, out var indexedSkill))
return false;
return CanLearnSkill(target, indexedSkill, component);
}
/// <summary>
/// Checks if the player can learn the specified skill.
/// </summary>
public bool CanLearnSkill(EntityUid target,
CP14SkillPrototype skill,
CP14SkillStorageComponent? component = null)
{
if (!Resolve(target, ref component, false))
return false;
if (!_proto.TryIndex(skill, out var indexedSkill))
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;
}
/// <summary>
/// Is it allowed to learn this skill? The player may not have enough points to learn it, but has already met all the requirements to learn it.
/// </summary>
public bool AllowedToLearn(EntityUid target,
CP14SkillPrototype skill,
CP14SkillStorageComponent? component = null)
{
if (!Resolve(target, ref component, false))
return false;
//Already learned
@@ -150,22 +182,16 @@ public abstract partial class CP14SharedSkillSystem : EntitySystem
return false;
//Check max cap
if (component.SkillsSumExperience + indexedSkill.LearnCost >= component.ExperienceMaxCap)
if (component.SkillsSumExperience + skill.LearnCost >= component.ExperienceMaxCap)
return false;
//Prerequisite check
foreach (var prerequisite in indexedSkill.Prerequisites)
//Restrictions check
foreach (var req in skill.Restrictions)
{
if (!HaveSkill(target, prerequisite, component))
if (!req.Check(EntityManager, target))
return false;
}
//Experience check
if (!component.Progress.TryGetValue(indexedSkill.Tree, out var currentExp))
return false;
if (currentExp < indexedSkill.LearnCost)
return false;
return true;
}

View File

@@ -1,3 +1,4 @@
using System.Linq;
using Content.Shared._CP14.Skill.Components;
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared.Administration;
@@ -11,21 +12,43 @@ public abstract partial class CP14SharedSkillSystem
{
[Dependency] private readonly ISharedAdminManager _admin = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
private IEnumerable<CP14SkillPrototype>? _allSkills;
private void InitializeAdmin()
{
SubscribeLocalEvent<CP14SkillStorageComponent, GetVerbsEvent<Verb>>(OnGetAdminVerbs);
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypeReloaded);
UpdateCachedSkill();
}
private void OnPrototypeReloaded(PrototypesReloadedEventArgs ev)
{
if (!ev.WasModified<CP14SkillPrototype>())
return;
UpdateCachedSkill();
}
private void UpdateCachedSkill()
{
_allSkills = _proto.EnumeratePrototypes<CP14SkillPrototype>().ToList().OrderBy(skill => skill.Tree.Id).ThenBy(skill => skill.Name);
}
private void OnGetAdminVerbs(Entity<CP14SkillStorageComponent> ent, ref GetVerbsEvent<Verb> args)
{
if (!_admin.HasAdminFlag(args.User, AdminFlags.Admin))
return;
if (_allSkills is null)
return;
var target = args.Target;
//Add Skill
foreach (var skill in _proto.EnumeratePrototypes<CP14SkillPrototype>())
foreach (var skill in _allSkills)
{
if (ent.Comp.LearnedSkills.Contains(skill))
continue;

View File

@@ -1,21 +1,21 @@
using Content.Shared.Actions;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill.Specials;
namespace Content.Shared._CP14.Skill.Effects;
public sealed partial class AddAction : CP14SkillEffect
{
[DataField(required: true)]
public EntProtoId Action;
public override void AddSkill(EntityManager entManager, EntityUid target)
public override void AddSkill(IEntityManager entManager, EntityUid target)
{
var actionsSystem = entManager.System<SharedActionsSystem>();
actionsSystem.AddAction(target, Action);
}
public override void RemoveSkill(EntityManager entManager, EntityUid target)
public override void RemoveSkill(IEntityManager entManager, EntityUid target)
{
var actionsSystem = entManager.System<SharedActionsSystem>();
@@ -34,19 +34,13 @@ public sealed partial class AddAction : CP14SkillEffect
}
}
public override string? GetName(EntityManager entMagager, IPrototypeManager protoManager)
public override string? GetName(IEntityManager entMagager, IPrototypeManager protoManager)
{
if (!protoManager.TryIndex(Action, out var indexedAction))
return String.Empty;
return indexedAction.Name;
return !protoManager.TryIndex(Action, out var indexedAction) ? string.Empty : indexedAction.Name;
}
public override string? GetDescription(EntityManager entMagager, IPrototypeManager protoManager)
public override string? GetDescription(IEntityManager entMagager, IPrototypeManager protoManager)
{
if (!protoManager.TryIndex(Action, out var indexedAction))
return String.Empty;
return indexedAction.Description;
return !protoManager.TryIndex(Action, out var indexedAction) ? string.Empty : indexedAction.Description;
}
}

View File

@@ -0,0 +1,17 @@
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill.Effects;
[ImplicitDataDefinitionForInheritors]
[MeansImplicitUse]
public abstract partial class CP14SkillEffect
{
public abstract void AddSkill(IEntityManager entManager, EntityUid target);
public abstract void RemoveSkill(IEntityManager entManager, EntityUid target);
public abstract string? GetName(IEntityManager entMagager, IPrototypeManager protoManager);
public abstract string? GetDescription(IEntityManager entMagager, IPrototypeManager protoManager);
}

View File

@@ -0,0 +1,69 @@
using Content.Shared.Actions;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill.Effects;
public sealed partial class ReplaceAction : CP14SkillEffect
{
[DataField(required: true)]
public EntProtoId OldAction;
[DataField(required: true)]
public EntProtoId NewAction;
public override void AddSkill(IEntityManager entManager, EntityUid target)
{
var actionsSystem = entManager.System<SharedActionsSystem>();
//Remove old action
foreach (var (uid, _) in actionsSystem.GetActions(target))
{
if (!entManager.TryGetComponent<MetaDataComponent>(uid, out var metaData))
continue;
if (metaData.EntityPrototype == null)
continue;
if (metaData.EntityPrototype != OldAction)
continue;
actionsSystem.RemoveAction(target, uid);
}
//Add new one
actionsSystem.AddAction(target, NewAction);
}
public override void RemoveSkill(IEntityManager entManager, EntityUid target)
{
var actionsSystem = entManager.System<SharedActionsSystem>();
//Remove old action
foreach (var (uid, _) in actionsSystem.GetActions(target))
{
if (!entManager.TryGetComponent<MetaDataComponent>(uid, out var metaData))
continue;
if (metaData.EntityPrototype == null)
continue;
if (metaData.EntityPrototype != NewAction)
continue;
actionsSystem.RemoveAction(target, uid);
}
//Add new one
actionsSystem.AddAction(target, OldAction);
}
public override string? GetName(IEntityManager entMagager, IPrototypeManager protoManager)
{
return !protoManager.TryIndex(NewAction, out var indexedAction) ? string.Empty : indexedAction.Name;
}
public override string? GetDescription(IEntityManager entMagager, IPrototypeManager protoManager)
{
return !protoManager.TryIndex(NewAction, out var indexedAction) ? string.Empty : indexedAction.Description;
}
}

View File

@@ -1,5 +1,6 @@
using System.Numerics;
using Content.Shared._CP14.Skill.Specials;
using Content.Shared._CP14.Skill.Effects;
using Content.Shared._CP14.Skill.Restrictions;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
@@ -37,12 +38,6 @@ public sealed partial class CP14SkillPrototype : IPrototype
[DataField]
public float LearnCost = 1f;
/// <summary>
/// Prerequisites for this skill. This is used to determine if the player can learn the skill. If the player does not have all the prerequisites, they cannot learn the skill.
/// </summary>
[DataField]
public HashSet<ProtoId<CP14SkillPrototype>> Prerequisites = new();
/// <summary>
/// Skill UI position. This is used to determine where the skill will be displayed in the skill tree UI.
/// </summary>
@@ -61,4 +56,10 @@ public sealed partial class CP14SkillPrototype : IPrototype
/// </summary>
[DataField]
public CP14SkillEffect? Effect;
/// <summary>
/// Skill restriction. Limiters on learning. Any reason why a player cannot learn this skill.
/// </summary>
[DataField]
public List<CP14SkillRestriction> Restrictions = new();
}

View File

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

View File

@@ -0,0 +1,27 @@
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 NeedPrerequisite : CP14SkillRestriction
{
[DataField(required: true)]
public ProtoId<CP14SkillPrototype> Prerequisite = new();
public override bool Check(IEntityManager entManager, EntityUid target)
{
if (!entManager.TryGetComponent<CP14SkillStorageComponent>(target, out var skillStorage))
return false;
var learned = skillStorage.LearnedSkills;
return learned.Contains(Prerequisite);
}
public override string GetDescription(IEntityManager entManager, IPrototypeManager protoManager)
{
var skillSystem = entManager.System<CP14SharedSkillSystem>();
return Loc.GetString("cp14-skill-req-prerequisite", ("name", skillSystem.GetSkillName(Prerequisite)));
}
}

View File

@@ -0,0 +1,26 @@
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill.Restrictions;
public sealed partial class SpeciesWhitelist : CP14SkillRestriction
{
[DataField(required: true)]
public ProtoId<SpeciesPrototype> Species = new();
public override bool Check(IEntityManager entManager, EntityUid target)
{
if (!entManager.TryGetComponent<HumanoidAppearanceComponent>(target, out var appearance))
return false;
return appearance.Species == Species;
}
public override string GetDescription(IEntityManager entManager, IPrototypeManager protoManager)
{
var species = protoManager.Index(Species);
return Loc.GetString("cp14-skill-req-species", ("name", Loc.GetString(species.Name)));
}
}

View File

@@ -1,17 +0,0 @@
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Shared._CP14.Skill.Specials;
[ImplicitDataDefinitionForInheritors]
[MeansImplicitUse]
public abstract partial class CP14SkillEffect
{
public abstract void AddSkill(EntityManager entManager, EntityUid target);
public abstract void RemoveSkill(EntityManager entManager, EntityUid target);
public abstract string? GetName(EntityManager entMagager, IPrototypeManager protoManager);
public abstract string? GetDescription(EntityManager entMagager, IPrototypeManager protoManager);
}