Skipping bounties (#26537)

* add button to menu

* networking and component work

* try to add access stuff

* main functionality done

* add access lock? I think?

* remove extra line

* fix access system

* move SkipTime to StationCargoBountyDatabaseComponent

* Disable/Enable skip button based on cooldown

* remove debugging

* add access denied sound

* remove DataField tags

* dynamic timer
This commit is contained in:
Killerqu00
2024-04-10 00:18:07 +02:00
committed by GitHub
parent 682afd4ae4
commit fd067731b5
9 changed files with 132 additions and 12 deletions

View File

@@ -27,6 +27,11 @@ public sealed class CargoBountyConsoleBoundUserInterface : BoundUserInterface
SendMessage(new BountyPrintLabelMessage(id)); SendMessage(new BountyPrintLabelMessage(id));
}; };
_menu.OnSkipButtonPressed += id =>
{
SendMessage(new BountySkipMessage(id));
};
_menu.OpenCentered(); _menu.OpenCentered();
} }
@@ -37,7 +42,7 @@ public sealed class CargoBountyConsoleBoundUserInterface : BoundUserInterface
if (message is not CargoBountyConsoleState state) if (message is not CargoBountyConsoleState state)
return; return;
_menu?.UpdateEntries(state.Bounties); _menu?.UpdateEntries(state.Bounties, state.UntilNextSkip);
} }
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)

View File

@@ -13,7 +13,18 @@
</BoxContainer> </BoxContainer>
<Control MinWidth="10"/> <Control MinWidth="10"/>
<BoxContainer Orientation="Vertical" MinWidth="120"> <BoxContainer Orientation="Vertical" MinWidth="120">
<Button Name="PrintButton" Text="{Loc 'bounty-console-label-button-text'}" HorizontalExpand="False" HorizontalAlignment="Right"/> <BoxContainer Orientation="Horizontal" MinWidth="120">
<Button Name="PrintButton"
Text="{Loc 'bounty-console-label-button-text'}"
HorizontalExpand="False"
HorizontalAlignment="Right"
StyleClasses="OpenRight"/>
<Button Name="SkipButton"
Text="{Loc 'bounty-console-skip-button-text'}"
HorizontalExpand="False"
HorizontalAlignment="Right"
StyleClasses="OpenLeft"/>
</BoxContainer>
<RichTextLabel Name="IdLabel" HorizontalAlignment="Right" Margin="0 0 5 0"/> <RichTextLabel Name="IdLabel" HorizontalAlignment="Right" Margin="0 0 5 0"/>
</BoxContainer> </BoxContainer>
</BoxContainer> </BoxContainer>

View File

