using Content.Shared.Actions; using Content.Shared.Body; using Content.Shared.Corvax.Ipc; using Content.Shared.Damage; using Content.Shared.Damage.Systems; using Content.Shared.Emp; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Markings; using Content.Shared.Mobs; using Content.Shared.Movement.Systems; using Content.Shared.Ninja.Components; using Content.Shared.Ninja.Systems; using Content.Shared.Popups; using Content.Shared.Power.EntitySystems; using Content.Shared.PowerCell; using Content.Shared.Sound.Components; using Robust.Shared.Audio; using Robust.Shared.Player; using Robust.Shared.Prototypes; namespace Content.Server.Corvax.Ipc; public sealed partial class IpcSystem : EntitySystem { [Dependency] private SharedActionsSystem _action = default!; [Dependency] private SharedBatteryDrainerSystem _batteryDrainer = default!; [Dependency] private SharedPopupSystem _popup = default!; [Dependency] private PowerCellSystem _powerCell = default!; [Dependency] private DamageableSystem _damageable = default!; [Dependency] private MovementSpeedModifierSystem _movementSpeedModifier = default!; [Dependency] private SharedUserInterfaceSystem _ui = default!; [Dependency] private SharedBatterySystem _battery = default!; [Dependency] private SharedVisualBodySystem _visualBody = default!; [Dependency] private IPrototypeManager _prototype = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnComponentShutdown); SubscribeLocalEvent(OnPowerCellChanged); SubscribeLocalEvent(OnToggleAction); SubscribeLocalEvent(OnEmpPulse); SubscribeLocalEvent(OnRefreshMovementSpeedModifiers); SubscribeLocalEvent(OnMobStateChanged); SubscribeLocalEvent(OnOpenFaceAction); Subs.BuiEvents(IpcFaceUiKey.Face, subs => { subs.Event(OnFaceSelected); }); SubscribeLocalEvent(OnPowerCellEmpty); } public override void Update(float frameTime) { base.Update(frameTime); var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var comp)) { if (!comp.IsSlowedByBattery) continue; if (_powerCell.TryGetBatteryFromSlot(uid, out var battery) && _battery.GetCharge(battery.Value.AsNullable()) / battery.Value.Comp.MaxCharge >= 0.01f) { comp.IsSlowedByBattery = false; _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); } } } private void OnPowerCellEmpty(EntityUid uid, IpcComponent component, ref PowerCellSlotEmptyEvent args) { UpdateBatteryAlert((uid, component)); } private void OnMapInit(EntityUid uid, IpcComponent component, MapInitEvent args) { UpdateBatteryAlert((uid, component)); _action.AddAction(uid, ref component.ActionEntity, component.DrainBatteryAction); _action.AddAction(uid, ref component.ChangeFaceActionEntity, component.ChangeFaceAction); _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); if (!HasComp(uid)) return; if (_visualBody.TryGatherMarkingsData(uid, null, out _, out _, out var applied) && applied.TryGetValue("Head", out var headMarkings) && headMarkings.TryGetValue(HumanoidVisualLayers.Snout, out var snoutMarkings) && snoutMarkings.Count > 0) { component.SelectedFace = snoutMarkings[0].MarkingId; Dirty(uid, component); } } private void OnComponentShutdown(EntityUid uid, IpcComponent component, ComponentShutdown args) { _action.RemoveAction(uid, component.ActionEntity); _action.RemoveAction(uid, component.ChangeFaceActionEntity); } private void OnPowerCellChanged(EntityUid uid, IpcComponent component, ref PowerCellChangedEvent args) { if (MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating) return; UpdateBatteryAlert((uid, component)); } private void OnToggleAction(EntityUid uid, IpcComponent component, ToggleDrainActionEvent args) { if (args.Handled) return; component.DrainActivated = !component.DrainActivated; _action.SetToggled(component.ActionEntity, component.DrainActivated); args.Handled = true; if (component.DrainActivated && _powerCell.TryGetBatteryFromSlot(uid, out var battery)) { EnsureComp(uid); _batteryDrainer.SetBattery(uid, battery.Value.Owner); } else RemComp(uid); var message = component.DrainActivated ? "ipc-component-ready" : "ipc-component-disabled"; _popup.PopupEntity(Loc.GetString(message), uid, uid); } private void UpdateBatteryAlert(Entity ent) { _movementSpeedModifier.RefreshMovementSpeedModifiers(ent.Owner); } private void OnRefreshMovementSpeedModifiers(EntityUid uid, IpcComponent comp, RefreshMovementSpeedModifiersEvent args) { if (!_powerCell.TryGetBatteryFromSlot(uid, out var battery) || battery.Value.Comp.MaxCharge <= 0 || _battery.GetCharge(battery.Value.AsNullable()) / battery.Value.Comp.MaxCharge < 0.01f) { args.ModifySpeed(0.2f); comp.IsSlowedByBattery = true; } else { comp.IsSlowedByBattery = false; } } private void OnOpenFaceAction(EntityUid uid, IpcComponent comp, OpenIpcFaceActionEvent args) { if (args.Handled) return; if (!TryComp(uid, out var actor)) return; _ui.SetUiState(uid, IpcFaceUiKey.Face, new IpcFaceBuiState(comp.FaceProfile, comp.SelectedFace)); _ui.TryToggleUi(uid, IpcFaceUiKey.Face, actor.PlayerSession); args.Handled = true; } private void OnFaceSelected(Entity ent, ref IpcFaceSelectMessage msg) { if (!_prototype.TryIndex(ent.Comp.FaceProfile, out var faceProfile) || !faceProfile.Faces.Contains(msg.State)) return; if (_visualBody.TryGatherMarkingsData(ent.Owner, null, out var profiles, out var markings, out var applied)) { if (applied.TryGetValue("Head", out var headMarkings) && headMarkings.TryGetValue(HumanoidVisualLayers.Snout, out var snoutMarkings) && snoutMarkings.Count > 0) { _visualBody.ApplyMarkings(ent.Owner, new() { ["Head"] = new() { [HumanoidVisualLayers.Snout] = new List() { new(msg.State, snoutMarkings[0].MarkingColors.Count) }, }, }); } else if (_prototype.TryIndex(msg.State, out var proto)) { _visualBody.ApplyMarkings(ent.Owner, new() { ["Head"] = new() { [HumanoidVisualLayers.Snout] = new List() { proto.AsMarking() }, }, }); } } ent.Comp.SelectedFace = msg.State; Dirty(ent); _ui.CloseUi(ent.Owner, IpcFaceUiKey.Face); } private void OnEmpPulse(EntityUid uid, IpcComponent component, ref EmpPulseEvent args) { args.Affected = true; var damage = new DamageSpecifier(); damage.DamageDict.Add("Shock", 30); _damageable.TryChangeDamage(uid, damage); } private void OnMobStateChanged(EntityUid uid, IpcComponent component, ref MobStateChangedEvent args) { if (args.NewMobState == MobState.Critical) { var sound = EnsureComp(uid); sound.Sound = new SoundPathSpecifier("/Audio/Machines/buzz-two.ogg"); sound.MinInterval = TimeSpan.FromSeconds(15); sound.MaxInterval = TimeSpan.FromSeconds(30); sound.PopUp = Loc.GetString("sleep-ipc"); Dirty(uid, sound); } else { RemComp(uid); } } }