Make Saltern driveable (#4257)
* Broadphase refactor (content) * Shuttle jank * Fixes * Testing jank * Features and things * Balance stuffsies * AHHHHHHHHHHHHHHHH * Mass and stuff working * Fix drops * Another balance pass * Balance AGEN * Add in stuff for rotating shuttles for debugging * Nothing to see here * Testbed stuffsies * Fix some tests * Fixen test * Try fixing map * Shuttle movement balance pass * lasaggne * Basic Helmsman console working * Slight docking cleanup * Helmsman requires power * Basic shuttle test * Stuff * Fix computations * Add shuttle console to saltern * Rename helmsman to shuttleconsole * Final stretch * More tweaks * Fix piloting prediction for now.
This commit is contained in:
11
Content.Server/Shuttles/ShuttleComponent.cs
Normal file
11
Content.Server/Shuttles/ShuttleComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Content.Shared.Shuttles;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Shuttles
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ShuttleComponent : SharedShuttleComponent
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
21
Content.Server/Shuttles/ShuttleConsoleComponent.cs
Normal file
21
Content.Server/Shuttles/ShuttleConsoleComponent.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Shuttles;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Shuttles
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedShuttleConsoleComponent))]
|
||||
internal sealed class ShuttleConsoleComponent : SharedShuttleConsoleComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
public List<PilotComponent> SubscribedPilots = new();
|
||||
|
||||
/// <summary>
|
||||
/// Whether the console can be used to pilot. Toggled whenever it gets powered / unpowered.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool Enabled { get; set; } = false;
|
||||
}
|
||||
}
|
||||
169
Content.Server/Shuttles/ShuttleConsoleSystem.cs
Normal file
169
Content.Server/Shuttles/ShuttleConsoleSystem.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Alert;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Content.Shared.Shuttles;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Shuttles
|
||||
{
|
||||
internal sealed class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ShuttleConsoleComponent, ComponentShutdown>(HandleConsoleShutdown);
|
||||
SubscribeLocalEvent<PilotComponent, ComponentShutdown>(HandlePilotShutdown);
|
||||
SubscribeLocalEvent<ShuttleConsoleComponent, ActivateInWorldEvent>(HandleConsoleInteract);
|
||||
SubscribeLocalEvent<PilotComponent, MoveEvent>(HandlePilotMove);
|
||||
SubscribeLocalEvent<ShuttleConsoleComponent, PowerChangedEvent>(HandlePowerChange);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
foreach (var comp in ComponentManager.EntityQuery<PilotComponent>().ToArray())
|
||||
{
|
||||
if (comp.Console == null) continue;
|
||||
|
||||
if (!Get<ActionBlockerSystem>().CanInteract(comp.Owner))
|
||||
{
|
||||
RemovePilot(comp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Console requires power to operate.
|
||||
/// </summary>
|
||||
private void HandlePowerChange(EntityUid uid, ShuttleConsoleComponent component, PowerChangedEvent args)
|
||||
{
|
||||
if (!args.Powered)
|
||||
{
|
||||
component.Enabled = false;
|
||||
|
||||
ClearPilots(component);
|
||||
}
|
||||
else
|
||||
{
|
||||
component.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If pilot is moved then we'll stop them from piloting.
|
||||
/// </summary>
|
||||
private void HandlePilotMove(EntityUid uid, PilotComponent component, MoveEvent args)
|
||||
{
|
||||
if (component.Console == null) return;
|
||||
RemovePilot(component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For now pilots just interact with the console and can start piloting with wasd.
|
||||
/// </summary>
|
||||
private void HandleConsoleInteract(EntityUid uid, ShuttleConsoleComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (!args.User.HasTag("CanPilot"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pilotComponent = args.User.EnsureComponent<PilotComponent>();
|
||||
|
||||
if (!component.Enabled)
|
||||
{
|
||||
args.User.PopupMessage($"Console is not powered.");
|
||||
return;
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
var console = pilotComponent.Console;
|
||||
|
||||
if (console != null)
|
||||
{
|
||||
RemovePilot(pilotComponent);
|
||||
|
||||
if (console != component)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AddPilot(args.User, component);
|
||||
}
|
||||
|
||||
private void HandlePilotShutdown(EntityUid uid, PilotComponent component, ComponentShutdown args)
|
||||
{
|
||||
RemovePilot(component);
|
||||
}
|
||||
|
||||
private void HandleConsoleShutdown(EntityUid uid, ShuttleConsoleComponent component, ComponentShutdown args)
|
||||
{
|
||||
ClearPilots(component);
|
||||
}
|
||||
|
||||
public void AddPilot(IEntity entity, ShuttleConsoleComponent component)
|
||||
{
|
||||
if (!Get<ActionBlockerSystem>().CanInteract(entity) ||
|
||||
!entity.TryGetComponent(out PilotComponent? pilotComponent) ||
|
||||
component.SubscribedPilots.Contains(pilotComponent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
component.SubscribedPilots.Add(pilotComponent);
|
||||
|
||||
if (entity.TryGetComponent(out ServerAlertsComponent? alertsComponent))
|
||||
{
|
||||
alertsComponent.ShowAlert(AlertType.PilotingShuttle);
|
||||
}
|
||||
|
||||
entity.PopupMessage(Loc.GetString("shuttle-pilot-start"));
|
||||
pilotComponent.Console = component;
|
||||
pilotComponent.Dirty();
|
||||
}
|
||||
|
||||
public void RemovePilot(PilotComponent pilotComponent)
|
||||
{
|
||||
var console = pilotComponent.Console;
|
||||
|
||||
if (console is not ShuttleConsoleComponent helmsman) return;
|
||||
|
||||
pilotComponent.Console = null;
|
||||
|
||||
if (!helmsman.SubscribedPilots.Remove(pilotComponent)) return;
|
||||
|
||||
if (pilotComponent.Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent))
|
||||
{
|
||||
alertsComponent.ClearAlert(AlertType.PilotingShuttle);
|
||||
}
|
||||
|
||||
pilotComponent.Owner.PopupMessage(Loc.GetString("shuttle-pilot-end"));
|
||||
// TODO: RemoveComponent<T> is cooked and doesn't sync to client so this fucks up movement prediction.
|
||||
// ComponentManager.RemoveComponent<PilotComponent>(pilotComponent.Owner.Uid);
|
||||
pilotComponent.Console = null;
|
||||
pilotComponent.Dirty();
|
||||
}
|
||||
|
||||
public void RemovePilot(IEntity entity)
|
||||
{
|
||||
if (!entity.TryGetComponent(out PilotComponent? pilotComponent)) return;
|
||||
|
||||
RemovePilot(pilotComponent);
|
||||
}
|
||||
|
||||
public void ClearPilots(ShuttleConsoleComponent component)
|
||||
{
|
||||
foreach (var pilot in component.SubscribedPilots)
|
||||
{
|
||||
RemovePilot(pilot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
147
Content.Server/Shuttles/ShuttleSystem.cs
Normal file
147
Content.Server/Shuttles/ShuttleSystem.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using Content.Shared.Shuttles;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Physics;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Content.Server.Shuttles
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class ShuttleSystem : EntitySystem
|
||||
{
|
||||
private const float TileMassMultiplier = 1f;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ShuttleComponent, ComponentStartup>(HandleShuttleStartup);
|
||||
SubscribeLocalEvent<ShuttleComponent, ComponentShutdown>(HandleShuttleShutdown);
|
||||
|
||||
SubscribeLocalEvent<GridInitializeEvent>(HandleGridInit);
|
||||
SubscribeLocalEvent<GridFixtureChangeEvent>(HandleGridFixtureChange);
|
||||
}
|
||||
|
||||
private void HandleGridFixtureChange(GridFixtureChangeEvent args)
|
||||
{
|
||||
var fixture = args.NewFixture;
|
||||
|
||||
if (fixture == null) return;
|
||||
|
||||
fixture.Mass = fixture.Area * TileMassMultiplier;
|
||||
|
||||
if (fixture.Body.Owner.TryGetComponent(out ShuttleComponent? shuttleComponent))
|
||||
{
|
||||
RecalculateSpeedMultiplier(shuttleComponent, fixture.Body);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleGridInit(GridInitializeEvent ev)
|
||||
{
|
||||
EntityManager.GetEntity(ev.EntityUid).EnsureComponent<ShuttleComponent>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache the thrust available to this shuttle.
|
||||
/// </summary>
|
||||
private void RecalculateSpeedMultiplier(SharedShuttleComponent shuttle, PhysicsComponent physics)
|
||||
{
|
||||
// TODO: Need per direction speed (Never Eat Soggy Weetbix).
|
||||
// TODO: This will need hella tweaking.
|
||||
var thrusters = physics.FixtureCount;
|
||||
|
||||
if (thrusters == 0)
|
||||
{
|
||||
shuttle.SpeedMultipler = 0f;
|
||||
}
|
||||
|
||||
const float ThrustPerThruster = 0.25f;
|
||||
const float MinimumThrustRequired = 0.005f;
|
||||
|
||||
// Just so someone can't slap a single thruster on a station and call it a day; need to hit a minimum amount.
|
||||
var thrustRatio = Math.Max(0, thrusters * ThrustPerThruster / physics.Mass);
|
||||
|
||||
if (thrustRatio < MinimumThrustRequired)
|
||||
shuttle.SpeedMultipler = 0f;
|
||||
|
||||
const float MaxThrust = 10f;
|
||||
// This doesn't need to align with MinimumThrustRequired; if you set this higher it just means the first few additional
|
||||
// thrusters won't do anything.
|
||||
const float MinThrust = MinimumThrustRequired;
|
||||
|
||||
shuttle.SpeedMultipler = MathF.Max(MinThrust, MathF.Min(MaxThrust, thrustRatio));
|
||||
}
|
||||
|
||||
private void HandleShuttleStartup(EntityUid uid, ShuttleComponent component, ComponentStartup args)
|
||||
{
|
||||
if (!component.Owner.HasComponent<IMapGridComponent>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!component.Owner.TryGetComponent(out PhysicsComponent? physicsComponent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.Enabled)
|
||||
{
|
||||
Enable(physicsComponent);
|
||||
}
|
||||
|
||||
if (component.Owner.TryGetComponent(out ShuttleComponent? shuttleComponent))
|
||||
{
|
||||
RecalculateSpeedMultiplier(shuttleComponent, physicsComponent);
|
||||
}
|
||||
}
|
||||
|
||||
public void Toggle(ShuttleComponent component)
|
||||
{
|
||||
if (!component.Owner.TryGetComponent(out PhysicsComponent? physicsComponent)) return;
|
||||
|
||||
component.Enabled = !component.Enabled;
|
||||
|
||||
if (component.Enabled)
|
||||
{
|
||||
Enable(physicsComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Disable(physicsComponent);
|
||||
}
|
||||
}
|
||||
|
||||
private void Enable(PhysicsComponent component)
|
||||
{
|
||||
component.BodyType = BodyType.Dynamic;
|
||||
component.BodyStatus = BodyStatus.InAir;
|
||||
//component.FixedRotation = false; TODO WHEN ROTATING SHUTTLES FIXED.
|
||||
component.FixedRotation = true;
|
||||
component.LinearDamping = 0.05f;
|
||||
}
|
||||
|
||||
private void Disable(PhysicsComponent component)
|
||||
{
|
||||
component.BodyType = BodyType.Static;
|
||||
component.BodyStatus = BodyStatus.OnGround;
|
||||
component.FixedRotation = true;
|
||||
}
|
||||
|
||||
private void HandleShuttleShutdown(EntityUid uid, ShuttleComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (!component.Owner.TryGetComponent(out PhysicsComponent? physicsComponent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Disable(physicsComponent);
|
||||
|
||||
foreach (var fixture in physicsComponent.Fixtures)
|
||||
{
|
||||
fixture.Mass = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user