Cargo economy balance (#11123)

Co-authored-by: Visne <39844191+Visne@users.noreply.github.com>
This commit is contained in:
metalgearsloth
2022-09-15 11:53:17 +10:00
committed by GitHub
parent f4c38d74e1
commit ad7a851e27
113 changed files with 615 additions and 58 deletions

View File

@@ -15,5 +15,5 @@ public sealed class StationBankAccountComponent : Component
/// How much the bank balance goes up per second, every Delay period. Rounded down when multiplied.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("increasePerSecond")]
public int IncreasePerSecond = 10;
public int IncreasePerSecond = 2;
}

View File

@@ -9,6 +9,7 @@ using Content.Shared.MobState.Components;
using Robust.Shared.Console;
using Robust.Shared.Containers;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Server.Cargo.Systems;
@@ -111,6 +112,33 @@ public sealed class PricingSystem : EntitySystem
args.Price += component.Price;
}
/// <summary>
/// Get a rough price for an entityprototype. Does not consider contained entities.
/// </summary>
public double GetEstimatedPrice(EntityPrototype prototype, IComponentFactory? factory = null)
{
IoCManager.Resolve(ref factory);
var price = 0.0;
if (prototype.Components.TryGetValue(factory.GetComponentName(typeof(StaticPriceComponent)),
out var staticPriceProto))
{
var staticComp = (StaticPriceComponent) staticPriceProto.Component;
price += staticComp.Price;
}
if (prototype.Components.TryGetValue(factory.GetComponentName(typeof(StackPriceComponent)), out var stackpriceProto) &&
prototype.Components.TryGetValue(factory.GetComponentName(typeof(StackComponent)), out var stackProto))
{
var stackPrice = (StackPriceComponent) stackpriceProto.Component;
var stack = (StackComponent) stackProto.Component;
price += stack.Count * stackPrice.Price;
}
return price;
}
/// <summary>
/// Appraises an entity, returning it's price.
/// </summary>
@@ -123,7 +151,7 @@ public sealed class PricingSystem : EntitySystem
public double GetPrice(EntityUid uid)
{
var ev = new PriceCalculationEvent();
RaiseLocalEvent(uid, ref ev, true);
RaiseLocalEvent(uid, ref ev);
//TODO: Add an OpaqueToAppraisal component or similar for blocking the recursive descent into containers, or preventing material pricing.

View File

@@ -15,6 +15,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Player;
using JetBrains.Annotations;
using System.Linq;
using Content.Server.Cargo.Systems;
using Content.Server.Power.Components;
using Robust.Server.Player;

View File

@@ -0,0 +1,144 @@
using Content.Server.Administration;
using Content.Server.Cargo.Systems;
using Content.Server.EUI;
using Content.Shared.Administration;
using Content.Shared.Materials;
using Content.Shared.Research.Prototypes;
using Content.Shared.UserInterface;
using Robust.Server.Player;
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
namespace Content.Server.UserInterface;
[AdminCommand(AdminFlags.Debug)]
public sealed class StatValuesCommand : IConsoleCommand
{
public string Command => "showvalues";
public string Description => "Dumps all stats for a particular category into a table.";
public string Help => $"{Command} <cargosell / lathsell>";
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (shell.Player is not IPlayerSession pSession)
{
shell.WriteError($"{Command} can't be run on server!");
return;
}
if (args.Length != 1)
{
shell.WriteError($"Invalid number of args, need 1");
return;
}
StatValuesEuiMessage message;
switch (args[0])
{
case "cargosell":
message = GetCargo();
break;
case "lathesell":
message = GetLatheMessage();
break;
default:
shell.WriteError($"{args[0]} is not a valid stat!");
return;
}
var euiManager = IoCManager.Resolve<EuiManager>();
var eui = new StatValuesEui();
euiManager.OpenEui(eui, pSession);
eui.SendMessage(message);
}
private StatValuesEuiMessage GetCargo()
{
// Okay so there's no easy way to do this with how pricing works
// So we'll just get the first value for each prototype ID which is probably good enough for the majority.
var values = new List<string[]>();
var entManager = IoCManager.Resolve<IEntityManager>();
var priceSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<PricingSystem>();
var metaQuery = entManager.GetEntityQuery<MetaDataComponent>();
var prices = new HashSet<string>(256);
foreach (var entity in entManager.GetEntities())
{
if (!metaQuery.TryGetComponent(entity, out var meta))
continue;
var id = meta.EntityPrototype?.ID;
// We'll add it even if we don't have it so we don't have to raise the event again because this is probably faster.
if (id == null || !prices.Add(id))
continue;
var price = priceSystem.GetPrice(entity);
if (price == 0)
continue;
values.Add(new string[]
{
id,
$"{price:0}",
});
}
var state = new StatValuesEuiMessage()
{
Title = "Cargo sell prices",
Headers = new List<string>()
{
"ID",
"Price",
},
Values = values,
};
return state;
}
private StatValuesEuiMessage GetLatheMessage()
{
var values = new List<string[]>();
var protoManager = IoCManager.Resolve<IPrototypeManager>();
var factory = IoCManager.Resolve<IComponentFactory>();
var priceSystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<PricingSystem>();
foreach (var proto in protoManager.EnumeratePrototypes<LatheRecipePrototype>())
{
var cost = 0.0;
foreach (var (material, count) in proto.RequiredMaterials)
{
var materialPrice = protoManager.Index<MaterialPrototype>(material).Price;
cost += materialPrice * count;
}
var sell = priceSystem.GetEstimatedPrice(protoManager.Index<EntityPrototype>(proto.Result), factory);
values.Add(new[]
{
proto.ID,
$"{cost:0}",
$"{sell:0}",
});
}
var state = new StatValuesEuiMessage()
{
Title = "Lathe sell prices",
Headers = new List<string>()
{
"ID",
"Cost",
"Sell price",
},
Values = values,
};
return state;
}
}

