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 _query; public override void Initialize() { base.Initialize(); _query = GetEntityQuery(); SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent>(OnActivateVerb); SubscribeLocalEvent(OnActivate); SubscribeLocalEvent(UpdateClothingLayer); } private void OnStartup(Entity ent, ref ComponentStartup args) { var state = ent.Comp.State; Switch((ent, ent.Comp), state, predicted: ent.Comp.Predictable); } private void OnMapInit(Entity ent, ref MapInitEvent args) { var state = ent.Comp.State; Switch((ent, ent.Comp), state, predicted: ent.Comp.Predictable); } private void OnUseInHand(Entity 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 ent, ref GetVerbsEvent 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 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 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; } /// /// Used when an item is attempted to be toggled. /// Sets its state to the opposite of what it is. /// public bool Switch(Entity 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 ent, string key) { } protected virtual void UpdateVisuals(Entity 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 ent, ref ItemSwitchedEvent args) { _clothing.SetEquippedPrefix(ent, args.State, ent.Comp); } }