Action Upgrade System (#22277)
* Adds uses before delay so actions can be used multiple times before cooldown * adds methods to get remaining charges, to set uses before delay, and to set use delay * adds method to change action name * moves set usedelay * action upgrade ECS * adds method to reset remaining uses * adds upgrade events * refactors action upgrade event and adds logic to parse it * fix serialization issue * adds level up draft method * adds action commands and a command to upgrade an action * more warning lines to help * Gets action to upgrade properly * Removes unneeded fields from the action upgrade component and now properly raises the level of the new action * Cleans up dead code and comments * Fixes punctuation in actions-commands and adds a TryUpgradeAction method. * removes TODO comment * robust fix * removes RT * readds RT * update RT to 190 * removes change name method * removes remaining uses & related fields and adds that functionality to charges * Adds Charges to action tooltips that require it
This commit is contained in:
@@ -87,6 +87,8 @@ namespace Content.Client.Actions
|
|||||||
component.Cooldown = state.Cooldown;
|
component.Cooldown = state.Cooldown;
|
||||||
component.UseDelay = state.UseDelay;
|
component.UseDelay = state.UseDelay;
|
||||||
component.Charges = state.Charges;
|
component.Charges = state.Charges;
|
||||||
|
component.MaxCharges = state.MaxCharges;
|
||||||
|
component.RenewCharges = state.RenewCharges;
|
||||||
component.Container = EnsureEntity<T>(state.Container, uid);
|
component.Container = EnsureEntity<T>(state.Container, uid);
|
||||||
component.EntityIcon = EnsureEntity<T>(state.EntityIcon, uid);
|
component.EntityIcon = EnsureEntity<T>(state.EntityIcon, uid);
|
||||||
component.CheckCanInteract = state.CheckCanInteract;
|
component.CheckCanInteract = state.CheckCanInteract;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace Content.Client.Actions.UI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public (TimeSpan Start, TimeSpan End)? Cooldown { get; set; }
|
public (TimeSpan Start, TimeSpan End)? Cooldown { get; set; }
|
||||||
|
|
||||||
public ActionAlertTooltip(FormattedMessage name, FormattedMessage? desc, string? requires = null)
|
public ActionAlertTooltip(FormattedMessage name, FormattedMessage? desc, string? requires = null, FormattedMessage? charges = null)
|
||||||
{
|
{
|
||||||
_gameTiming = IoCManager.Resolve<IGameTiming>();
|
_gameTiming = IoCManager.Resolve<IGameTiming>();
|
||||||
|
|
||||||
@@ -52,6 +52,17 @@ namespace Content.Client.Actions.UI
|
|||||||
vbox.AddChild(description);
|
vbox.AddChild(description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (charges != null && !string.IsNullOrWhiteSpace(charges.ToString()))
|
||||||
|
{
|
||||||
|
var chargesLabel = new RichTextLabel
|
||||||
|
{
|
||||||
|
MaxWidth = TooltipTextMaxWidth,
|
||||||
|
StyleClasses = { StyleNano.StyleClassTooltipActionCharges }
|
||||||
|
};
|
||||||
|
chargesLabel.SetMessage(charges);
|
||||||
|
vbox.AddChild(chargesLabel);
|
||||||
|
}
|
||||||
|
|
||||||
vbox.AddChild(_cooldownLabel = new RichTextLabel
|
vbox.AddChild(_cooldownLabel = new RichTextLabel
|
||||||
{
|
{
|
||||||
MaxWidth = TooltipTextMaxWidth,
|
MaxWidth = TooltipTextMaxWidth,
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ namespace Content.Client.Stylesheets
|
|||||||
public const string StyleClassTooltipActionDescription = "tooltipActionDesc";
|
public const string StyleClassTooltipActionDescription = "tooltipActionDesc";
|
||||||
public const string StyleClassTooltipActionCooldown = "tooltipActionCooldown";
|
public const string StyleClassTooltipActionCooldown = "tooltipActionCooldown";
|
||||||
public const string StyleClassTooltipActionRequirements = "tooltipActionCooldown";
|
public const string StyleClassTooltipActionRequirements = "tooltipActionCooldown";
|
||||||
|
public const string StyleClassTooltipActionCharges = "tooltipActionCharges";
|
||||||
public const string StyleClassHotbarSlotNumber = "hotbarSlotNumber";
|
public const string StyleClassHotbarSlotNumber = "hotbarSlotNumber";
|
||||||
public const string StyleClassActionSearchBox = "actionSearchBox";
|
public const string StyleClassActionSearchBox = "actionSearchBox";
|
||||||
public const string StyleClassActionMenuItemRevoked = "actionMenuItemRevoked";
|
public const string StyleClassActionMenuItemRevoked = "actionMenuItemRevoked";
|
||||||
@@ -940,6 +941,10 @@ namespace Content.Client.Stylesheets
|
|||||||
{
|
{
|
||||||
new StyleProperty("font", notoSans15)
|
new StyleProperty("font", notoSans15)
|
||||||
}),
|
}),
|
||||||
|
new StyleRule(new SelectorElement(typeof(RichTextLabel), new[] {StyleClassTooltipActionCharges}, null, null), new[]
|
||||||
|
{
|
||||||
|
new StyleProperty("font", notoSans15)
|
||||||
|
}),
|
||||||
|
|
||||||
// small number for the entity counter in the entity menu
|
// small number for the entity counter in the entity menu
|
||||||
new StyleRule(new SelectorElement(typeof(Label), new[] {ContextMenuElement.StyleClassEntityMenuIconLabel}, null, null), new[]
|
new StyleRule(new SelectorElement(typeof(Label), new[] {ContextMenuElement.StyleClassEntityMenuIconLabel}, null, null), new[]
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
|||||||
|
|
||||||
// Is the action currently valid?
|
// Is the action currently valid?
|
||||||
if (!action.Enabled
|
if (!action.Enabled
|
||||||
|| action.Charges is 0
|
|| action is { Charges: 0, RenewCharges: false }
|
||||||
|| action.Cooldown.HasValue && action.Cooldown.Value.End > _timing.CurTime)
|
|| action.Cooldown.HasValue && action.Cooldown.Value.End > _timing.CurTime)
|
||||||
{
|
{
|
||||||
// The user is targeting with this action, but it is not valid. Maybe mark this click as
|
// The user is targeting with this action, but it is not valid. Maybe mark this click as
|
||||||
|
|||||||
@@ -191,6 +191,12 @@ public sealed class ActionButton : Control, IEntityControl
|
|||||||
var name = FormattedMessage.FromMarkupPermissive(Loc.GetString(metadata.EntityName));
|
var name = FormattedMessage.FromMarkupPermissive(Loc.GetString(metadata.EntityName));
|
||||||
var decr = FormattedMessage.FromMarkupPermissive(Loc.GetString(metadata.EntityDescription));
|
var decr = FormattedMessage.FromMarkupPermissive(Loc.GetString(metadata.EntityDescription));
|
||||||
|
|
||||||
|
if (_action is { Charges: not null })
|
||||||
|
{
|
||||||
|
var charges = FormattedMessage.FromMarkupPermissive(Loc.GetString($"Charges: {_action.Charges.Value.ToString()}/{_action.MaxCharges.ToString()}"));
|
||||||
|
return new ActionAlertTooltip(name, decr, charges: charges);
|
||||||
|
}
|
||||||
|
|
||||||
return new ActionAlertTooltip(name, decr);
|
return new ActionAlertTooltip(name, decr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,5 +5,6 @@
|
|||||||
<BoxContainer Orientation="Vertical" RectClipContent="True">
|
<BoxContainer Orientation="Vertical" RectClipContent="True">
|
||||||
<RichTextLabel MaxWidth="350" StyleClasses="StyleClassTooltipActionTitle"/>
|
<RichTextLabel MaxWidth="350" StyleClasses="StyleClassTooltipActionTitle"/>
|
||||||
<RichTextLabel MaxWidth="350" StyleClasses="StyleClassTooltipActionDescription"/>
|
<RichTextLabel MaxWidth="350" StyleClasses="StyleClassTooltipActionDescription"/>
|
||||||
|
<RichTextLabel MaxWidth="350" StyleClasses="StyleClassTooltipActionCharges"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</controls:ActionTooltip>
|
</controls:ActionTooltip>
|
||||||
|
|||||||
81
Content.Server/Commands/ActionCommands.cs
Normal file
81
Content.Server/Commands/ActionCommands.cs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
using Content.Server.Administration;
|
||||||
|
using Content.Shared.Actions;
|
||||||
|
using Content.Shared.Administration;
|
||||||
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
|
namespace Content.Server.Commands;
|
||||||
|
|
||||||
|
[AdminCommand(AdminFlags.Fun)]
|
||||||
|
internal sealed class UpgradeActionCommand : IConsoleCommand
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||||
|
|
||||||
|
public string Command => "upgradeaction";
|
||||||
|
public string Description => Loc.GetString("upgradeaction-command-description");
|
||||||
|
public string Help => Loc.GetString("upgradeaction-command-help");
|
||||||
|
|
||||||
|
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 1)
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("upgradeaction-command-need-one-argument"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Length > 2)
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("upgradeaction-command-max-two-arguments"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var actionUpgrade = _entMan.EntitySysManager.GetEntitySystem<ActionUpgradeSystem>();
|
||||||
|
var id = args[0];
|
||||||
|
|
||||||
|
if (!EntityUid.TryParse(id, out var uid))
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("upgradeaction-command-incorrect-entityuid-format"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_entMan.EntityExists(uid))
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("upgradeaction-command-entity-does-not-exist"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_entMan.TryGetComponent<ActionUpgradeComponent>(uid, out var actionUpgradeComponent))
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("upgradeaction-command-entity-is-not-action"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Length == 1)
|
||||||
|
{
|
||||||
|
if (!actionUpgrade.TryUpgradeAction(uid, actionUpgradeComponent))
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("upgradeaction-command-cannot-level-up"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Length == 2)
|
||||||
|
{
|
||||||
|
var levelArg = args[1];
|
||||||
|
|
||||||
|
if (!int.TryParse(levelArg, out var level))
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("upgradeaction-command-second-argument-not-number"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level <= 0)
|
||||||
|
{
|
||||||
|
shell.WriteLine(Loc.GetString("upgradeaction-command-less-than-required-level"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!actionUpgrade.TryUpgradeAction(uid, actionUpgradeComponent, level))
|
||||||
|
shell.WriteLine(Loc.GetString("upgradeaction-command-cannot-level-up"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Content.Shared/Actions/ActionUpgradeComponent.cs
Normal file
24
Content.Shared/Actions/ActionUpgradeComponent.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Actions;
|
||||||
|
|
||||||
|
// For actions that can use basic upgrades
|
||||||
|
// Not all actions should be upgradable
|
||||||
|
[RegisterComponent, NetworkedComponent, Access(typeof(ActionUpgradeSystem))]
|
||||||
|
public sealed partial class ActionUpgradeComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Current Level of the action.
|
||||||
|
/// </summary>
|
||||||
|
public int Level = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What level(s) effect this action?
|
||||||
|
/// You can skip levels, so you can have this entity change at level 2 but then won't change again until level 5.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("effectedLevels"), ViewVariables]
|
||||||
|
public Dictionary<int, EntProtoId> EffectedLevels = new();
|
||||||
|
|
||||||
|
// TODO: Branching level upgrades
|
||||||
|
}
|
||||||
149
Content.Shared/Actions/ActionUpgradeSystem.cs
Normal file
149
Content.Shared/Actions/ActionUpgradeSystem.cs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using Content.Shared.Actions.Events;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Shared.Actions;
|
||||||
|
|
||||||
|
public sealed class ActionUpgradeSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||||
|
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||||
|
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ActionUpgradeComponent, ActionUpgradeEvent>(OnActionUpgradeEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnActionUpgradeEvent(EntityUid uid, ActionUpgradeComponent component, ActionUpgradeEvent args)
|
||||||
|
{
|
||||||
|
if (!CanLevelUp(args.NewLevel, component.EffectedLevels, out var newActionProto)
|
||||||
|
|| !_actions.TryGetActionData(uid, out var actionComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var originalContainer = actionComp.Container;
|
||||||
|
var originalAttachedEntity = actionComp.AttachedEntity;
|
||||||
|
|
||||||
|
_actionContainer.RemoveAction(uid, actionComp);
|
||||||
|
|
||||||
|
EntityUid? upgradedActionId = null;
|
||||||
|
if (originalContainer != null
|
||||||
|
&& TryComp<ActionsContainerComponent>(originalContainer.Value, out var actionContainerComp))
|
||||||
|
{
|
||||||
|
upgradedActionId = _actionContainer.AddAction(originalContainer.Value, newActionProto, actionContainerComp);
|
||||||
|
|
||||||
|
if (originalAttachedEntity != null)
|
||||||
|
_actions.GrantContainedActions(originalAttachedEntity.Value, originalContainer.Value);
|
||||||
|
else
|
||||||
|
_actions.GrantContainedActions(originalContainer.Value, originalContainer.Value);
|
||||||
|
}
|
||||||
|
else if (originalAttachedEntity != null)
|
||||||
|
{
|
||||||
|
upgradedActionId = _actionContainer.AddAction(originalAttachedEntity.Value, newActionProto);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryComp<ActionUpgradeComponent>(upgradedActionId, out var upgradeComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
upgradeComp.Level = args.NewLevel;
|
||||||
|
|
||||||
|
// TODO: Preserve ordering of actions
|
||||||
|
|
||||||
|
_entityManager.DeleteEntity(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryUpgradeAction(EntityUid? actionId, ActionUpgradeComponent? actionUpgradeComponent = null, int newLevel = 0)
|
||||||
|
{
|
||||||
|
if (!TryGetActionUpgrade(actionId, out var actionUpgradeComp))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
actionUpgradeComponent ??= actionUpgradeComp;
|
||||||
|
DebugTools.AssertNotNull(actionUpgradeComponent);
|
||||||
|
DebugTools.AssertNotNull(actionId);
|
||||||
|
|
||||||
|
if (newLevel < 1)
|
||||||
|
newLevel = actionUpgradeComponent.Level + 1;
|
||||||
|
|
||||||
|
if (!CanLevelUp(newLevel, actionUpgradeComponent.EffectedLevels, out _))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
UpgradeAction(actionId, actionUpgradeComp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add checks for branching upgrades
|
||||||
|
private bool CanLevelUp(
|
||||||
|
int newLevel,
|
||||||
|
Dictionary<int, EntProtoId> levelDict,
|
||||||
|
[NotNullWhen(true)]out EntProtoId? newLevelProto)
|
||||||
|
{
|
||||||
|
newLevelProto = null;
|
||||||
|
|
||||||
|
if (levelDict.Count < 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var canLevel = false;
|
||||||
|
var finalLevel = levelDict.Keys.ToList()[levelDict.Keys.Count - 1];
|
||||||
|
|
||||||
|
foreach (var (level, proto) in levelDict)
|
||||||
|
{
|
||||||
|
if (newLevel != level || newLevel > finalLevel)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
canLevel = true;
|
||||||
|
newLevelProto = proto;
|
||||||
|
DebugTools.AssertNotNull(newLevelProto);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return canLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raises a level by one
|
||||||
|
/// </summary>
|
||||||
|
public void UpgradeAction(EntityUid? actionId, ActionUpgradeComponent? actionUpgradeComponent = null, int newLevel = 0)
|
||||||
|
{
|
||||||
|
if (!TryGetActionUpgrade(actionId, out var actionUpgradeComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
actionUpgradeComponent ??= actionUpgradeComp;
|
||||||
|
DebugTools.AssertNotNull(actionUpgradeComponent);
|
||||||
|
DebugTools.AssertNotNull(actionId);
|
||||||
|
|
||||||
|
if (newLevel < 1)
|
||||||
|
newLevel = actionUpgradeComponent.Level + 1;
|
||||||
|
|
||||||
|
RaiseActionUpgradeEvent(newLevel, actionId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RaiseActionUpgradeEvent(int level, EntityUid actionId)
|
||||||
|
{
|
||||||
|
var ev = new ActionUpgradeEvent(level, actionId);
|
||||||
|
RaiseLocalEvent(actionId, ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetActionUpgrade(
|
||||||
|
[NotNullWhen(true)] EntityUid? uid,
|
||||||
|
[NotNullWhen(true)] out ActionUpgradeComponent? result,
|
||||||
|
bool logError = true)
|
||||||
|
{
|
||||||
|
result = null;
|
||||||
|
if (!Exists(uid))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!TryComp<ActionUpgradeComponent>(uid, out var actionUpgradeComponent))
|
||||||
|
{
|
||||||
|
Log.Error($"Failed to get action upgrade from action entity: {ToPrettyString(uid.Value)}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = actionUpgradeComponent;
|
||||||
|
DebugTools.AssertOwner(uid, result);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -66,9 +66,21 @@ public abstract partial class BaseActionComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convenience tool for actions with limited number of charges. Automatically decremented on use, and the
|
/// Convenience tool for actions with limited number of charges. Automatically decremented on use, and the
|
||||||
/// action is disabled when it reaches zero. Does NOT automatically remove the action from the action bar.
|
/// action is disabled when it reaches zero. Does NOT automatically remove the action from the action bar.
|
||||||
|
/// However, charges will regenerate if <see cref="RenewCharges"/> is enabled and the action will not disable
|
||||||
|
/// when charges reach zero.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("charges")] public int? Charges;
|
[DataField("charges")] public int? Charges;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The max charges this action has, set automatically from <see cref="Charges"/>
|
||||||
|
/// </summary>
|
||||||
|
public int MaxCharges;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If enabled, charges will regenerate after a <see cref="Cooldown"/> is complete
|
||||||
|
/// </summary>
|
||||||
|
[DataField("renewCharges")]public bool RenewCharges;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The entity that contains this action. If the action is innate, this may be the user themselves.
|
/// The entity that contains this action. If the action is innate, this may be the user themselves.
|
||||||
/// This should almost always be non-null.
|
/// This should almost always be non-null.
|
||||||
@@ -159,6 +171,8 @@ public abstract class BaseActionComponentState : ComponentState
|
|||||||
public (TimeSpan Start, TimeSpan End)? Cooldown;
|
public (TimeSpan Start, TimeSpan End)? Cooldown;
|
||||||
public TimeSpan? UseDelay;
|
public TimeSpan? UseDelay;
|
||||||
public int? Charges;
|
public int? Charges;
|
||||||
|
public int MaxCharges;
|
||||||
|
public bool RenewCharges;
|
||||||
public NetEntity? Container;
|
public NetEntity? Container;
|
||||||
public NetEntity? EntityIcon;
|
public NetEntity? EntityIcon;
|
||||||
public bool CheckCanInteract;
|
public bool CheckCanInteract;
|
||||||
@@ -186,6 +200,8 @@ public abstract class BaseActionComponentState : ComponentState
|
|||||||
Cooldown = component.Cooldown;
|
Cooldown = component.Cooldown;
|
||||||
UseDelay = component.UseDelay;
|
UseDelay = component.UseDelay;
|
||||||
Charges = component.Charges;
|
Charges = component.Charges;
|
||||||
|
MaxCharges = component.MaxCharges;
|
||||||
|
RenewCharges = component.RenewCharges;
|
||||||
CheckCanInteract = component.CheckCanInteract;
|
CheckCanInteract = component.CheckCanInteract;
|
||||||
ClientExclusive = component.ClientExclusive;
|
ClientExclusive = component.ClientExclusive;
|
||||||
Priority = component.Priority;
|
Priority = component.Priority;
|
||||||
|
|||||||
13
Content.Shared/Actions/Events/ActionUpgradeEvent.cs
Normal file
13
Content.Shared/Actions/Events/ActionUpgradeEvent.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Content.Shared.Actions.Events;
|
||||||
|
|
||||||
|
public sealed class ActionUpgradeEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public int NewLevel;
|
||||||
|
public EntityUid? ActionId;
|
||||||
|
|
||||||
|
public ActionUpgradeEvent(int newLevel, EntityUid? actionId)
|
||||||
|
{
|
||||||
|
NewLevel = newLevel;
|
||||||
|
ActionId = actionId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,11 +30,16 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||||
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||||
|
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<InstantActionComponent, MapInitEvent>(OnInit);
|
||||||
|
SubscribeLocalEvent<EntityTargetActionComponent, MapInitEvent>(OnInit);
|
||||||
|
SubscribeLocalEvent<WorldTargetActionComponent, MapInitEvent>(OnInit);
|
||||||
|
|
||||||
SubscribeLocalEvent<ActionsComponent, DidEquipEvent>(OnDidEquip);
|
SubscribeLocalEvent<ActionsComponent, DidEquipEvent>(OnDidEquip);
|
||||||
SubscribeLocalEvent<ActionsComponent, DidEquipHandEvent>(OnHandEquipped);
|
SubscribeLocalEvent<ActionsComponent, DidEquipHandEvent>(OnHandEquipped);
|
||||||
SubscribeLocalEvent<ActionsComponent, DidUnequipEvent>(OnDidUnequip);
|
SubscribeLocalEvent<ActionsComponent, DidUnequipEvent>(OnDidUnequip);
|
||||||
@@ -56,6 +61,12 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
SubscribeAllEvent<RequestPerformActionEvent>(OnActionRequest);
|
SubscribeAllEvent<RequestPerformActionEvent>(OnActionRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnInit(EntityUid uid, BaseActionComponent component, MapInitEvent args)
|
||||||
|
{
|
||||||
|
if (component.Charges != null)
|
||||||
|
component.MaxCharges = component.Charges.Value;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnShutdown(EntityUid uid, ActionsComponent component, ComponentShutdown args)
|
private void OnShutdown(EntityUid uid, ActionsComponent component, ComponentShutdown args)
|
||||||
{
|
{
|
||||||
foreach (var act in component.Actions)
|
foreach (var act in component.Actions)
|
||||||
@@ -165,6 +176,31 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
Dirty(actionId.Value, action);
|
Dirty(actionId.Value, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetUseDelay(EntityUid? actionId, TimeSpan? delay)
|
||||||
|
{
|
||||||
|
if (!TryGetActionData(actionId, out var action) || action.UseDelay == delay)
|
||||||
|
return;
|
||||||
|
|
||||||
|
action.UseDelay = delay;
|
||||||
|
UpdateAction(actionId, action);
|
||||||
|
Dirty(actionId.Value, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReduceUseDelay(EntityUid? actionId, TimeSpan? lowerDelay)
|
||||||
|
{
|
||||||
|
if (!TryGetActionData(actionId, out var action))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (action.UseDelay != null && lowerDelay != null)
|
||||||
|
action.UseDelay = action.UseDelay - lowerDelay;
|
||||||
|
|
||||||
|
if (action.UseDelay < TimeSpan.Zero)
|
||||||
|
action.UseDelay = null;
|
||||||
|
|
||||||
|
UpdateAction(actionId, action);
|
||||||
|
Dirty(actionId.Value, action);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnRejuventate(EntityUid uid, ActionsComponent component, RejuvenateEvent args)
|
private void OnRejuventate(EntityUid uid, ActionsComponent component, RejuvenateEvent args)
|
||||||
{
|
{
|
||||||
foreach (var act in component.Actions)
|
foreach (var act in component.Actions)
|
||||||
@@ -218,6 +254,51 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
Dirty(actionId.Value, action);
|
Dirty(actionId.Value, action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int? GetCharges(EntityUid? actionId)
|
||||||
|
{
|
||||||
|
if (!TryGetActionData(actionId, out var action))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return action.Charges;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddCharges(EntityUid? actionId, int addCharges)
|
||||||
|
{
|
||||||
|
if (!TryGetActionData(actionId, out var action) || action.Charges == null || addCharges < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
action.Charges += addCharges;
|
||||||
|
UpdateAction(actionId, action);
|
||||||
|
Dirty(actionId.Value, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveCharges(EntityUid? actionId, int? removeCharges)
|
||||||
|
{
|
||||||
|
if (!TryGetActionData(actionId, out var action) || action.Charges == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (removeCharges == null)
|
||||||
|
action.Charges = removeCharges;
|
||||||
|
else
|
||||||
|
action.Charges -= removeCharges;
|
||||||
|
|
||||||
|
if (action.Charges is < 0)
|
||||||
|
action.Charges = null;
|
||||||
|
|
||||||
|
UpdateAction(actionId, action);
|
||||||
|
Dirty(actionId.Value, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetCharges(EntityUid? actionId)
|
||||||
|
{
|
||||||
|
if (!TryGetActionData(actionId, out var action))
|
||||||
|
return;
|
||||||
|
|
||||||
|
action.Charges = action.MaxCharges;
|
||||||
|
UpdateAction(actionId, action);
|
||||||
|
Dirty(actionId.Value, action);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnActionsGetState(EntityUid uid, ActionsComponent component, ref ComponentGetState args)
|
private void OnActionsGetState(EntityUid uid, ActionsComponent component, ref ComponentGetState args)
|
||||||
{
|
{
|
||||||
args.State = new ActionsComponentState(GetNetEntitySet(component.Actions));
|
args.State = new ActionsComponentState(GetNetEntitySet(component.Actions));
|
||||||
@@ -261,9 +342,14 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var curTime = GameTiming.CurTime;
|
var curTime = GameTiming.CurTime;
|
||||||
|
// TODO: Check for charge recovery timer
|
||||||
if (action.Cooldown.HasValue && action.Cooldown.Value.End > curTime)
|
if (action.Cooldown.HasValue && action.Cooldown.Value.End > curTime)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// TODO: Replace with individual charge recovery when we have the visuals to aid it
|
||||||
|
if (action is { Charges: < 1, RenewCharges: true })
|
||||||
|
ResetCharges(actionEnt);
|
||||||
|
|
||||||
BaseActionEvent? performEvent = null;
|
BaseActionEvent? performEvent = null;
|
||||||
|
|
||||||
// Validate request by checking action blockers and the like:
|
// Validate request by checking action blockers and the like:
|
||||||
@@ -438,12 +524,12 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
dirty = true;
|
dirty = true;
|
||||||
action.Charges--;
|
action.Charges--;
|
||||||
if (action.Charges == 0)
|
if (action is { Charges: 0, RenewCharges: false })
|
||||||
action.Enabled = false;
|
action.Enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
action.Cooldown = null;
|
action.Cooldown = null;
|
||||||
if (action.UseDelay != null)
|
if (action is { UseDelay: not null, Charges: null or < 1 })
|
||||||
{
|
{
|
||||||
dirty = true;
|
dirty = true;
|
||||||
action.Cooldown = (curTime, curTime + action.UseDelay.Value);
|
action.Cooldown = (curTime, curTime + action.UseDelay.Value);
|
||||||
|
|||||||
12
Resources/Locale/en-US/actions/actions/actions-commands.ftl
Normal file
12
Resources/Locale/en-US/actions/actions/actions-commands.ftl
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
## Actions Commands loc
|
||||||
|
|
||||||
|
## Upgradeaction command loc
|
||||||
|
upgradeaction-command-need-one-argument = upgradeaction needs at least one argument, the action entity uid. The second optional argument is a specified level.
|
||||||
|
upgradeaction-command-max-two-arguments = upgradeaction has a maximum of two arguments, the action entity uid and the (optional) level to set.
|
||||||
|
upgradeaction-command-second-argument-not-number = upgradeaction's second argument can only be a number.
|
||||||
|
upgradeaction-command-less-than-required-level = upgradeaction cannot accept a level of 0 or lower.
|
||||||
|
upgradeaction-command-incorrect-entityuid-format = You must use a valid entityuid format for upgradeaction.
|
||||||
|
upgradeaction-command-entity-does-not-exist = This entity does not exist, a valid entity is required for upgradeaction.
|
||||||
|
upgradeaction-command-entity-is-not-action = This entity doesn't have the action upgrade component, so this action cannot be leveled.
|
||||||
|
upgradeaction-command-cannot-level-up = The action cannot be leveled up.
|
||||||
|
upgradeaction-command-description = Upgrades an action by one level, or to the specified level, if applicable.
|
||||||
@@ -5,7 +5,34 @@
|
|||||||
noSpawn: true
|
noSpawn: true
|
||||||
components:
|
components:
|
||||||
- type: WorldTargetAction
|
- type: WorldTargetAction
|
||||||
useDelay: 30
|
useDelay: 15
|
||||||
|
itemIconStyle: BigAction
|
||||||
|
checkCanAccess: false
|
||||||
|
range: 60
|
||||||
|
sound: !type:SoundPathSpecifier
|
||||||
|
path: /Audio/Magic/fireball.ogg
|
||||||
|
icon:
|
||||||
|
sprite: Objects/Magic/magicactions.rsi
|
||||||
|
state: fireball
|
||||||
|
event: !type:ProjectileSpellEvent
|
||||||
|
prototype: ProjectileFireball
|
||||||
|
posData: !type:TargetCasterPos
|
||||||
|
speech: action-speech-spell-fireball
|
||||||
|
- type: ActionUpgrade
|
||||||
|
effectedLevels:
|
||||||
|
2: ActionFireballII
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: ActionFireballII
|
||||||
|
parent: ActionFireball
|
||||||
|
name: Fireball II
|
||||||
|
description: Fire three explosive fireball towards the clicked location.
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: WorldTargetAction
|
||||||
|
useDelay: 5
|
||||||
|
charges: 3
|
||||||
|
renewCharges: true
|
||||||
itemIconStyle: BigAction
|
itemIconStyle: BigAction
|
||||||
checkCanAccess: false
|
checkCanAccess: false
|
||||||
range: 60
|
range: 60
|
||||||
|
|||||||
Reference in New Issue
Block a user