using Content.Server.Damage.Components; using Content.Server.DeviceLinking.Events; using Content.Server.DeviceLinking.Systems; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.PowerCell; using Content.Shared._White.Events; using Content.Shared.Actions; using Content.Shared.Damage; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Inventory; using Content.Shared.Popups; using Content.Shared.PowerCell; using Content.Shared.PowerCell.Components; using Content.Shared.Throwing; using Content.Shared.Timing; using Content.Shared.Toggleable; using Content.Shared.Verbs; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; namespace Content.Server.EnergyDome; public sealed partial class EnergyDomeSystem : EntitySystem { [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly PowerCellSystem _powerCell = default!; [Dependency] private readonly DeviceLinkSystem _signalSystem = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnThrow); //Generator events SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnActivatedInWorld); SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnSignalReceived); SubscribeLocalEvent(OnGetActions); SubscribeLocalEvent(OnToggleAction); SubscribeLocalEvent(OnPowerCellChanged); SubscribeLocalEvent(OnPowerCellSlotEmpty); SubscribeLocalEvent(OnChargeChanged); SubscribeLocalEvent(OnParentChanged); SubscribeLocalEvent>(AddToggleDomeVerb); SubscribeLocalEvent(OnExamine); SubscribeLocalEvent(OnComponentRemove); SubscribeLocalEvent>( OnClothesTurnOff); //Dome events SubscribeLocalEvent(OnDomeDamaged); } private void OnThrow(Entity ent, ref ThrownEvent args) { if (args.User == null) return; RaiseLocalEvent(args.User.Value, new EnergyDomeClothesTurnOffEvent()); } private void OnClothesTurnOff(Entity ent, ref InventoryRelayedEvent args) { TurnOff(ent, false); } private void OnInit(Entity generator, ref MapInitEvent args) { if (generator.Comp.CanDeviceNetworkUse) _signalSystem.EnsureSinkPorts(generator, generator.Comp.TogglePort, generator.Comp.OnPort, generator.Comp.OffPort); } //different ways of use private void OnSignalReceived(Entity generator, ref SignalReceivedEvent args) { if (!generator.Comp.CanDeviceNetworkUse) return; if (args.Port == generator.Comp.OnPort) { AttemptToggle(generator, true); } if (args.Port == generator.Comp.OffPort) { AttemptToggle(generator, false); } if (args.Port == generator.Comp.TogglePort) { AttemptToggle(generator, !generator.Comp.Enabled); } } private void OnAfterInteract(Entity generator, ref AfterInteractEvent args) { if (generator.Comp.CanInteractUse) AttemptToggle(generator, !generator.Comp.Enabled); } private void OnActivatedInWorld(Entity generator, ref ActivateInWorldEvent args) { if (generator.Comp.CanInteractUse) AttemptToggle(generator, !generator.Comp.Enabled); } private void OnExamine(Entity generator, ref ExaminedEvent args) { args.PushMarkup(Loc.GetString( (generator.Comp.Enabled) ? "energy-dome-on-examine-is-on-message" : "energy-dome-on-examine-is-off-message" )); } private void AddToggleDomeVerb( Entity generator, ref GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract || !generator.Comp.CanInteractUse) return; ActivationVerb verb = new() { Text = Loc.GetString("energy-dome-verb-toggle"), Act = () => AttemptToggle(generator, !generator.Comp.Enabled) }; args.Verbs.Add(verb); } private void OnGetActions(Entity generator, ref GetItemActionsEvent args) { if (args.SlotFlags != SlotFlags.OUTERCLOTHING) return; args.AddAction(ref generator.Comp.ToggleActionEntity, generator.Comp.ToggleAction); } private void OnToggleAction(Entity generator, ref ToggleActionEvent args) { if (args.Handled) return; AttemptToggle(generator, !generator.Comp.Enabled); args.Handled = true; } // System interactions private void OnPowerCellSlotEmpty(Entity generator, ref PowerCellSlotEmptyEvent args) { TurnOff(generator, true); } private void OnPowerCellChanged(Entity generator, ref PowerCellChangedEvent args) { if (args.Ejected || !_powerCell.HasDrawCharge(generator)) TurnOff(generator, true); } private void OnChargeChanged(Entity generator, ref ChargeChangedEvent args) { if (args.Charge == 0) TurnOff(generator, true); } private void OnDomeDamaged(Entity dome, ref DamageChangedEvent args) { if (dome.Comp.Generator == null) return; var generatorUid = dome.Comp.Generator.Value; if (!TryComp(generatorUid, out var generatorComp)) return; if (args.DamageDelta == null) return; float totalDamage = args.DamageDelta.GetTotal().Float(); var energyLeak = totalDamage * generatorComp.DamageEnergyDraw; _audio.PlayPvs(generatorComp.ParrySound, dome); if (HasComp(generatorUid)) { if (_powerCell.TryGetBatteryFromSlot(generatorUid, out var cell, out var batteryComp)) { _battery.UseCharge(cell.Value, energyLeak, batteryComp); if (batteryComp.CurrentCharge == 0) TurnOff((generatorUid, generatorComp), true); } } //it seems to me it would not work well to hang both a powercell and an internal battery with wire charging on the object.... if (TryComp(generatorUid, out var battery)) { _battery.UseCharge(generatorUid, energyLeak); if (battery.CurrentCharge == 0) TurnOff((generatorUid, generatorComp), true); } } private void OnParentChanged(Entity generator, ref EntParentChangedMessage args) { //To do: taking the active barrier in hand for some reason does not manage to change the parent in this case, //and the barrier is not turned off. // //Laying down works well (-_-) if (GetProtectedEntity(generator) != generator.Comp.DomeParentEntity) TurnOff(generator, false); } private void OnComponentRemove(Entity generator, ref ComponentRemove args) { TurnOff(generator, false); } // Functional public bool AttemptToggle(Entity generator, bool status) { if (TryComp(generator, out var useDelay) && _useDelay.IsDelayed(new Entity(generator, useDelay))) { _audio.PlayPvs(generator.Comp.TurnOffSound, generator); _popup.PopupEntity(Loc.GetString("energy-dome-recharging"), generator); return false; } if (TryComp(generator, out _)) { if (!_powerCell.TryGetBatteryFromSlot(generator, out _) && !TryComp(generator, out BatteryComponent? _)) { _audio.PlayPvs(generator.Comp.TurnOffSound, generator); _popup.PopupEntity(Loc.GetString("energy-dome-no-cell"), generator); return false; } if (!_powerCell.HasDrawCharge(generator)) { _audio.PlayPvs(generator.Comp.TurnOffSound, generator); _popup.PopupEntity(Loc.GetString("energy-dome-no-power"), generator); return false; } } if (TryComp(generator, out var battery)) { if (battery.CurrentCharge == 0) { _audio.PlayPvs(generator.Comp.TurnOffSound, generator); _popup.PopupEntity(Loc.GetString("energy-dome-no-power"), generator); return false; } } Toggle(generator, status); return true; } private void Toggle(Entity generator, bool status) { if (status) TurnOn(generator); else TurnOff(generator, false); } private void TurnOn(Entity generator) { if (generator.Comp.Enabled) return; var protectedEntity = GetProtectedEntity(generator); var newDome = Spawn(generator.Comp.DomePrototype, Transform(protectedEntity).Coordinates); generator.Comp.DomeParentEntity = protectedEntity; _transform.SetParent(newDome, protectedEntity); if (TryComp(newDome, out var domeComp)) { domeComp.Generator = generator; } _powerCell.SetPowerCellDrawEnabled(generator, true); if (TryComp(generator, out var recharger)) { recharger.AutoRecharge = true; } generator.Comp.SpawnedDome = newDome; _audio.PlayPvs(generator.Comp.TurnOnSound, generator); generator.Comp.Enabled = true; } private void TurnOff(Entity generator, bool startReloading) { if (!generator.Comp.Enabled) return; generator.Comp.Enabled = false; Del(generator.Comp.SpawnedDome); _powerCell.SetPowerCellDrawEnabled(generator, false); if (TryComp(generator, out var recharger)) { recharger.AutoRecharge = false; } _audio.PlayPvs(generator.Comp.TurnOffSound, generator); if (startReloading) { _audio.PlayPvs(generator.Comp.EnergyOutSound, generator); if (TryComp(generator, out var useDelay)) { _useDelay.TryResetDelay(new Entity(generator, useDelay)); } } } // Util private EntityUid GetProtectedEntity(EntityUid entity) { return (_container.TryGetOuterContainer(entity, Transform(entity), out var container)) ? container.Owner : entity; } }