@@ -1,11 +1,13 @@
using Content.Client.Message; using Content.Client.Message;
using Content.Shared.Cargo; using Content.Shared.Cargo;
using Content.Shared.Cargo.Prototypes; using Content.Shared.Cargo.Prototypes;
using Content.Shared.Random;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Serilog;
namespace Content.Client.Cargo.UI; namespace Content.Client.Cargo.UI;
@@ -14,15 +16,19 @@ public sealed partial class BountyEntry : BoxContainer
{ {
[Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IPrototypeManager _prototype = default!;
public Action? OnButtonPressed; public Action? OnLabelButtonPressed;
public Action? OnSkipButtonPressed;
public TimeSpan EndTime; public TimeSpan EndTime;
public TimeSpan UntilNextSkip;
public BountyEntry(CargoBountyData bounty) public BountyEntry(CargoBountyData bounty, TimeSpan untilNextSkip)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
UntilNextSkip = untilNextSkip;
if (!_prototype.TryIndex<CargoBountyPrototype>(bounty.Bounty, out var bountyPrototype)) if (!_prototype.TryIndex<CargoBountyPrototype>(bounty.Bounty, out var bountyPrototype))
return; return;
@@ -38,6 +44,27 @@ public sealed partial class BountyEntry : BoxContainer
DescriptionLabel.SetMarkup(Loc.GetString("bounty-console-description-label", ("description", Loc.GetString(bountyPrototype.Description)))); DescriptionLabel.SetMarkup(Loc.GetString("bounty-console-description-label", ("description", Loc.GetString(bountyPrototype.Description))));
IdLabel.SetMarkup(Loc.GetString("bounty-console-id-label", ("id", bounty.Id))); IdLabel.SetMarkup(Loc.GetString("bounty-console-id-label", ("id", bounty.Id)));
PrintButton.OnPressed += _ => OnButtonPressed?.Invoke(); PrintButton.OnPressed += _ => OnLabelButtonPressed?.Invoke();
SkipButton.OnPressed += _ => OnSkipButtonPressed?.Invoke();
}
private void UpdateSkipButton(float deltaSeconds)
{
UntilNextSkip -= TimeSpan.FromSeconds(deltaSeconds);
if (UntilNextSkip > TimeSpan.Zero)
{
SkipButton.Label.Text = UntilNextSkip.ToString("mm\\:ss");
SkipButton.Disabled = true;
return;
}
SkipButton.Label.Text = Loc.GetString("bounty-console-skip-button-text");
SkipButton.Disabled = false;
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
UpdateSkipButton(args.DeltaSeconds);
} }
} }

View File

@@ -10,19 +10,21 @@ namespace Content.Client.Cargo.UI;
public sealed partial class CargoBountyMenu : FancyWindow public sealed partial class CargoBountyMenu : FancyWindow
{ {
public Action<string>? OnLabelButtonPressed; public Action<string>? OnLabelButtonPressed;
public Action<string>? OnSkipButtonPressed;
public CargoBountyMenu() public CargoBountyMenu()
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
} }
public void UpdateEntries(List<CargoBountyData> bounties) public void UpdateEntries(List<CargoBountyData> bounties, TimeSpan untilNextSkip)
{ {
BountyEntriesContainer.Children.Clear(); BountyEntriesContainer.Children.Clear();
foreach (var b in bounties) foreach (var b in bounties)
{ {
var entry = new BountyEntry(b); var entry = new BountyEntry(b, untilNextSkip);
entry.OnButtonPressed += () => OnLabelButtonPressed?.Invoke(b.Id); entry.OnLabelButtonPressed += () => OnLabelButtonPressed?.Invoke(b.Id);
entry.OnSkipButtonPressed += () => OnSkipButtonPressed?.Invoke(b.Id);
BountyEntriesContainer.AddChild(entry); BountyEntriesContainer.AddChild(entry);
} }

View File

@@ -1,4 +1,5 @@
using Content.Shared.Cargo; using Content.Shared.Cargo;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.Cargo.Components; namespace Content.Server.Cargo.Components;
@@ -32,4 +33,16 @@ public sealed partial class StationCargoBountyDatabaseComponent : Component
/// </summary> /// </summary>
[DataField] [DataField]
public HashSet<string> CheckedBounties = new(); public HashSet<string> CheckedBounties = new();
/// <summary>
/// The time at which players will be able to skip the next bounty.
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
public TimeSpan NextSkipTime = TimeSpan.Zero;
/// <summary>
/// The time between skipping bounties.
/// </summary>
[DataField]
public TimeSpan SkipDelay = TimeSpan.FromMinutes(15);
} }

View File

