using Content.Shared.Access.Components; using Content.Shared.Access.Systems; using Content.Shared.Actions; using Content.Shared.Audio; using Content.Shared.Buckle; using Content.Shared.Buckle.Components; using Content.Shared.Hands; using Content.Shared.Inventory.VirtualItem; using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Content.Shared._NF.Vehicle.Components; // Frontier using Content.Shared.ActionBlocker; // Frontier using Content.Shared.Actions.Components; // Frontier using Content.Shared.Light.Components; // Frontier using Content.Shared.Light.EntitySystems; // Frontier using Content.Shared.Movement.Pulling.Components; // Frontier using Content.Shared.Movement.Pulling.Events; // Frontier using Content.Shared.Popups; // Frontier using Robust.Shared.Network; // Frontier using Robust.Shared.Prototypes; // Frontier using Robust.Shared.Timing; // Frontier using Content.Shared.Weapons.Melee.Events; // Frontier using Content.Shared.Emag.Systems; // Frontier namespace Content.Shared._Goobstation.Vehicles; // Frontier: migrate under _Goobstation public abstract partial class SharedVehicleSystem : EntitySystem { [Dependency] private readonly AccessReaderSystem _access = default!; [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly SharedAmbientSoundSystem _ambientSound = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedBuckleSystem _buckle = default!; [Dependency] private readonly SharedMoverController _mover = default!; [Dependency] private readonly SharedVirtualItemSystem _virtualItem = default!; [Dependency] private readonly IGameTiming _timing = default!; // Frontier [Dependency] private readonly INetManager _net = default!; // Frontier [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; // Frontier [Dependency] private readonly ActionContainerSystem _actionContainer = default!; // Frontier [Dependency] private readonly EmagSystem _emag = default!; // Frontier [Dependency] private readonly SharedPopupSystem _popup = default!; // Frontier [Dependency] private readonly UnpoweredFlashlightSystem _flashlight = default!; // Frontier public static readonly EntProtoId HornActionId = "ActionHorn"; public static readonly EntProtoId SirenActionId = "ActionSiren"; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnMapInit); // Frontier SubscribeLocalEvent(OnRemove); SubscribeLocalEvent(OnStrapAttempt); SubscribeLocalEvent(OnStrapped); SubscribeLocalEvent(OnUnstrapped); SubscribeLocalEvent(OnDropped); SubscribeLocalEvent(OnMeleeHit); // Frontier SubscribeLocalEvent(OnGotEmagged, before: [typeof(UnpoweredFlashlightSystem)]); // Frontier SubscribeLocalEvent(OnGotUnemagged, before: [typeof(UnpoweredFlashlightSystem)]); // Frontier SubscribeLocalEvent(OnInsert); SubscribeLocalEvent(OnEject); SubscribeLocalEvent(OnHorn); SubscribeLocalEvent(OnSiren); SubscribeLocalEvent(OnRiderPull); // Frontier } private void OnInit(EntityUid uid, VehicleComponent component, ComponentInit args) { _appearance.SetData(uid, VehicleState.Animated, component.EngineRunning && component.Driver != null); // Frontier: add Driver != null _appearance.SetData(uid, VehicleState.DrawOver, false); } // Frontier private void OnMapInit(EntityUid uid, VehicleComponent component, MapInitEvent args) { bool actionsUpdated = false; if (component.HornSound != null) { _actionContainer.EnsureAction(uid, ref component.HornAction, HornActionId); actionsUpdated = true; } if (component.SirenSound != null) { _actionContainer.EnsureAction(uid, ref component.SirenAction, SirenActionId); actionsUpdated = true; } if (actionsUpdated) Dirty(uid, component); } // End Frontier private void OnRemove(EntityUid uid, VehicleComponent component, ComponentRemove args) { if (component.Driver == null) return; _buckle.TryUnbuckle(component.Driver.Value, component.Driver.Value); Dismount(component.Driver.Value, uid); _appearance.SetData(uid, VehicleState.DrawOver, false); } private void OnInsert(EntityUid uid, VehicleComponent component, ref EntInsertedIntoContainerMessage args) { if (HasComp(args.Entity)) return; // Frontier: check key slot if (args.Container.ID != component.KeySlotId) return; if (!_timing.IsFirstTimePredicted) return; // End Frontier: check key slot component.EngineRunning = true; _appearance.SetData(uid, VehicleState.Animated, component.Driver != null); _ambientSound.SetAmbience(uid, true); if (component.Driver == null) return; Mount(component.Driver.Value, uid); } private void OnEject(EntityUid uid, VehicleComponent component, ref EntRemovedFromContainerMessage args) { // Frontier: check key slot if (args.Container.ID != component.KeySlotId) return; if (!_timing.IsFirstTimePredicted) return; // End Frontier: check key slot component.EngineRunning = false; _appearance.SetData(uid, VehicleState.Animated, false); _ambientSound.SetAmbience(uid, false); if (component.Driver == null) return; Dismount(component.Driver.Value, uid, removeDriver: false); // Frontier: add removeDriver: false - the driver is still around. } private void OnHorn(EntityUid uid, VehicleComponent component, InstantActionEvent args) { if (args.Handled == true || component.Driver != args.Performer || component.HornSound == null) return; _audio.PlayPredicted(component.HornSound, uid, args.Performer); // Frontier: PlayPvs ent, ref StrapAttemptEvent args) { var driver = args.Buckle.Owner; // i dont want to re write this shit 100 fucking times if (ent.Comp.Driver != null) { args.Cancelled = true; return; } // Frontier: no pulling when riding if (TryComp(args.Buckle, out var puller) && puller.Pulling != null) { _popup.PopupPredicted(Loc.GetString("vehicle-cannot-pull", ("object", puller.Pulling), ("vehicle", ent)), ent, args.Buckle); args.Cancelled = true; return; } // End Frontier if (ent.Comp.RequiredHands != 0) { for (int hands = 0; hands < ent.Comp.RequiredHands; hands++) { if (!_virtualItem.TrySpawnVirtualItemInHand(ent.Owner, driver, false)) { args.Cancelled = true; _virtualItem.DeleteInHandsMatching(driver, ent.Owner); return; } } } // AddHorns(driver, ent); // Frontier: delay until mounted } protected virtual void OnStrapped(Entity ent, ref StrappedEvent args) // Frontier: private(driver); // Frontier Dirty(driver, rider); // Frontier if (!ent.Comp.EngineRunning) return; Mount(driver, ent.Owner); } protected virtual void OnUnstrapped(Entity ent, ref UnstrappedEvent args) // Frontier: private(args.Buckle.Owner); // Frontier } private void OnDropped(EntityUid uid, VehicleComponent comp, VirtualItemDeletedEvent args) { if (comp.Driver != args.User) return; _buckle.TryUnbuckle(args.User, args.User); Dismount(args.User, uid); _appearance.SetData(uid, VehicleState.DrawOver, false); _appearance.SetData(uid, VehicleState.Animated, false); // Frontier RemComp(args.User); // Frontier } // Frontier: do not hit your own vehicle private void OnMeleeHit(Entity ent, ref MeleeHitEvent args) { if (args.User == ent.Comp.Driver) // Don't hit your own vehicle args.Handled = true; } // End Frontier: do not hit your own vehicle private void AddHorns(EntityUid driver, EntityUid vehicle) { if (!TryComp(vehicle, out var vehicleComp)) return; // Frontier: grant existing actions List grantedActions = new(); if (vehicleComp.HornAction != null) grantedActions.Add(vehicleComp.HornAction.Value); if (vehicleComp.SirenAction != null) grantedActions.Add(vehicleComp.SirenAction.Value); if (TryComp(vehicle, out var flashlight) && flashlight.ToggleActionEntity != null) { grantedActions.Add(flashlight.ToggleActionEntity.Value); _flashlight.SetLight((vehicle, flashlight), flashlight.LightOn, quiet: true); } // Only try to grant actions if the vehicle actually has them. if (grantedActions.Count > 0) _actions.GrantActions(driver, grantedActions, vehicle); // End Frontier } private void Mount(EntityUid driver, EntityUid vehicle) { if (TryComp(vehicle, out var accessComp)) { var accessSources = _access.FindPotentialAccessItems(driver); var access = _access.FindAccessTags(driver, accessSources); foreach (var tag in access) { accessComp.Tags.Add(tag); } } _mover.SetRelay(driver, vehicle); AddHorns(driver, vehicle); // Frontier } private void Dismount(EntityUid driver, EntityUid vehicle, bool removeDriver = true) // Frontier: add removeDriver { if (!TryComp(vehicle, out var vehicleComp) || vehicleComp.Driver != driver) return; RemComp(driver); _actionBlocker.UpdateCanMove(driver); // Frontier: bugfix, relay input mover only updates on shutdown, not remove if (removeDriver) // Frontier vehicleComp.Driver = null; _actions.RemoveProvidedActions(driver, vehicle); // Frontier: don't remove actions, just provide/revoke them if (removeDriver) // Frontier _virtualItem.DeleteInHandsMatching(driver, vehicle); if (TryComp(vehicle, out var accessComp)) accessComp.Tags.Clear(); } // Frontier: prevent drivers from pulling things, emag handlers private void OnRiderPull(Entity ent, ref PullAttemptEvent args) { if (args.PullerUid == ent.Owner) args.Cancelled = true; } private void OnGotEmagged(Entity ent, ref GotEmaggedEvent args) { if (args.Handled) return; if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) return; if (ent.Comp.RadarBlip) { ent.Comp.RadarBlip = false; Dirty(ent); HandleEmag(ent); // Hack: assuming the only other emaggable component on the vehicle is a flashlight args.Repeatable = HasComp(ent); args.Handled = true; } } private void OnGotUnemagged(Entity ent, ref GotUnEmaggedEvent args) { if (args.Handled) return; if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) return; if (!ent.Comp.RadarBlip) { ent.Comp.RadarBlip = true; Dirty(ent); HandleUnemag(ent); args.Handled = true; } } protected abstract void HandleEmag(Entity ent); protected abstract void HandleUnemag(Entity ent); // End Frontier } public sealed partial class HornActionEvent : InstantActionEvent; public sealed partial class SirenActionEvent : InstantActionEvent;