mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-23 00:31:20 +03:00
* Yay * Do a LOT changes in yml * and again * Finale changes * yml and locale * locale tweak * yml and charp cleanup * fix carpets * and some-some * And some * I want to believe that its last🙏🙏 * 😭 * 😭😭 * clean * fix * AAHHAHAHAHAHAH * fix * FINAL * thisguyusa... * LAST_ONE * wawa * merge2 * sdobfjibwdsjaqaksmzxsowaw2edqw84 * aoaoaooaoaao * this hardcode took 30 minutes of my life * fix aaaa * fix test * FIX BUILD * oops * oops * aaooaoaoa * fuck * oops * refactor: cleanup after merge + use protoId in KitchenSpikeComponent * refactor: fix butcherable stuff again!~ * fix: now actively checks for EdibleComponent on slicing, predicted items splitting * fix: changed knife.yaml to use Slicing tool * fix: no more sounds spam on chop-chop * refactor: cleanups * refactor: default time value for slicable * refactor: whitespaces and extract variables * fix: slicable no longer prevents InteractUsingEvent by marking handled w/o actually handling * feat: chopping trees now requires sawing tool, same for slicing logs * refactor: cleanup after merge * refactor: fix non-related PR changes * refactor: rename locid * refactor: remove unneeded red 'slice' verb name when slicing entities with mob-state. * refactor: simplify test code changes * refactor: better readability for test * refactor: linebreak for unary op * refactor: xml-doc for SliceableComponent corrected to be more fitting. * refactor: cleanup SliceableSystem some more * refactor: use HasComp instead of TryComp in AddSliceVerb * refactor: extracted methods to improve readability * refactor: remove unchecked operator - not needed when casting from uint * refactor: SliceableSystem cleanups, fix logs not being chopped due to solution splitting * fix: slice verb is now showing up even if disabled, slicing is done even if there is no solution to split, replaced direct dependency for sliceable component to food component with default solution-name * refactor: comment about RandomPredicted + toy knife now fits into clown shoes * refactor: remove EdibleComponent dependency from SliceableSystem * refactor: moved SliceableSystem into ToolRefinableSystem * refactor: basic renaming for comps * refactor: cleanup after merge * refactor: reorder methods, renaming, simplify polymorph related conditions on slicing * refactor: xml-doc + reorder yaml fields * refactor: fixing yaml + method renaming, unused usings removed * refactor: removed unused usings * refactor: fix typo * refactor: remove reference for deleted meat proto * refactor: clean up yaml after merge * refactor: fix multiple typos from mass replace * refactor: fix missed field renaming * refactor: ensure all entities that had ToolRefinable got BaseSlicingRefinable (aftermerge cleanup) * refactor: undo solution-related changes for animals.yml * refactor: fix invalid parent set for corgi + fix amount of slices for the rest of pies * refactor: revert Solution to SolutionContainerManager changes after merge * refactor: revert Solution to SolutionContainerManager changes after merge * refactor: fix invalid property name for toolMissingQualityTooltip in yml, fixed missing entity on which AttemptToolRefineEvent is raised * refactor: remove over-indent for components of BaseSlicingRefinable and for Slicing tool quality * refactor: remove over-indentation on MobCarpHolo * empty collection --------- Co-authored-by: pa.pecherskij <pa.pecherskij@interfax.ru> Co-authored-by: Fildrance <fildrance@gmail.com> Co-authored-by: iaada <iaada@users.noreply.github.com>
273 lines
9.8 KiB
C#
273 lines
9.8 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using Content.IntegrationTests.Fixtures;
|
|
using Content.Server.Cargo.Components;
|
|
using Content.Server.Cargo.Systems;
|
|
using Content.Shared.Cargo.Prototypes;
|
|
using Content.Shared.Mobs.Components;
|
|
using Content.Shared.Prototypes;
|
|
using Content.Shared.Stacks;
|
|
using Content.Shared.Whitelist;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Prototypes;
|
|
using Content.Shared.Storage;
|
|
using Content.Shared.Tools.Components;
|
|
|
|
namespace Content.IntegrationTests.Tests;
|
|
|
|
[TestFixture]
|
|
public sealed class CargoTest : GameTest
|
|
{
|
|
private static readonly HashSet<ProtoId<CargoProductPrototype>> Ignored =
|
|
[
|
|
// This is ignored because it is explicitly intended to be able to sell for more than it costs.
|
|
new("FunCrateGambling")
|
|
];
|
|
|
|
[Test]
|
|
public async Task NoCargoOrderArbitrage()
|
|
{
|
|
var pair = Pair;
|
|
var server = pair.Server;
|
|
|
|
var testMap = await pair.CreateTestMap();
|
|
|
|
var entManager = server.ResolveDependency<IEntityManager>();
|
|
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
|
var pricing = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<PricingSystem>();
|
|
|
|
await server.WaitAssertion(() =>
|
|
{
|
|
Assert.Multiple(() =>
|
|
{
|
|
foreach (var proto in protoManager.EnumeratePrototypes<CargoProductPrototype>())
|
|
{
|
|
if (Ignored.Contains(proto.ID))
|
|
continue;
|
|
|
|
var ent = entManager.SpawnEntity(proto.Product, testMap.MapCoords);
|
|
var price = pricing.GetPrice(ent);
|
|
|
|
Assert.That(price, Is.AtMost(proto.Cost), $"Found arbitrage on {proto.ID} cargo product! Cost is {proto.Cost} but sell is {price}!");
|
|
entManager.DeleteEntity(ent);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
[Test]
|
|
public async Task NoCargoBountyArbitrageTest()
|
|
{
|
|
var pair = Pair;
|
|
var server = pair.Server;
|
|
|
|
var testMap = await pair.CreateTestMap();
|
|
|
|
var entManager = server.ResolveDependency<IEntityManager>();
|
|
var mapSystem = server.System<SharedMapSystem>();
|
|
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
|
var cargo = entManager.System<CargoSystem>();
|
|
|
|
var bounties = protoManager.EnumeratePrototypes<CargoBountyPrototype>().ToList();
|
|
|
|
await server.WaitAssertion(() =>
|
|
{
|
|
var mapId = testMap.MapId;
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
foreach (var proto in protoManager.EnumeratePrototypes<CargoProductPrototype>())
|
|
{
|
|
var ent = entManager.SpawnEntity(proto.Product, new MapCoordinates(Vector2.Zero, mapId));
|
|
|
|
foreach (var bounty in bounties)
|
|
{
|
|
if (cargo.IsBountyComplete(ent, bounty))
|
|
Assert.That(proto.Cost, Is.GreaterThanOrEqualTo(bounty.Reward), $"Found arbitrage on {bounty.ID} cargo bounty! Product {proto.ID} costs {proto.Cost} but fulfills bounty {bounty.ID} with reward {bounty.Reward}!");
|
|
}
|
|
|
|
entManager.DeleteEntity(ent);
|
|
}
|
|
});
|
|
|
|
mapSystem.DeleteMap(mapId);
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public async Task NoStaticPriceAndStackPrice()
|
|
{
|
|
var pair = Pair;
|
|
var server = pair.Server;
|
|
|
|
var protoManager = server.ProtoMan;
|
|
var compFact = server.ResolveDependency<IComponentFactory>();
|
|
|
|
await server.WaitAssertion(() =>
|
|
{
|
|
var protoIds = protoManager.EnumeratePrototypes<EntityPrototype>()
|
|
.Where(p => !p.Abstract)
|
|
.Where(p => !pair.IsTestPrototype(p))
|
|
.Where(p => p.Components.ContainsKey("StaticPrice"))
|
|
.ToList();
|
|
|
|
foreach (var proto in protoIds)
|
|
{
|
|
// Sanity check
|
|
Assert.That(proto.TryGetComponent<StaticPriceComponent>(out var staticPriceComp, compFact), Is.True);
|
|
|
|
if (proto.TryGetComponent<StackPriceComponent>(out var stackPriceComp, compFact) && stackPriceComp.Price > 0)
|
|
{
|
|
Assert.That(staticPriceComp.Price, Is.EqualTo(0),
|
|
$"The prototype {proto} has a StackPriceComponent and StaticPriceComponent whose values are not compatible with each other.");
|
|
}
|
|
|
|
if (proto.HasComponent<StackComponent>(compFact))
|
|
{
|
|
Assert.That(staticPriceComp.Price, Is.EqualTo(0),
|
|
$"The prototype {proto} has a StackComponent and StaticPriceComponent whose values are not compatible with each other.");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tests to see if any items that are valid for cargo bounties can be sliced into items that
|
|
/// are also valid for the same bounty entry.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task NoSliceableBountyArbitrageTest()
|
|
{
|
|
var pair = Pair;
|
|
var server = pair.Server;
|
|
|
|
var testMap = await pair.CreateTestMap();
|
|
|
|
var entManager = server.ResolveDependency<IEntityManager>();
|
|
var mapSystem = server.System<SharedMapSystem>();
|
|
var mapManager = server.ResolveDependency<IMapManager>();
|
|
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
|
var componentFactory = server.ResolveDependency<IComponentFactory>();
|
|
var whitelist = entManager.System<EntityWhitelistSystem>();
|
|
var cargo = entManager.System<CargoSystem>();
|
|
|
|
var bounties = protoManager.EnumeratePrototypes<CargoBountyPrototype>().ToList();
|
|
|
|
await server.WaitAssertion(() =>
|
|
{
|
|
var mapId = testMap.MapId;
|
|
var grid = mapManager.CreateGridEntity(mapId);
|
|
var coord = new EntityCoordinates(grid.Owner, 0, 0);
|
|
|
|
var sliceableEntityProtos = protoManager.EnumeratePrototypes<EntityPrototype>()
|
|
.Where(p => !p.Abstract)
|
|
.Where(p => !pair.IsTestPrototype(p))
|
|
.Where(p => p.TryGetComponent<ToolRefinableComponent>(out _, componentFactory))
|
|
.Select(p => p.ID)
|
|
.ToList();
|
|
|
|
foreach (var proto in sliceableEntityProtos)
|
|
{
|
|
var ent = entManager.SpawnEntity(proto, coord);
|
|
var sliceable = entManager.GetComponent<ToolRefinableComponent>(ent);
|
|
|
|
// Check each bounty
|
|
foreach (var bounty in bounties)
|
|
{
|
|
// Check each entry in the bounty
|
|
foreach (var entry in bounty.Entries)
|
|
{
|
|
// See if the entity counts as part of this bounty entry
|
|
if (!cargo.IsValidBountyEntry(ent, entry))
|
|
continue;
|
|
|
|
// Spawn a slice
|
|
|
|
var sliceCountByProtoId = EntitySpawnCollection.GetSpawns(sliceable.RefineResult)
|
|
.GroupBy(x => x)
|
|
.ToDictionary(x => x.Key, x => x.Count());
|
|
|
|
foreach (var (sliceProtoId, sliceCount) in sliceCountByProtoId)
|
|
{
|
|
var slice = entManager.SpawnEntity(sliceProtoId, coord);
|
|
|
|
// See if the slice also counts for this bounty entry
|
|
if (!cargo.IsValidBountyEntry(slice, entry))
|
|
{
|
|
entManager.DeleteEntity(slice);
|
|
continue;
|
|
}
|
|
|
|
entManager.DeleteEntity(slice);
|
|
|
|
// If for some reason it can only make one slice, that's okay, I guess
|
|
Assert.That(
|
|
sliceCount,
|
|
Is.EqualTo(1),
|
|
$"{proto} counts as part of cargo bounty {bounty.ID} "
|
|
+ $"and slices into {sliceCount} slices which count for the same bounty!"
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
entManager.DeleteEntity(ent);
|
|
}
|
|
mapSystem.DeleteMap(mapId);
|
|
});
|
|
}
|
|
|
|
[TestPrototypes]
|
|
private const string StackProto = @"
|
|
- type: stack
|
|
id: StackProto
|
|
name: stack-steel
|
|
spawn: StackEnt
|
|
|
|
- type: entity
|
|
id: StackEnt
|
|
components:
|
|
- type: StackPrice
|
|
price: 20
|
|
- type: Stack
|
|
stackType: StackProto
|
|
count: 5
|
|
";
|
|
|
|
[Test]
|
|
public async Task StackPrice()
|
|
{
|
|
var pair = Pair;
|
|
var server = pair.Server;
|
|
var entManager = server.ResolveDependency<IEntityManager>();
|
|
|
|
await server.WaitAssertion(() =>
|
|
{
|
|
var priceSystem = entManager.System<PricingSystem>();
|
|
|
|
var ent = entManager.SpawnEntity("StackEnt", MapCoordinates.Nullspace);
|
|
var price = priceSystem.GetPrice(ent);
|
|
Assert.That(price, Is.EqualTo(100.0));
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public async Task MobPrice()
|
|
{
|
|
var pair = Pair;
|
|
|
|
var componentFactory = pair.Server.ResolveDependency<IComponentFactory>();
|
|
|
|
await pair.Server.WaitAssertion(() =>
|
|
{
|
|
Assert.Multiple(() =>
|
|
{
|
|
foreach (var (proto, comp) in pair.GetPrototypesWithComponent<MobPriceComponent>())
|
|
{
|
|
Assert.That(proto.TryGetComponent<MobStateComponent>(out _, componentFactory), $"Found MobPriceComponent on {proto.ID}, but no MobStateComponent!");
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|