6
2025-11-12 10:55:00 +03:00

240 lines
7.8 KiB
C#

using Content.Shared.Actions;
using Content.Shared.Actions.Components;
using Content.Shared.Actions.Events;
using Content.Shared.Interaction;
using Robust.Shared.Serialization;
namespace Content.Shared._RMC14.Actions;
public sealed class RMCActionsSystem : EntitySystem
{
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
private EntityQuery<ActionSharedCooldownComponent> _actionSharedCooldownQuery;
public override void Initialize()
{
_actionSharedCooldownQuery = GetEntityQuery<ActionSharedCooldownComponent>();
SubscribeLocalEvent<ActionSharedCooldownComponent, ActionPerformedEvent>(OnSharedCooldownPerformed);
SubscribeLocalEvent<ActionCooldownComponent, RMCActionUseEvent>(OnCooldownUse);
SubscribeLocalEvent<ActionInRangeUnobstructedComponent, RMCActionUseAttemptEvent>(OnInRangeUnobstructedUseAttempt);
SubscribeLocalEvent<ActionComponent, ActionReducedUseDelayEvent>(OnReducedUseDelayEvent);
SubscribeAllEvent<RMCMissedTargetActionEvent>(OnMissedTargetAction);
}
private void OnSharedCooldownPerformed(Entity<ActionSharedCooldownComponent> ent, ref ActionPerformedEvent args)
{
if (ent.Comp.OnPerform)
ActivateSharedCooldown((ent, ent), args.Performer);
}
public void ActivateSharedCooldown(Entity<ActionSharedCooldownComponent?> action, EntityUid performer)
{
if (!Resolve(action, ref action.Comp, false))
return;
if (action.Comp.Cooldown == TimeSpan.Zero)
return;
foreach (var (actionId, _) in _actions.GetActions(performer))
{
if (!_actionSharedCooldownQuery.TryComp(actionId, out var shared))
continue;
// Same ID or primary ID found in subset of other action's ids
if ((shared.Id != null && shared.Id == action.Comp.Id) || (action.Comp.Id != null && shared.Ids.Contains(action.Comp.Id.Value)))
_actions.SetIfBiggerCooldown(actionId, action.Comp.Cooldown);
}
}
/// <summary>
/// Enable all events that have a shared cooldown with the provided action
/// </summary>
public void EnableSharedCooldownEvents(Entity<ActionSharedCooldownComponent?> action, EntityUid performer)
{
SetStatusOfSharedCooldownEvents(action, performer, true);
}
/// <summary>
/// Disable all events that have a shared cooldown with the provided action
/// </summary>
public void DisableSharedCooldownEvents(Entity<ActionSharedCooldownComponent?> action, EntityUid performer)
{
SetStatusOfSharedCooldownEvents(action, performer, false);
}
/// <summary>
/// Sets the enabled status of all events that have a shared cooldown with the provided action
/// </summary>
private void SetStatusOfSharedCooldownEvents(Entity<ActionSharedCooldownComponent?> action, EntityUid performer, bool newStatus)
{
if (!Resolve(action, ref action.Comp, false))
return;
if (action.Comp.Cooldown == TimeSpan.Zero)
return;
foreach (var (actionId, comp) in _actions.GetActions(performer))
{
if (!_actionSharedCooldownQuery.TryComp(actionId, out var shared))
continue;
// Same ID or primary ID found in subset of other action's ids
if (!(shared.Id != null && shared.Id == action.Comp.Id || action.Comp.Id != null &&
(shared.Ids.Contains(action.Comp.Id.Value) || shared.ActiveIds.Contains(action.Comp.Id.Value))))
{
continue;
}
_actions.SetEnabled((actionId, comp), newStatus);
}
}
private void OnReducedUseDelayEvent(EntityUid uid, ActionComponent component, ActionReducedUseDelayEvent args)
{
if (!TryComp(uid, out ActionReducedUseDelayComponent? comp))
return;
if (args.Amount < 0 || args.Amount > 1)
return;
comp.UseDelayReduction = args.Amount;
if (TryComp(uid, out ActionSharedCooldownComponent? shared))
{
comp.UseDelayBase ??= shared.Cooldown;
RefreshSharedUseDelay((uid, comp), shared);
return;
}
// Should be fine to only set this once as the base use delay should remain constant
comp.UseDelayBase ??= component.UseDelay;
RefreshUseDelay((uid, comp));
}
private void RefreshUseDelay(Entity<ActionReducedUseDelayComponent> ent)
{
if (ent.Comp.UseDelayBase is not { } delayBase)
return;
var reduction = ent.Comp.UseDelayReduction.Double();
var delayNew = delayBase.Multiply(1 - reduction);
_actions.SetUseDelay(ent.Owner, delayNew);
}
private void RefreshSharedUseDelay(Entity<ActionReducedUseDelayComponent> ent, ActionSharedCooldownComponent shared)
{
if (ent.Comp.UseDelayBase is not { } delayBase)
return;
var reduction = ent.Comp.UseDelayReduction.Double();
var delayNew = delayBase.Multiply(1 - reduction);
shared.Cooldown = delayNew;
}
private void OnCooldownUse(Entity<ActionCooldownComponent> ent, ref RMCActionUseEvent args)
{
_actions.SetIfBiggerCooldown(ent.Owner, ent.Comp.Cooldown);
}
private void OnMissedTargetAction(RMCMissedTargetActionEvent args)
{
var action = GetEntity(args.Action);
if (!TryComp(action, out RMCCooldownOnMissComponent? cooldown))
return;
_actions.SetIfBiggerCooldown(action, cooldown.MissCooldown);
}
private void OnInRangeUnobstructedUseAttempt(Entity<ActionInRangeUnobstructedComponent> ent, ref RMCActionUseAttemptEvent args)
{
if (args.Cancelled)
return;
if (args.Target is not { } target)
return;
if (!_interaction.InRangeUnobstructed(ent.Owner, target, ent.Comp.Range))
args.Cancelled = true;
}
public bool CanUseActionPopup(EntityUid user, EntityUid action, EntityUid? target = null)
{
var ev = new RMCActionUseAttemptEvent(user, target);
RaiseLocalEvent(action, ref ev);
return !ev.Cancelled;
}
private void ActionUsed(EntityUid user, EntityUid action)
{
var ev = new RMCActionUseEvent(user);
RaiseLocalEvent(action, ref ev);
}
public bool TryUseAction(EntityUid user, EntityUid action, EntityUid target)
{
if (!CanUseActionPopup(user, action, target))
return false;
ActionUsed(user, action);
return true;
}
public bool TryUseAction(InstantActionEvent action)
{
if (!CanUseActionPopup(action.Performer, action.Action))
return false;
ActionUsed(action.Performer, action.Action);
return true;
}
public bool TryUseAction(EntityTargetActionEvent action)
{
if (!CanUseActionPopup(action.Performer, action.Action, action.Target))
return false;
ActionUsed(action.Performer, action.Action);
return true;
}
public bool TryUseAction(WorldTargetActionEvent action)
{
if (!CanUseActionPopup(action.Performer, action.Action))
return false;
ActionUsed(action.Performer, action.Action);
return true;
}
public IEnumerable<Entity<ActionComponent>> GetActionsWithEvent<T>(EntityUid user) where T : BaseActionEvent
{
foreach (var action in _actions.GetActions(user))
{
if (_actions.GetEvent(action) is T)
yield return action;
}
}
}
[Serializable, NetSerializable]
public sealed class RMCMissedTargetActionEvent : EntityEventArgs
{
public readonly NetEntity Action;
public RMCMissedTargetActionEvent(NetEntity actionId)
{
Action = actionId;
}
}