Skip to content

Commit

Permalink
shipyard rewrite/port (DeltaV-Station#1171)
Browse files Browse the repository at this point in the history
* add shipyard code

* add shipyard preferred dock

* shipyard locale

* add the computer itself

* add all the pre rebase shuttles

* name the shuttle

* tweak all shuttle maps + fix test

* add some categories for future use

* rename nanotrasen to civilian

* update for new ui system and move remaining yaml into deltav folder

* good

* write tests

* wow

* helix price hike

* fix dirty dispose

* use deltav cvar file

* s

Signed-off-by: deltanedas <39013340+deltanedas@users.noreply.github.com>

---------

Signed-off-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: deltanedas <@deltanedas:kde.org>
Co-authored-by: Null <56081759+NullWanderer@users.noreply.github.com>
  • Loading branch information
deltanedas and NullWanderer authored May 15, 2024
1 parent 7e3ba62 commit 9291c35
Show file tree
Hide file tree
Showing 31 changed files with 17,236 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Content.Client/DeltaV/Shipyard/ShipyardConsoleSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Content.Shared.Shipyard;

namespace Content.Client.Shipyard;

public sealed class ShipyardConsoleSystem : SharedShipyardConsoleSystem;
56 changes: 56 additions & 0 deletions Content.Client/DeltaV/Shipyard/UI/ShipyardBoundUserInterface.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Content.Shared.Access.Systems;
using Content.Shared.Shipyard;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Shared.Prototypes;

namespace Content.Client.DeltaV.Shipyard.UI;

public sealed class ShipyardConsoleBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IPlayerManager _player = default!;

private readonly AccessReaderSystem _access;

[ViewVariables]
private ShipyardConsoleMenu? _menu;

public ShipyardConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
_access = EntMan.System<AccessReaderSystem>();
}

protected override void Open()
{
base.Open();

_menu = new ShipyardConsoleMenu(Owner, _proto, EntMan, _player, _access);
_menu.OpenCentered();
_menu.OnClose += Close;
_menu.OnPurchased += Purchase;
}

protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);

if (state is not ShipyardConsoleState cast)
return;

_menu?.UpdateState(cast);
}

protected override void Dispose(bool disposing)
{
base.Dispose(disposing);

if (disposing)
_menu?.Dispose();
}

private void Purchase(string id)
{
SendMessage(new ShipyardConsolePurchaseMessage(id));
}
}
29 changes: 29 additions & 0 deletions Content.Client/DeltaV/Shipyard/UI/ShipyardConsoleMenu.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
SetSize="500 360"
MinSize="460 280"
Title="{Loc 'shipyard-console-menu-title'}">
<BoxContainer Orientation="Vertical" Margin="5 0 5 0">
<Label Name="BankAccountLabel" />
<BoxContainer Orientation="Horizontal">
<OptionButton Name="Categories"
Prefix="{Loc 'cargo-console-menu-categories-label'}"
HorizontalExpand="True" />
<LineEdit Name="SearchBar"
PlaceHolder="{Loc 'cargo-console-menu-search-bar-placeholder'}"
HorizontalExpand="True" />
</BoxContainer>
<ScrollContainer HorizontalExpand="True"
VerticalExpand="True"
SizeFlagsStretchRatio="6">
<BoxContainer Name="Vessels"
Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True">
<!-- Vessels get added here by code -->
</BoxContainer>
</ScrollContainer>
<TextureButton VerticalExpand="True" />
</BoxContainer>
</controls:FancyWindow>
110 changes: 110 additions & 0 deletions Content.Client/DeltaV/Shipyard/UI/ShipyardConsoleMenu.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Access.Systems;
using Content.Shared.Shipyard;
using Content.Shared.Shipyard.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;

namespace Content.Client.DeltaV.Shipyard.UI;

[GenerateTypedNameReferences]
public sealed partial class ShipyardConsoleMenu : FancyWindow
{
private readonly AccessReaderSystem _access;
private readonly IPlayerManager _player;

public event Action<string>? OnPurchased;

private readonly List<VesselPrototype> _vessels = new();
private readonly List<string> _categories = new();

public Entity<ShipyardConsoleComponent> Console;
private string? _category;

public ShipyardConsoleMenu(EntityUid console, IPrototypeManager proto, IEntityManager entMan, IPlayerManager player, AccessReaderSystem access)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);

