using Content.Server.Actions; using Content.Server.Bed.Components; using Content.Server.Bed.Sleep; using Content.Server.Body.Systems; using Content.Server.Construction; 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.Components; using Content.Shared.Emag.Systems; using Content.Shared.Mobs.Systems; using Robust.Shared.Timing; namespace Content.Server.Bed { public sealed class BedSystem : EntitySystem { [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly ActionsSystem _actionsSystem = 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(ManageUpdateList); SubscribeLocalEvent(OnBuckleChange); SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnEmagged); SubscribeLocalEvent(OnRefreshParts); SubscribeLocalEvent(OnUpgradeExamine); } private void ManageUpdateList(EntityUid uid, HealOnBuckleComponent component, ref BuckleChangeEvent args) { if (args.Buckling) { AddComp(uid); component.NextHealTime = _timing.CurTime + TimeSpan.FromSeconds(component.HealTime); _actionsSystem.AddAction(args.BuckledEntity, ref component.SleepAction, SleepingSystem.SleepActionId, uid); Dirty(uid, component); return; } _actionsSystem.RemoveAction(args.BuckledEntity, component.SleepAction); _sleepingSystem.TryWaking(args.BuckledEntity); RemComp(uid); } 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 OnBuckleChange(EntityUid uid, StasisBedComponent component, ref BuckleChangeEvent args) { // In testing this also received an unbuckle event when the bed is destroyed // So don't worry about that if (!HasComp(args.BuckledEntity)) return; if (!this.IsPowered(uid, EntityManager)) return; var metabolicEvent = new ApplyMetabolicMultiplierEvent {Uid = args.BuckledEntity, Multiplier = component.Multiplier, Apply = args.Buckling}; RaiseLocalEvent(args.BuckledEntity, 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) { args.Repeatable = true; // 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 {Uid = buckledEntity, Multiplier = component.Multiplier, Apply = shouldApply}; RaiseLocalEvent(buckledEntity, metabolicEvent); } } private void OnRefreshParts(EntityUid uid, StasisBedComponent component, RefreshPartsEvent args) { var metabolismRating = args.PartRatings[component.MachinePartMetabolismModifier]; component.Multiplier = component.BaseMultiplier * metabolismRating; //linear scaling so it's not OP if (HasComp(uid)) component.Multiplier = 1f / component.Multiplier; } private void OnUpgradeExamine(EntityUid uid, StasisBedComponent component, UpgradeExamineEvent args) { args.AddPercentageUpgrade("stasis-bed-component-upgrade-stasis", component.Multiplier / component.BaseMultiplier); } } }