Skip to content
This repository has been archived by the owner on Nov 1, 2024. It is now read-only.

Commit

Permalink
Add an item appraisal cartridge for PDAs (new-frontiers-14#2045)
Browse files Browse the repository at this point in the history
* Add an item appraisal cartridge for PDAs.

* Move cartridge localization lines into their own file under _NF hierarchy. Add localizations for PDA UI labels

* Add appraisal cartridge to Astro Vend

---------

Co-authored-by: eidolon <[email protected]>
Co-authored-by: Dvir <[email protected]>
  • Loading branch information
3 people authored Oct 13, 2024
1 parent 55c5b0d commit d543a72
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 0 deletions.
29 changes: 29 additions & 0 deletions Content.Client/_NF/CartridgeLoader/Cartridges/AppraisalUi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Content.Client.UserInterface.Fragments;
using Content.Shared.CartridgeLoader.Cartridges;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;

namespace Content.Client._NF.CartridgeLoader.Cartridges;

public sealed partial class AppraisalUi : UIFragment
{
private AppraisalUiFragment? _fragment;

public override Control GetUIFragmentRoot()
{
return _fragment!;
}

public override void Setup(BoundUserInterface userInterface, EntityUid? fragmentOwner)
{
_fragment = new AppraisalUiFragment();
}

public override void UpdateState(BoundUserInterfaceState state)
{
if (state is not AppraisalUiState appraisalUiState)
return;

_fragment?.UpdateState(appraisalUiState.AppraisedItems);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<cartridges:AppraisalUiFragment xmlns:cartridges="clr-namespace:Content.Client._NF.CartridgeLoader.Cartridges"
xmlns="https://spacestation14.io" Margin="1 0 2 0">
<PanelContainer StyleClasses="BackgroundDark"></PanelContainer>
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
<BoxContainer Orientation="Vertical" MinHeight="32" MaxHeight="32" VerticalAlignment="Top" Margin="3 0">
<PanelContainer Name="HeaderPanel">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="4">
<Label HorizontalExpand="True" Text="{Loc 'appraisal-label-name'}"/>
<Label HorizontalExpand="True" Text="{Loc 'appraisal-label-price'}"/>
</BoxContainer>
</PanelContainer>
</BoxContainer>
<ScrollContainer Name="ScrollContainer" HorizontalExpand="True" VerticalExpand="True">
<BoxContainer Orientation="Vertical" Name="AppraisedItemContainer" HorizontalExpand="True" VerticalExpand="True" VerticalAlignment="Top" Margin="3 0"/>
</ScrollContainer>
</BoxContainer>
</cartridges:AppraisalUiFragment>
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Content.Shared.CartridgeLoader.Cartridges;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;

namespace Content.Client._NF.CartridgeLoader.Cartridges;

[GenerateTypedNameReferences]
public sealed partial class AppraisalUiFragment : BoxContainer
{
private readonly StyleBoxFlat _styleBox = new()
{
BackgroundColor = Color.Transparent,
BorderColor = Color.FromHex("#5a5a5a"),
BorderThickness = new Thickness(0, 0, 0, 1)
};

public AppraisalUiFragment()
{
RobustXamlLoader.Load(this);
Orientation = LayoutOrientation.Vertical;
HorizontalExpand = true;
VerticalExpand = true;
HeaderPanel.PanelOverride = _styleBox;
}

public void UpdateState(List<AppraisedItem> items)
{
AppraisedItemContainer.RemoveAllChildren();

//Reverse the list so the oldest entries appear at the bottom
items.Reverse();

//Enable scrolling if there are more entries that can fit on the screen
ScrollContainer.HScrollEnabled = items.Count > 9;

foreach (var item in items)
{
AddAppraisedItem(item);
}
}

private void AddAppraisedItem(AppraisedItem item)
{
var row = new BoxContainer();
row.HorizontalExpand = true;
row.Orientation = LayoutOrientation.Horizontal;
row.Margin = new Thickness(4);

var nameLabel = new Label();
nameLabel.Text = item.Name;
nameLabel.HorizontalExpand = true;
nameLabel.ClipText = true;
row.AddChild(nameLabel);

var valueLabel = new Label();
valueLabel.Text = item.AppraisedPrice;
valueLabel.HorizontalExpand = true;
valueLabel.ClipText = true;
row.AddChild(valueLabel);

AppraisedItemContainer.AddChild(row);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Content.Shared.CartridgeLoader.Cartridges;
using Robust.Shared.Audio;

namespace Content.Server.CartridgeLoader.Cartridges;

[RegisterComponent]
public sealed partial class AppraisalCartridgeComponent : Component
{
/// <summary>
/// The list of appraised items
/// </summary>
[DataField("appraisedItems")]
public List<AppraisedItem> AppraisedItems = new();

/// <summary>
/// Limits the amount of items that can be saved
/// </summary>
[DataField("maxSavedItems")]
public int MaxSavedItems { get; set; } = 9;

[DataField("soundScan")]
public SoundSpecifier SoundScan = new SoundPathSpecifier("/Audio/Machines/scan_finish.ogg");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using Content.Server.Cargo.Systems;
using Content.Shared.Audio;
using Content.Shared.CartridgeLoader;
using Content.Shared.CartridgeLoader.Cartridges;
using Content.Shared.Popups;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Content.Server.Cargo.Components;
using Content.Shared.Timing;

namespace Content.Server.CartridgeLoader.Cartridges;

public sealed class AppraisalCartridgeSystem : EntitySystem
{
[Dependency] private readonly CargoSystem _bountySystem = default!;
[Dependency] private readonly CartridgeLoaderSystem? _cartridgeLoaderSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly PricingSystem _pricingSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AppraisalCartridgeComponent, CartridgeUiReadyEvent>(OnUiReady);
SubscribeLocalEvent<AppraisalCartridgeComponent, CartridgeAfterInteractEvent>(AfterInteract);
SubscribeLocalEvent<AppraisalCartridgeComponent, CartridgeActivatedEvent>(OnCartridgeActivated);
SubscribeLocalEvent<AppraisalCartridgeComponent, CartridgeDeactivatedEvent>(OnCartridgeDeactivated);
}

// Kinda jank, but easiest way to get the right-click Appraisal verb to also work.
// I'd much rather pass the GetUtilityVerb event through to the AppraisalCartridgeSystem and have all of
// the functionality in there, rather than adding a PriceGunComponent to the PDA itself, but getting
// that passthrough to work is not a straightforward thing.

// Because of this weird workaround, items appraised with the right-click utility verb don't get added
// to the history in the UI. That'll be something to revisit someday if anyone notices and complains :P

// Doing this on cartridge activation and deactivation rather than install and remove so that the price
// gun functionality is only there when the program is active.
private void OnCartridgeActivated(Entity<AppraisalCartridgeComponent> ent, ref CartridgeActivatedEvent args)
{
EnsureComp<PriceGunComponent>(args.Loader);
// PriceGunSystem methods exit early if a DelayComponent is not present
EnsureComp<UseDelayComponent>(args.Loader);
}

private void OnCartridgeDeactivated(Entity<AppraisalCartridgeComponent> ent, ref CartridgeDeactivatedEvent args)
{
var parent = Transform(args.Loader).ParentUid;
RemComp<PriceGunComponent>(parent);
RemComp<UseDelayComponent>(parent);
}

/// <summary>
/// The <see cref="CartridgeAfterInteractEvent" /> gets relayed to this system if the cartridge loader is running
/// the Appraisal program and someone clicks on something with it. <br/>
/// <br/>
/// Does the thing... TODO
/// </summary>
private void AfterInteract(EntityUid uid, AppraisalCartridgeComponent component, CartridgeAfterInteractEvent args)
{
if (args.InteractEvent.Handled || !args.InteractEvent.CanReach || !args.InteractEvent.Target.HasValue)
return;

var target = args.InteractEvent.Target;
var who = args.InteractEvent.User;
double price = 0.00;

// All of the pop up display stuff is being handled by the PriceGunComponent addded to the PDA,
// all we're doing in here is getting the price and recording it to the PDA interface bit.
price = _pricingSystem.GetPrice(target.Value);

//Limit the amount of saved probe results to 9
//This is hardcoded because the UI doesn't support a dynamic number of results
if (component.AppraisedItems.Count >= component.MaxSavedItems)
component.AppraisedItems.RemoveAt(0);

var item = new AppraisedItem(
Name(target.Value),
price.ToString("0.00")
);

component.AppraisedItems.Add(item);
UpdateUiState(uid, args.Loader, component);
}

/// <summary>
/// This gets called when the ui fragment needs to be updated for the first time after activating
/// </summary>
private void OnUiReady(EntityUid uid, AppraisalCartridgeComponent component, CartridgeUiReadyEvent args)
{
UpdateUiState(uid, args.Loader, component);
}

private void UpdateUiState(EntityUid uid, EntityUid loaderUid, AppraisalCartridgeComponent? component)
{
if (!Resolve(uid, ref component))
return;

var state = new AppraisalUiState(component.AppraisedItems);
_cartridgeLoaderSystem?.UpdateCartridgeUiState(loaderUid, state);
}
}
30 changes: 30 additions & 0 deletions Content.Shared/_NF/CartridgeLoader/Cartridges/AppraisalUiState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Robust.Shared.Serialization;

namespace Content.Shared.CartridgeLoader.Cartridges;

[Serializable, NetSerializable]
public sealed class AppraisalUiState : BoundUserInterfaceState
{
/// <summary>
/// The list of appraised items
/// </summary>
public List<AppraisedItem> AppraisedItems;

public AppraisalUiState(List<AppraisedItem> appraisedItems)
{
AppraisedItems = appraisedItems;
}
}

[Serializable, NetSerializable, DataRecord]
public sealed class AppraisedItem
{
public readonly string Name;
public readonly string AppraisedPrice;

public AppraisedItem(string name, string appraisedPrice)
{
Name = name;
AppraisedPrice = appraisedPrice;
}
}
4 changes: 4 additions & 0 deletions Resources/Locale/en-US/_NF/cartridge-loader/cartridges.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Appraisal cartridge
appraisal-program-name = Appraisal App Plus
appraisal-label-name = Item
appraisal-label-price = Appraised Price
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
JetpackMiniFilled: 10
JetpackBlue: 10
HandHeldMassScanner: 10
AppraisalCartridge: 3
# Keys
EncryptionKeyCommon: 3
EncryptionKeyTraffic: 3
Expand Down Expand Up @@ -53,6 +54,7 @@
JetpackMiniFilled: 4294967295 # Infinite
JetpackBlue: 4294967295 # Infinite
HandHeldMassScanner: 4294967295 # Infinite
AppraisalCartridge: 4294967295 # Infinite
# Keys
EncryptionKeyCommon: 4294967295 # Infinite
EncryptionKeyTraffic: 4294967295 # Infinite
Expand Down
19 changes: 19 additions & 0 deletions Resources/Prototypes/_NF/Entities/Objects/Devices/cartridges.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@
- type: AccessReader
access: [["HeadOfSecurity"], ["HeadOfPersonnel"]]

- type: entity
parent: BaseItem
id: AppraisalCartridge
name: appraisal cartridge
description: A program for appraising the monetary value of items
components:
- type: Sprite
sprite: Objects/Devices/cartridge.rsi
state: cart-y
- type: Icon
sprite: Objects/Devices/cartridge.rsi
state: cart-y
- type: UIFragment
ui: !type:AppraisalUi
- type: Cartridge
programName: appraisal-program-name
icon: Interface/Actions/shop.png
- type: AppraisalCartridge

# Not a PDA cartridge (then why is this here)
- type: entity
parent: BaseItem
Expand Down

0 comments on commit d543a72

Please sign in to comment.