Console = (console, entMan.GetComponent<ShipyardConsoleComponent>(console));
_access = access;
_player = player;

// don't include ships that aren't allowed by whitelist, server won't accept them anyway
foreach (var vessel in proto.EnumeratePrototypes<VesselPrototype>())
{
if (vessel.Whitelist?.IsValid(console, entMan) != false)
_vessels.Add(vessel);
}
_vessels.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.CurrentCultureIgnoreCase));

// only list categories in said ships
foreach (var vessel in _vessels)
{
foreach (var category in vessel.Categories)
{
if (!_categories.Contains(category))
_categories.Add(category);
}
}

_categories.Sort();
// inserting here and not adding at the start so it doesn't get affected by sort
_categories.Insert(0, Loc.GetString("cargo-console-menu-populate-categories-all-text"));
PopulateCategories();

SearchBar.OnTextChanged += _ => PopulateProducts();
Categories.OnItemSelected += args =>
{
_category = args.Id == 0 ? null : _categories[args.Id];
Categories.SelectId(args.Id);
PopulateProducts();
};
}

/// <summary>
/// Populates the list of products that will actually be shown, using the current filters.
/// </summary>
private void PopulateProducts()
{
Vessels.RemoveAllChildren();

var access = _player.LocalSession?.AttachedEntity is {} player
&& _access.IsAllowed(player, Console);

var search = SearchBar.Text.Trim().ToLowerInvariant();
foreach (var vessel in _vessels)
{
if (search.Length != 0 && !vessel.Name.ToLowerInvariant().Contains(search))
continue;
if (_category != null && !vessel.Categories.Contains(_category))
continue;

var vesselEntry = new VesselRow(vessel, access);
vesselEntry.OnPurchasePressed += () => OnPurchased?.Invoke(vessel.ID);
Vessels.AddChild(vesselEntry);
}
}

/// <summary>
/// Populates the list categories that will actually be shown, using the current filters.
/// </summary>
private void PopulateCategories()
{
Categories.Clear();
foreach (var category in _categories)
{
Categories.AddItem(category);
}
}

public void UpdateState(ShipyardConsoleState state)
{
BankAccountLabel.Text = Loc.GetString("cargo-console-menu-points-amount", ("amount", state.Balance.ToString()));
PopulateProducts();
}
}
16 changes: 16 additions & 0 deletions Content.Client/DeltaV/Shipyard/UI/VesselRow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<PanelContainer xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
HorizontalExpand="True">
<BoxContainer Orientation="Horizontal"
HorizontalExpand="True">
<Button Name="Purchase" Text="{Loc 'purchase'}" StyleClasses="LabelSubText" />
<Label Name="VesselName" HorizontalExpand="True" />
<PanelContainer>
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#25252A" />
</PanelContainer.PanelOverride>

<Label Name="Price" MinSize="52 32" Align="Right" />
</PanelContainer>
</BoxContainer>
</PanelContainer>
29 changes: 29 additions & 0 deletions Content.Client/DeltaV/Shipyard/UI/VesselRow.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Content.Shared.Shipyard.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;

namespace Content.Client.DeltaV.Shipyard.UI;

[GenerateTypedNameReferences]
public sealed partial class VesselRow : PanelContainer
{
public event Action? OnPurchasePressed;

public VesselRow(VesselPrototype vessel, bool access)
{
RobustXamlLoader.Load(this);

VesselName.Text = vessel.Name;

var tooltip = new Tooltip();
tooltip.SetMessage(FormattedMessage.FromMarkup(vessel.Description));
Purchase.TooltipSupplier = _ => tooltip;
Purchase.Disabled = !access;
Purchase.OnPressed += _ => OnPurchasePressed?.Invoke();

Price.Text = Loc.GetString("cargo-console-menu-points-amount", ("amount", vessel.Price));
}
}
90 changes: 90 additions & 0 deletions Content.IntegrationTests/Tests/DeltaV/ShipyardTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using Content.Server.Cargo.Systems;
using Content.Server.Shipyard;
using Content.Server.Shuttles.Components;
using Content.Shared.Shipyard.Prototypes;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;

