6
2025-11-03 10:15:18 +03:00

186 lines
6.2 KiB
C#

using Content.Shared._Horizon.ItemSwitch.Components;
using Content.Shared.Clothing.Components;
using Content.Shared.Clothing.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Item;
using Content.Shared.Popups;
using Content.Shared.Toggleable;
using Content.Shared.Verbs;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
namespace Content.Shared._Horizon.ItemSwitch;
public abstract class SharedItemSwitchSystem : EntitySystem
{
[Dependency] private readonly INetManager _netManager = null!;
[Dependency] private readonly SharedAppearanceSystem _appearance = null!;
[Dependency] private readonly SharedAudioSystem _audio = null!;
[Dependency] private readonly SharedPopupSystem _popup = null!;
[Dependency] private readonly SharedItemSystem _item = null!;
[Dependency] private readonly ClothingSystem _clothing = null!;
private EntityQuery<ItemSwitchComponent> _query;
public override void Initialize()
{
base.Initialize();
_query = GetEntityQuery<ItemSwitchComponent>();
SubscribeLocalEvent<ItemSwitchComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<ItemSwitchComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<ItemSwitchComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<ItemSwitchComponent, GetVerbsEvent<ActivationVerb>>(OnActivateVerb);
SubscribeLocalEvent<ItemSwitchComponent, ActivateInWorldEvent>(OnActivate);
SubscribeLocalEvent<ClothingComponent, ItemSwitchedEvent>(UpdateClothingLayer);
}
private void OnStartup(Entity<ItemSwitchComponent> ent, ref ComponentStartup args)
{
var state = ent.Comp.State;
Switch((ent, ent.Comp), state, predicted: ent.Comp.Predictable);
}
private void OnMapInit(Entity<ItemSwitchComponent> ent, ref MapInitEvent args)
{
var state = ent.Comp.State;
Switch((ent, ent.Comp), state, predicted: ent.Comp.Predictable);
}
private void OnUseInHand(Entity<ItemSwitchComponent> ent, ref UseInHandEvent args)
{
if (args.Handled || !ent.Comp.OnUse || ent.Comp.States.Count == 0)
return;
args.Handled = true;
Switch((ent, ent.Comp), Next(ent), args.User, predicted: ent.Comp.Predictable);
}
private void OnActivateVerb(Entity<ItemSwitchComponent> ent, ref GetVerbsEvent<ActivationVerb> args)
{
if (!args.CanAccess || !args.CanInteract || !ent.Comp.OnActivate || ent.Comp.States.Count == 0)
return;
var user = args.User;
args.ExtraCategories.Add(VerbCategory.Switch);
foreach (var state in ent.Comp.States)
{
args.Verbs.Add(new ActivationVerb()
{
Text = Loc.TryGetString(state.Value.Verb, out var title) ? title : state.Value.Verb,
Category = VerbCategory.Switch,
Act = () => Switch((ent.Owner, ent.Comp), state.Key, user, ent.Comp.Predictable)
});
}
}
private void OnActivate(Entity<ItemSwitchComponent> ent, ref ActivateInWorldEvent args)
{
if (args.Handled || !ent.Comp.OnActivate)
return;
args.Handled = true;
Switch((ent.Owner, ent.Comp), Next(ent), args.User, predicted: ent.Comp.Predictable);
}
private static string Next(Entity<ItemSwitchComponent> ent)
{
var foundCurrent = false;
string firstState = null!;
foreach (var state in ent.Comp.States.Keys)
{
if (foundCurrent)
return state;
if (state == ent.Comp.State)
foundCurrent = true;
}
return firstState;
}
/// <summary>
/// Used when an item is attempted to be toggled.
/// Sets its state to the opposite of what it is.
/// </summary>
public bool Switch(Entity<ItemSwitchComponent?> ent, string key, EntityUid? user = null, bool predicted = true)
{
if (!_query.Resolve(ent, ref ent.Comp, false) || !ent.Comp.States.TryGetValue(key, out var state))
return false;
var uid = ent.Owner;
var comp = ent.Comp;
if (!comp.Predictable && _netManager.IsClient)
return true;
var attempt = new ItemSwitchAttemptEvent
{
User = user,
State = key
};
RaiseLocalEvent(uid, ref attempt);
if (ent.Comp.States.TryGetValue(ent.Comp.State, out var prevState) && prevState.RemoveComponents is not null)
EntityManager.RemoveComponents(ent, prevState.RemoveComponents);
if (state.AddComponents is not null)
EntityManager.AddComponents(ent, state.AddComponents);
if (!comp.Predictable)
predicted = false;
if (attempt.Cancelled)
{
if (predicted)
_audio.PlayPredicted(state.SoundFailToActivate, uid, user);
else
_audio.PlayPvs(state.SoundFailToActivate, uid);
if (attempt.Popup == null || user == null)
return false;
if (predicted)
_popup.PopupClient(attempt.Popup, uid, user.Value);
else
_popup.PopupEntity(attempt.Popup, uid, user.Value);
return false;
}
if (predicted)
_audio.PlayPredicted(state.SoundStateActivate, uid, user);
else
_audio.PlayPvs(state.SoundStateActivate, uid);
comp.State = key;
UpdateVisuals((uid, comp), key);
Dirty(uid, comp);
var switched = new ItemSwitchedEvent { Predicted = predicted, State = key, User = user };
RaiseLocalEvent(uid, ref switched);
return true;
}
public virtual void VisualsChanged(Entity<ItemSwitchComponent> ent, string key)
{
}
protected virtual void UpdateVisuals(Entity<ItemSwitchComponent> ent, string key)
{
if (TryComp(ent, out AppearanceComponent? appearance))
_appearance.SetData(ent, SwitchableVisuals.Switched, key, appearance);
_item.SetHeldPrefix(ent, key);
VisualsChanged(ent, key);
}
private void UpdateClothingLayer(Entity<ClothingComponent> ent, ref ItemSwitchedEvent args)
{
_clothing.SetEquippedPrefix(ent, args.State, ent.Comp);
}
}