Delivery system now has the possibility of penalisation (#36660)

* Add new fields to DeliveryComponent for #36636

* Setting the baseSpesoPenalty for currently available deliveries

* Small fixes

* Basic delivery penalization

* Penalty and reward multiplier calculation in place

Also fixes an issue in SharedCargoSystem when opening a delivery in dev server due to trying to allocate cargo twice.

* Calling penalty no longer happens on opening

* Extract multiplier getting

* Removing unused include

* Changing method description. \n\n Not actually sure what I meant by the first one

* Localising default delivery messages

* Unused include removal

* minor tweaks

* slarti changes

* forgot

* stuffs

* yippee

* Locn't

* doc

* Apply suggestions from code review

---------

Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Co-authored-by: ScarKy0 <scarky0@onet.eu>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
This commit is contained in:
J
2025-04-24 16:05:32 +00:00
committed by GitHub
parent 5371dbe9a7
commit 1f722bcc44
6 changed files with 127 additions and 16 deletions

View File

@@ -1,13 +1,16 @@
using Content.Server.Cargo.Systems; using Content.Server.Cargo.Systems;
using Content.Server.Chat.Systems;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
using Content.Server.StationRecords.Systems; using Content.Server.StationRecords.Systems;
using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Components;
using Content.Shared.Cargo.Prototypes;
using Content.Shared.Delivery; using Content.Shared.Delivery;
using Content.Shared.FingerprintReader; using Content.Shared.FingerprintReader;
using Content.Shared.Labels.EntitySystems; using Content.Shared.Labels.EntitySystems;
using Content.Shared.StationRecords; using Content.Shared.StationRecords;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
namespace Content.Server.Delivery; namespace Content.Server.Delivery;
@@ -25,6 +28,13 @@ public sealed partial class DeliverySystem : SharedDeliverySystem
[Dependency] private readonly FingerprintReaderSystem _fingerprintReader = default!; [Dependency] private readonly FingerprintReaderSystem _fingerprintReader = default!;
[Dependency] private readonly LabelSystem _label = default!; [Dependency] private readonly LabelSystem _label = default!;
[Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly IPrototypeManager _protoMan = default!;
/// <summary>
/// Default reason to use if the penalization is triggered
/// </summary>
private static readonly LocId DefaultMessage = "delivery-penalty-default-reason";
public override void Initialize() public override void Initialize()
{ {
@@ -39,19 +49,15 @@ public sealed partial class DeliverySystem : SharedDeliverySystem
{ {
_container.EnsureContainer<Container>(ent, ent.Comp.Container); _container.EnsureContainer<Container>(ent, ent.Comp.Container);
var stationId = _station.GetStationInMap(Transform(ent).MapID); if (_station.GetStationInMap(Transform(ent).MapID) is not { } stationId)
if (stationId == null)
return; return;
_records.TryGetRandomRecord<GeneralStationRecord>(stationId.Value, out var entry); if (!_records.TryGetRandomRecord<GeneralStationRecord>(stationId, out var entry))
if (entry == null)
return; return;
ent.Comp.RecipientName = entry.Name; ent.Comp.RecipientName = entry.Name;
ent.Comp.RecipientJobTitle = entry.JobTitle; ent.Comp.RecipientJobTitle = entry.JobTitle;
ent.Comp.RecipientStation = stationId.Value; ent.Comp.RecipientStation = stationId;
_appearance.SetData(ent, DeliveryVisuals.JobIcon, entry.JobIcon); _appearance.SetData(ent, DeliveryVisuals.JobIcon, entry.JobIcon);
@@ -73,10 +79,74 @@ public sealed partial class DeliverySystem : SharedDeliverySystem
if (!TryComp<StationBankAccountComponent>(ent.Comp.RecipientStation, out var account)) if (!TryComp<StationBankAccountComponent>(ent.Comp.RecipientStation, out var account))
return; return;
var stationAccountEnt = (ent.Comp.RecipientStation.Value, account);
var multiplier = GetDeliveryMultiplier(ent!); // Resolve so we know it's got the component
_cargo.UpdateBankAccount( _cargo.UpdateBankAccount(
(ent.Comp.RecipientStation.Value, account), stationAccountEnt,
ent.Comp.SpesoReward, (int)(ent.Comp.BaseSpesoReward * multiplier),
_cargo.CreateAccountDistribution((ent.Comp.RecipientStation.Value, account))); _cargo.CreateAccountDistribution((ent.Comp.RecipientStation.Value, account)));
}
/// <summary>
/// Runs the penalty logic: Announcing the penalty and calculating how much to charge the designated account
/// </summary>
/// <param name="ent">The delivery for which to run the penalty.</param>
/// <param name="reason">The penalty reason, displayed in front of the message.</param>
protected override void HandlePenalty(Entity<DeliveryComponent> ent, string? reason = null)
{
if (!TryComp<StationBankAccountComponent>(ent.Comp.RecipientStation, out var stationAccount))
return;
if (ent.Comp.WasPenalized)
return;
if (!_protoMan.TryIndex(ent.Comp.PenaltyBankAccount, out var accountInfo))
return;
var multiplier = GetDeliveryMultiplier(ent);
var localizedAccountName = Loc.GetString(accountInfo.Name);
reason ??= Loc.GetString(DefaultMessage);
var dist = new Dictionary<ProtoId<CargoAccountPrototype>, double>()
{
{ ent.Comp.PenaltyBankAccount, 1.0 }
};
var penaltyAccountBalance = stationAccount.Accounts[ent.Comp.PenaltyBankAccount];
var calculatedPenalty = (int)(ent.Comp.BaseSpesoPenalty * multiplier);
// Prevents cargo from going into negatives
if (calculatedPenalty > penaltyAccountBalance )
calculatedPenalty = Math.Max(0, penaltyAccountBalance);
_cargo.UpdateBankAccount(
(ent.Comp.RecipientStation.Value, stationAccount),
-calculatedPenalty,
dist);
var message = Loc.GetString("delivery-penalty-message", ("reason", reason), ("spesos", calculatedPenalty), ("account", localizedAccountName.ToUpper()));
_chat.TrySendInGameICMessage(ent, message, InGameICChatType.Speak, hideChat: true);
ent.Comp.WasPenalized = true;
DirtyField(ent.Owner, ent.Comp, nameof(DeliveryComponent.WasPenalized));
}
/// <summary>
/// Gathers the total multiplier for a delivery.
/// This is done by components having subscribed to GetDeliveryMultiplierEvent and having added onto it.
/// </summary>
/// <param name="ent">The delivery for which to get the multiplier.</param>
/// <returns>Total multiplier.</returns>
private float GetDeliveryMultiplier(Entity<DeliveryComponent> ent)
{
var ev = new GetDeliveryMultiplierEvent();
RaiseLocalEvent(ent, ref ev);
return ev.Multiplier;
} }
public override void Update(float frameTime) public override void Update(float frameTime)

View File

@@ -1,5 +1,7 @@
using Content.Shared.Cargo.Prototypes;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared.Delivery; namespace Content.Shared.Delivery;
@@ -23,10 +25,16 @@ public sealed partial class DeliveryComponent : Component
public bool IsLocked = true; public bool IsLocked = true;
/// <summary> /// <summary>
/// The amount of spesos that gets added to the station bank account on unlock. /// The base amount of spesos that gets added to the station bank account on unlock.
/// </summary> /// </summary>
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public int SpesoReward = 500; public int BaseSpesoReward = 500;
/// <summary>
/// The base amount of spesos that will be removed from the station bank account on a penalized delivery
/// </summary>
[DataField, AutoNetworkedField]
public int BaseSpesoPenalty = 250;
/// <summary> /// <summary>
/// The name of the recipient of this delivery. /// The name of the recipient of this delivery.
@@ -48,6 +56,19 @@ public sealed partial class DeliveryComponent : Component
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public EntityUid? RecipientStation; public EntityUid? RecipientStation;
/// <summary>
/// The bank account ID of the account to subtract funds from in case of penalization
/// </summary>
[DataField, AutoNetworkedField]
public ProtoId<CargoAccountPrototype> PenaltyBankAccount = "Cargo";
/// <summary>
/// Whether this delivery has already received a penalty.
/// Used to avoid getting penalized several times.
/// </summary>
[DataField, AutoNetworkedField]
public bool WasPenalized;
/// <summary> /// <summary>
/// The sound to play when the delivery is unlocked. /// The sound to play when the delivery is unlocked.
/// </summary> /// </summary>

View File

@@ -1,8 +1,6 @@
using Content.Shared.EntityTable.EntitySelectors; using Content.Shared.EntityTable.EntitySelectors;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared.Delivery; namespace Content.Shared.Delivery;
/// <summary> /// <summary>

View File

@@ -210,9 +210,21 @@ public abstract class SharedDeliverySystem : EntitySystem
protected virtual void GrantSpesoReward(Entity<DeliveryComponent?> ent) { } protected virtual void GrantSpesoReward(Entity<DeliveryComponent?> ent) { }
protected virtual void HandlePenalty(Entity<DeliveryComponent> ent, string? reason = null) { }
protected virtual void SpawnDeliveries(Entity<DeliverySpawnerComponent?> ent) { } protected virtual void SpawnDeliveries(Entity<DeliverySpawnerComponent?> ent) { }
} }
/// <summary>
/// Used to gather the multiplier from all different delivery components.
/// </summary>
[ByRefEvent]
public record struct GetDeliveryMultiplierEvent(float Multiplier)
{
// we can't use an optional parameter because the default parameterless constructor defaults everything
public GetDeliveryMultiplierEvent() : this(1.0f) { }
}
/// <summary> /// <summary>
/// Event raised on the delivery when it is unlocked. /// Event raised on the delivery when it is unlocked.
/// </summary> /// </summary>

View File

@@ -0,0 +1,4 @@
delivery-penalty-default-reason = WARNING
delivery-penalty-default-account-name = UNKNOWN ACCOUNT
delivery-penalty-message = {$reason}! INVOKING A PENALTY OF {$spesos} SPESOS ON {$account}!

View File

@@ -74,7 +74,10 @@
- type: Item - type: Item
size: Huge size: Huge
- type: Delivery - type: Delivery
spesoReward: 1000 baseSpesoReward: 1000
baseSpesoPenalty: 500
- type: Speech
speechVerb: Robotic
- type: EntityTableContainerFill - type: EntityTableContainerFill
containers: containers:
delivery: !type:NestedSelector delivery: !type:NestedSelector
@@ -111,7 +114,10 @@
- type: Item - type: Item
storedRotation: 90 storedRotation: 90
- type: Delivery - type: Delivery
spesoReward: 500 baseSpesoReward: 500
baseSpesoPenalty: 250
- type: Speech
speechVerb: Robotic
- type: EntityTableContainerFill - type: EntityTableContainerFill
containers: containers:
delivery: !type:NestedSelector delivery: !type:NestedSelector