View File

@@ -0,0 +1,5 @@
using Content.Server.EUI;
namespace Content.Server.UserInterface;
public sealed class StatValuesEui : BaseEui {}

View File

@@ -1,3 +1,4 @@
using Content.Server.Cargo.Systems;
using Content.Server.Popups;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
@@ -21,24 +22,30 @@ namespace Content.Server.VendingMachines
{
public sealed class VendingMachineSystem : SharedVendingMachineSystem
{
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
[Dependency] private readonly SharedActionsSystem _action = default!;
[Dependency] private readonly AudioSystem _audioSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly AudioSystem _audioSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedActionsSystem _action = default!;
[Dependency] private readonly PricingSystem _pricing = default!;
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
private ISawmill _sawmill = default!;
public override void Initialize()
{
base.Initialize();
_sawmill = Logger.GetSawmill("vending");
SubscribeLocalEvent<VendingMachineComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<VendingMachineComponent, BreakageEventArgs>(OnBreak);
SubscribeLocalEvent<VendingMachineComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<VendingMachineComponent, DamageChangedEvent>(OnDamage);
SubscribeLocalEvent<VendingMachineComponent, PriceCalculationEvent>(OnVendingPrice);
SubscribeLocalEvent<VendingMachineComponent, ActivatableUIOpenAttemptEvent>(OnActivatableUIOpenAttempt);
SubscribeLocalEvent<VendingMachineComponent, BoundUIOpenedEvent>(OnBoundUIOpened);
@@ -47,6 +54,24 @@ namespace Content.Server.VendingMachines
SubscribeLocalEvent<VendingMachineComponent, VendingMachineSelfDispenseEvent>(OnSelfDispense);
}
private void OnVendingPrice(EntityUid uid, VendingMachineComponent component, ref PriceCalculationEvent args)
{
var price = 0.0;
foreach (var (id, entry) in component.Inventory)
{
if (!_prototypeManager.TryIndex<EntityPrototype>(entry.ID, out var proto))
{
_sawmill.Error($"Unable to find entity prototype {entry.ID} on {ToPrettyString(uid)} vending.");
continue;
}
price += entry.Amount * _pricing.GetEstimatedPrice(proto, _factory);
}
args.Price += price;
}
protected override void OnComponentInit(EntityUid uid, SharedVendingMachineComponent sharedComponent, ComponentInit args)
{
base.OnComponentInit(uid, sharedComponent, args);
@@ -113,7 +138,7 @@ namespace Content.Server.VendingMachines
component.Emagged = true;
args.Handled = true;
}
private void OnDamage(EntityUid uid, VendingMachineComponent component, DamageChangedEvent args)
{
if (component.Broken || component.DispenseOnHitCoolingDown ||
@@ -320,7 +345,7 @@ namespace Content.Server.VendingMachines
vendComponent.ThrowNextItem = false;
return;
}
var ent = EntityManager.SpawnEntity(vendComponent.NextItemToEject, Transform(vendComponent.Owner).Coordinates);
if (vendComponent.ThrowNextItem)
{

View File

@@ -1,4 +1,5 @@
using System.Linq;
using Content.Server.Cargo.Systems;
using Content.Server.Damage.Systems;
using Content.Server.Examine;
using Content.Server.Interaction;
@@ -23,6 +24,7 @@ using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using SharedGunSystem = Content.Shared.Weapons.Ranged.Systems.SharedGunSystem;
@@ -33,12 +35,35 @@ public sealed partial class GunSystem : SharedGunSystem
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly ExamineSystem _examine = default!;
[Dependency] private readonly InteractionSystem _interaction = default!;
[Dependency] private readonly PricingSystem _pricing = default!;
[Dependency] private readonly StaminaSystem _stamina = default!;
[Dependency] private readonly StunSystem _stun = default!;
public const float DamagePitchVariation = MeleeWeaponSystem.DamagePitchVariation;
public const float GunClumsyChance = 0.5f;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BallisticAmmoProviderComponent, PriceCalculationEvent>(OnBallisticPrice);
}
private void OnBallisticPrice(EntityUid uid, BallisticAmmoProviderComponent component, ref PriceCalculationEvent args)
{
if (string.IsNullOrEmpty(component.FillProto) || component.UnspawnedCount == 0)
return;
if (!ProtoManager.TryIndex<EntityPrototype>(component.FillProto, out var proto))
{
Sawmill.Error($"Unable to find fill prototype for price on {component.FillProto} on {ToPrettyString(uid)}");
return;
}
// Probably good enough for most.
var price = _pricing.GetEstimatedPrice(proto);
args.Price += price * component.UnspawnedCount;
}
public override void Shoot(GunComponent gun, List<IShootable> ammo, EntityCoordinates fromCoordinates, EntityCoordinates toCoordinates, EntityUid? user = null)
{
// Try a clumsy roll