@@ -4,7 +4,7 @@ using Content.Server.Cargo.Components;
using Content.Server.Labels; using Content.Server.Labels;
using Content.Server.NameIdentifier; using Content.Server.NameIdentifier;
using Content.Server.Paper; using Content.Server.Paper;
using Content.Server.Station.Systems; using Content.Shared.Access.Components;
using Content.Shared.Cargo; using Content.Shared.Cargo;
using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Components;
using Content.Shared.Cargo.Prototypes; using Content.Shared.Cargo.Prototypes;
@@ -35,6 +35,7 @@ public sealed partial class CargoSystem
{ {
SubscribeLocalEvent<CargoBountyConsoleComponent, BoundUIOpenedEvent>(OnBountyConsoleOpened); SubscribeLocalEvent<CargoBountyConsoleComponent, BoundUIOpenedEvent>(OnBountyConsoleOpened);
SubscribeLocalEvent<CargoBountyConsoleComponent, BountyPrintLabelMessage>(OnPrintLabelMessage); SubscribeLocalEvent<CargoBountyConsoleComponent, BountyPrintLabelMessage>(OnPrintLabelMessage);
SubscribeLocalEvent<CargoBountyConsoleComponent, BountySkipMessage>(OnSkipBountyMessage);
SubscribeLocalEvent<CargoBountyLabelComponent, PriceCalculationEvent>(OnGetBountyPrice); SubscribeLocalEvent<CargoBountyLabelComponent, PriceCalculationEvent>(OnGetBountyPrice);
SubscribeLocalEvent<EntitySoldEvent>(OnSold); SubscribeLocalEvent<EntitySoldEvent>(OnSold);
SubscribeLocalEvent<StationCargoBountyDatabaseComponent, MapInitEvent>(OnMapInit); SubscribeLocalEvent<StationCargoBountyDatabaseComponent, MapInitEvent>(OnMapInit);
@@ -50,7 +51,8 @@ public sealed partial class CargoSystem
!TryComp<StationCargoBountyDatabaseComponent>(station, out var bountyDb)) !TryComp<StationCargoBountyDatabaseComponent>(station, out var bountyDb))
return; return;
_uiSystem.TrySetUiState(uid, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(bountyDb.Bounties)); var untilNextSkip = bountyDb.NextSkipTime - _timing.CurTime;
_uiSystem.TrySetUiState(uid, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(bountyDb.Bounties, untilNextSkip));
} }
private void OnPrintLabelMessage(EntityUid uid, CargoBountyConsoleComponent component, BountyPrintLabelMessage args) private void OnPrintLabelMessage(EntityUid uid, CargoBountyConsoleComponent component, BountyPrintLabelMessage args)
@@ -70,6 +72,37 @@ public sealed partial class CargoSystem
_audio.PlayPvs(component.PrintSound, uid); _audio.PlayPvs(component.PrintSound, uid);
} }
private void OnSkipBountyMessage(EntityUid uid, CargoBountyConsoleComponent component, BountySkipMessage args)
{
if (_station.GetOwningStation(uid) is not { } station || !TryComp<StationCargoBountyDatabaseComponent>(station, out var db))
return;
if (_timing.CurTime < db.NextSkipTime)
return;
if (!TryGetBountyFromId(station, args.BountyId, out var bounty))
return;
if (args.Session.AttachedEntity is not { Valid: true } mob)
return;
if (TryComp<AccessReaderComponent>(uid, out var accessReaderComponent) &&
!_accessReaderSystem.IsAllowed(mob, uid, accessReaderComponent))
{
_audio.PlayPvs(component.DenySound, uid);
return;
}
if (!TryRemoveBounty(station, bounty.Value))
return;
FillBountyDatabase(station);
db.NextSkipTime = _timing.CurTime + db.SkipDelay;
var untilNextSkip = db.NextSkipTime - _timing.CurTime;
_uiSystem.TrySetUiState(uid, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(db.Bounties, untilNextSkip));
_audio.PlayPvs(component.SkipSound, uid);
}
public void SetupBountyLabel(EntityUid uid, EntityUid stationId, CargoBountyData bounty, PaperComponent? paper = null, CargoBountyLabelComponent? label = null) public void SetupBountyLabel(EntityUid uid, EntityUid stationId, CargoBountyData bounty, PaperComponent? paper = null, CargoBountyLabelComponent? label = null)
{ {
if (!Resolve(uid, ref paper, ref label) || !_protoMan.TryIndex<CargoBountyPrototype>(bounty.Bounty, out var prototype)) if (!Resolve(uid, ref paper, ref label) || !_protoMan.TryIndex<CargoBountyPrototype>(bounty.Bounty, out var prototype))
@@ -431,7 +464,8 @@ public sealed partial class CargoSystem
!TryComp<StationCargoBountyDatabaseComponent>(station, out var db)) !TryComp<StationCargoBountyDatabaseComponent>(station, out var db))
continue; continue;
_uiSystem.TrySetUiState(uid, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(db.Bounties), ui: ui); var untilNextSkip = db.NextSkipTime - _timing.CurTime;
_uiSystem.TrySetUiState(uid, CargoConsoleUiKey.Bounty, new CargoBountyConsoleState(db.Bounties, untilNextSkip), ui: ui);
} }
} }

