Cargo economy balance (#11123)
Co-authored-by: Visne <39844191+Visne@users.noreply.github.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
144
Content.Server/UserInterface/StatValuesCommand.cs
Normal file
144
Content.Server/UserInterface/StatValuesCommand.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
5
Content.Server/UserInterface/StatValuesEui.cs
Normal file
5
Content.Server/UserInterface/StatValuesEui.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
using Content.Server.EUI;
|
||||
|
||||
namespace Content.Server.UserInterface;
|
||||
|
||||
public sealed class StatValuesEui : BaseEui {}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user