namespace Content.IntegrationTests.Tests.DeltaV;

[TestFixture]
[TestOf(typeof(ShipyardSystem))]
public sealed class ShipyardTest
{
[Test]
public async Task NoShipyardArbitrage()
{
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;

var entities = server.ResolveDependency<IEntityManager>();
var proto = server.ResolveDependency<IPrototypeManager>();
var shipyard = entities.System<ShipyardSystem>();
var pricing = entities.System<PricingSystem>();

await server.WaitAssertion(() =>
{
Assert.Multiple(() =>
{
foreach (var vessel in proto.EnumeratePrototypes<VesselPrototype>())
{
var shuttle = shipyard.TryCreateShuttle(vessel.Path.ToString());
Assert.That(shuttle, Is.Not.Null, $"Failed to spawn shuttle {vessel.ID}!");

var value = pricing.AppraiseGrid(shuttle.Value);
Assert.That(value, Is.AtMost(vessel.Price), $"Found arbitrage on shuttle {vessel.ID}! Price is {vessel.Price} but value is {value}!");
entities.DeleteEntity(shuttle);
}
});
});

await pair.CleanReturnAsync();
}

[Test]
public async Task AllShuttlesValid()
{
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;

var entities = server.ResolveDependency<IEntityManager>();
var proto = server.ResolveDependency<IPrototypeManager>();
var shipyard = entities.System<ShipyardSystem>();

await server.WaitAssertion(() =>
{
Assert.Multiple(() =>
{
foreach (var vessel in proto.EnumeratePrototypes<VesselPrototype>())
{
var shuttle = shipyard.TryCreateShuttle(vessel.Path.ToString());
Assert.That(shuttle, Is.Not.Null, $"Failed to spawn shuttle {vessel.ID}!");

var console = FindComponent<ShuttleConsoleComponent>(entities, shuttle.Value);
Assert.That(console, Is.True, $"Shuttle {vessel.ID} had no shuttle console!");

var dock = FindComponent<DockingComponent>(entities, shuttle.Value);
Assert.That(dock, Is.True, $"Shuttle {vessel.ID} had no shuttle dock!");

entities.DeleteEntity(shuttle);
}
});
});

await pair.CleanReturnAsync();
}

private bool FindComponent<T>(IEntityManager entities, EntityUid shuttle) where T: Component
{
var query = entities.EntityQueryEnumerator<T, TransformComponent>();
while (query.MoveNext(out _, out var xform))
{
if (xform.ParentUid != shuttle)
continue;

return true;
}

return false;
}
}
17 changes: 17 additions & 0 deletions Content.Server/DeltaV/Shipyard/MapDeleterShuttleComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Content.Server.Shipyard;

/// <summary>
/// When added to a shuttle, once it FTLs the previous map is deleted.
/// After that the component is removed to prevent insane abuse.
/// </summary>
/// <remarks>
/// Could be upstreamed at some point, loneop shuttle could use it.
/// </remarks>
[RegisterComponent, Access(typeof(MapDeleterShuttleSystem))]
public sealed partial class MapDeleterShuttleComponent : Component
{
/// <summary>
/// Only set by the system to prevent someone in VV deleting maps by mistake or otherwise.
/// </summary>
public bool Enabled;
}
25 changes: 25 additions & 0 deletions Content.Server/DeltaV/Shipyard/MapDeleterShuttleSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Content.Server.Shuttles.Events;

namespace Content.Server.Shipyard;

public sealed class MapDeleterShuttleSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<MapDeleterShuttleComponent, FTLStartedEvent>(OnFTLStarted);
}

private void OnFTLStarted(Entity<MapDeleterShuttleComponent> ent, ref FTLStartedEvent args)
{
if (ent.Comp.Enabled)
Del(args.FromMapUid);
RemComp<MapDeleterShuttleComponent>(ent); // prevent the shuttle becoming a WMD
}

public void Enable(EntityUid shuttle)
{
EnsureComp<MapDeleterShuttleComponent>(shuttle).Enabled = true;
}
}
Loading

0 comments on commit 9291c35

Please sign in to comment.