View File

@@ -32,16 +32,30 @@ public sealed partial class CargoBountyConsoleComponent : Component
/// </summary> /// </summary>
[DataField("printSound")] [DataField("printSound")]
public SoundSpecifier PrintSound = new SoundPathSpecifier("/Audio/Machines/printer.ogg"); public SoundSpecifier PrintSound = new SoundPathSpecifier("/Audio/Machines/printer.ogg");
/// <summary>
/// The sound made when the bounty is skipped.
/// </summary>
[DataField("skipSound")]
public SoundSpecifier SkipSound = new SoundPathSpecifier("/Audio/Effects/Cargo/ping.ogg");
/// <summary>
/// The sound made when bounty skipping is denied due to lacking access.
/// </summary>
[DataField("denySound")]
public SoundSpecifier DenySound = new SoundPathSpecifier("/Audio/Effects/Cargo/buzz_two.ogg");
} }
[NetSerializable, Serializable] [NetSerializable, Serializable]
public sealed class CargoBountyConsoleState : BoundUserInterfaceState public sealed class CargoBountyConsoleState : BoundUserInterfaceState
{ {
public List<CargoBountyData> Bounties; public List<CargoBountyData> Bounties;
public TimeSpan UntilNextSkip;
public CargoBountyConsoleState(List<CargoBountyData> bounties) public CargoBountyConsoleState(List<CargoBountyData> bounties, TimeSpan untilNextSkip)
{ {
Bounties = bounties; Bounties = bounties;
UntilNextSkip = untilNextSkip;
} }
} }
@@ -55,3 +69,14 @@ public sealed class BountyPrintLabelMessage : BoundUserInterfaceMessage
BountyId = bountyId; BountyId = bountyId;
} }
} }
[Serializable, NetSerializable]
public sealed class BountySkipMessage : BoundUserInterfaceMessage
{
public string BountyId;
public BountySkipMessage(string bountyId)
{
BountyId = bountyId;
}
}

View File

@@ -1,5 +1,6 @@
bounty-console-menu-title = Cargo bounty console bounty-console-menu-title = Cargo bounty console
bounty-console-label-button-text = Print label bounty-console-label-button-text = Print label
bounty-console-skip-button-text = Skip
bounty-console-time-label = Time: [color=orange]{$time}[/color] bounty-console-time-label = Time: [color=orange]{$time}[/color]
bounty-console-reward-label = Reward: [color=limegreen]${$reward}[/color] bounty-console-reward-label = Reward: [color=limegreen]${$reward}[/color]
bounty-console-manifest-label = Manifest: [color=orange]{$item}[/color] bounty-console-manifest-label = Manifest: [color=orange]{$item}[/color]

View File

@@ -791,6 +791,8 @@
radius: 1.5 radius: 1.5
energy: 1.6 energy: 1.6
color: "#b89f25" color: "#b89f25"
- type: AccessReader
access: [["Quartermaster"]]
- type: GuideHelp - type: GuideHelp
guides: guides:
- CargoBounties - CargoBounties