using Content.Server.Actions; using Content.Server.Bed.Components; using Content.Server.Body.Systems; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Bed; using Content.Shared.Bed.Sleep; using Content.Shared.Body.Components; using Content.Shared.Buckle.Components; using Content.Shared.Damage; using Content.Shared.Emag.Systems; using Content.Shared.Mobs.Systems; using Content.Shared.Power; using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Server.Bed { public sealed class BedSystem : EntitySystem { [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly ActionsSystem _actionsSystem = default!; [Dependency] private readonly EmagSystem _emag = default!; [Dependency] private readonly SleepingSystem _sleepingSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly IGameTiming _timing = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnStrapped); SubscribeLocalEvent(OnUnstrapped); SubscribeLocalEvent(OnStasisStrapped); SubscribeLocalEvent(OnStasisUnstrapped); SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnEmagged); } private void OnStrapped(Entity bed, ref StrappedEvent args) { EnsureComp(bed); bed.Comp.NextHealTime = _timing.CurTime + TimeSpan.FromSeconds(bed.Comp.HealTime); _actionsSystem.AddAction(args.Buckle, ref bed.Comp.SleepAction, SleepingSystem.SleepActionId, bed); // Single action entity, cannot strap multiple entities to the same bed. DebugTools.AssertEqual(args.Strap.Comp.BuckledEntities.Count, 1); } private void OnUnstrapped(Entity bed, ref UnstrappedEvent args) { _actionsSystem.RemoveAction(args.Buckle, bed.Comp.SleepAction); _sleepingSystem.TryWaking(args.Buckle.Owner); RemComp(bed); } public override void Update(float frameTime) { base.Update(frameTime); var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out _, out var bedComponent, out var strapComponent)) { if (_timing.CurTime < bedComponent.NextHealTime) continue; bedComponent.NextHealTime += TimeSpan.FromSeconds(bedComponent.HealTime); if (strapComponent.BuckledEntities.Count == 0) continue; foreach (var healedEntity in strapComponent.BuckledEntities) { if (_mobStateSystem.IsDead(healedEntity)) continue; var damage = bedComponent.Damage; if (HasComp(healedEntity)) damage *= bedComponent.SleepMultiplier; _damageableSystem.TryChangeDamage(healedEntity, damage, true, origin: uid); } } } private void UpdateAppearance(EntityUid uid, bool isOn) { _appearance.SetData(uid, StasisBedVisuals.IsOn, isOn); } private void OnStasisStrapped(Entity bed, ref StrappedEvent args) { if (!HasComp(args.Buckle) || !this.IsPowered(bed, EntityManager)) return; var metabolicEvent = new ApplyMetabolicMultiplierEvent(args.Buckle, bed.Comp.Multiplier, true); RaiseLocalEvent(args.Buckle, ref metabolicEvent); } private void OnStasisUnstrapped(Entity bed, ref UnstrappedEvent args) { if (!HasComp(args.Buckle) || !this.IsPowered(bed, EntityManager)) return; var metabolicEvent = new ApplyMetabolicMultiplierEvent(args.Buckle, bed.Comp.Multiplier, false); RaiseLocalEvent(args.Buckle, ref metabolicEvent); } private void OnPowerChanged(EntityUid uid, StasisBedComponent component, ref PowerChangedEvent args) { UpdateAppearance(uid, args.Powered); UpdateMetabolisms(uid, component, args.Powered); } private void OnEmagged(EntityUid uid, StasisBedComponent component, ref GotEmaggedEvent args) { if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) return; if (_emag.CheckFlag(uid, EmagType.Interaction)) return; // Reset any metabolisms first so they receive the multiplier correctly UpdateMetabolisms(uid, component, false); component.Multiplier = 1 / component.Multiplier; UpdateMetabolisms(uid, component, true); args.Handled = true; } private void UpdateMetabolisms(EntityUid uid, StasisBedComponent component, bool shouldApply) { if (!TryComp(uid, out var strap) || strap.BuckledEntities.Count == 0) return; foreach (var buckledEntity in strap.BuckledEntities) { var metabolicEvent = new ApplyMetabolicMultiplierEvent(buckledEntity, component.Multiplier, shouldApply); RaiseLocalEvent(buckledEntity, ref metabolicEvent); } } } }