Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an item appraisal cartridge for PDAs #2045

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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="Item"/>
<Label HorizontalExpand="True" Text="Appraised 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;
}
}
3 changes: 3 additions & 0 deletions Resources/Locale/en-US/_NF/cargo/price-gun-component.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ pirate-contraband-price-gun-pricing-result = Skizzit's says {THE($object)} {$pri
pirate-contraband-price-gun-verb-text = Appraisal
pirate-contraband-price-gun-verb-message = Appraise {THE($object)}.
pirate-contraband-price-gun-pricing-result-none = Skizzit's doesn't have an entry for {THE($object)}.

# Appraisal cartridge
appraisal-program-name = Appraisal App Plus
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
Loading