VendingMachineRestockTest.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. #nullable enable
  2. using System.Collections.Generic;
  3. using Content.Server.VendingMachines;
  4. using Content.Server.Wires;
  5. using Content.Shared.Cargo.Prototypes;
  6. using Content.Shared.Damage;
  7. using Content.Shared.Damage.Prototypes;
  8. using Content.Shared.Prototypes;
  9. using Content.Shared.Storage.Components;
  10. using Content.Shared.VendingMachines;
  11. using Content.Shared.Wires;
  12. using Robust.Shared.GameObjects;
  13. using Robust.Shared.Map;
  14. using Robust.Shared.Prototypes;
  15. namespace Content.IntegrationTests.Tests
  16. {
  17. [TestFixture]
  18. [TestOf(typeof(VendingMachineRestockComponent))]
  19. [TestOf(typeof(VendingMachineSystem))]
  20. public sealed class VendingMachineRestockTest : EntitySystem
  21. {
  22. [TestPrototypes]
  23. private const string Prototypes = @"
  24. - type: entity
  25. name: HumanVendingDummy
  26. id: HumanVendingDummy
  27. components:
  28. - type: Hands
  29. - type: ComplexInteraction
  30. - type: Body
  31. prototype: Human
  32. - type: entity
  33. parent: FoodSnackBase
  34. id: TestRamen
  35. name: TestRamen
  36. - type: vendingMachineInventory
  37. id: TestInventory
  38. startingInventory:
  39. TestRamen: 1
  40. - type: vendingMachineInventory
  41. id: OtherTestInventory
  42. startingInventory:
  43. TestRamen: 3
  44. - type: vendingMachineInventory
  45. id: BigTestInventory
  46. startingInventory:
  47. TestRamen: 4
  48. - type: entity
  49. parent: BaseVendingMachineRestock
  50. id: TestRestockWrong
  51. name: TestRestockWrong
  52. components:
  53. - type: VendingMachineRestock
  54. canRestock:
  55. - OtherTestInventory
  56. - type: entity
  57. parent: BaseVendingMachineRestock
  58. id: TestRestockCorrect
  59. name: TestRestockCorrect
  60. components:
  61. - type: VendingMachineRestock
  62. canRestock:
  63. - TestInventory
  64. - type: entity
  65. parent: BaseVendingMachineRestock
  66. id: TestRestockExplode
  67. name: TestRestockExplode
  68. components:
  69. - type: Damageable
  70. damageContainer: Inorganic
  71. damageModifierSet: Metallic
  72. - type: Destructible
  73. thresholds:
  74. - trigger:
  75. !type:DamageTrigger
  76. damage: 20
  77. behaviors:
  78. - !type:DumpRestockInventory
  79. - !type:DoActsBehavior
  80. acts: [ 'Destruction' ]
  81. - type: VendingMachineRestock
  82. canRestock:
  83. - BigTestInventory
  84. - type: entity
  85. parent: VendingMachine
  86. id: VendingMachineTest
  87. name: Test Ramen
  88. components:
  89. - type: Wires
  90. layoutId: Vending
  91. - type: VendingMachine
  92. pack: TestInventory
  93. - type: Sprite
  94. sprite: error.rsi
  95. ";
  96. [Test]
  97. public async Task TestAllRestocksAreAvailableToBuy()
  98. {
  99. await using var pair = await PoolManager.GetServerClient();
  100. var server = pair.Server;
  101. await server.WaitIdleAsync();
  102. var prototypeManager = server.ResolveDependency<IPrototypeManager>();
  103. var compFact = server.ResolveDependency<IComponentFactory>();
  104. await server.WaitAssertion(() =>
  105. {
  106. HashSet<string> restocks = new();
  107. Dictionary<string, List<string>> restockStores = new();
  108. // Collect all the prototypes with restock components.
  109. foreach (var proto in prototypeManager.EnumeratePrototypes<EntityPrototype>())
  110. {
  111. if (proto.Abstract
  112. || pair.IsTestPrototype(proto)
  113. || !proto.HasComponent<VendingMachineRestockComponent>())
  114. {
  115. continue;
  116. }
  117. restocks.Add(proto.ID);
  118. }
  119. // Collect all the prototypes with StorageFills referencing those entities.
  120. foreach (var proto in prototypeManager.EnumeratePrototypes<EntityPrototype>())
  121. {
  122. if (!proto.TryGetComponent<StorageFillComponent>(out var storage, compFact))
  123. continue;
  124. List<string> restockStore = new();
  125. foreach (var spawnEntry in storage.Contents)
  126. {
  127. if (spawnEntry.PrototypeId != null && restocks.Contains(spawnEntry.PrototypeId))
  128. restockStore.Add(spawnEntry.PrototypeId);
  129. }
  130. if (restockStore.Count > 0)
  131. restockStores.Add(proto.ID, restockStore);
  132. }
  133. // Iterate through every CargoProduct and make sure each
  134. // prototype with a restock component is referenced in a
  135. // purchaseable entity with a StorageFill.
  136. foreach (var proto in prototypeManager.EnumeratePrototypes<CargoProductPrototype>())
  137. {
  138. if (restockStores.ContainsKey(proto.Product))
  139. {
  140. foreach (var entry in restockStores[proto.Product])
  141. restocks.Remove(entry);
  142. restockStores.Remove(proto.Product);
  143. }
  144. }
  145. Assert.Multiple(() =>
  146. {
  147. Assert.That(restockStores, Has.Count.EqualTo(0),
  148. $"Some entities containing entities with VendingMachineRestock components are unavailable for purchase: \n - {string.Join("\n - ", restockStores.Keys)}");
  149. Assert.That(restocks, Has.Count.EqualTo(0),
  150. $"Some entities with VendingMachineRestock components are unavailable for purchase: \n - {string.Join("\n - ", restocks)}");
  151. });
  152. });
  153. await pair.CleanReturnAsync();
  154. }
  155. [Test]
  156. public async Task TestCompleteRestockProcess()
  157. {
  158. await using var pair = await PoolManager.GetServerClient();
  159. var server = pair.Server;
  160. await server.WaitIdleAsync();
  161. var mapManager = server.ResolveDependency<IMapManager>();
  162. var entityManager = server.ResolveDependency<IEntityManager>();
  163. var entitySystemManager = server.ResolveDependency<IEntitySystemManager>();
  164. EntityUid packageRight;
  165. EntityUid packageWrong;
  166. EntityUid machine;
  167. EntityUid user;
  168. VendingMachineComponent machineComponent = default!;
  169. VendingMachineRestockComponent restockRightComponent = default!;
  170. VendingMachineRestockComponent restockWrongComponent = default!;
  171. WiresPanelComponent machineWiresPanel = default!;
  172. var testMap = await pair.CreateTestMap();
  173. await server.WaitAssertion(() =>
  174. {
  175. var coordinates = testMap.GridCoords;
  176. // Spawn the entities.
  177. user = entityManager.SpawnEntity("HumanVendingDummy", coordinates);
  178. machine = entityManager.SpawnEntity("VendingMachineTest", coordinates);
  179. packageRight = entityManager.SpawnEntity("TestRestockCorrect", coordinates);
  180. packageWrong = entityManager.SpawnEntity("TestRestockWrong", coordinates);
  181. // Sanity test for components existing.
  182. Assert.Multiple(() =>
  183. {
  184. Assert.That(entityManager.TryGetComponent(machine, out machineComponent!), $"Machine has no {nameof(VendingMachineComponent)}");
  185. Assert.That(entityManager.TryGetComponent(packageRight, out restockRightComponent!), $"Correct package has no {nameof(VendingMachineRestockComponent)}");
  186. Assert.That(entityManager.TryGetComponent(packageWrong, out restockWrongComponent!), $"Wrong package has no {nameof(VendingMachineRestockComponent)}");
  187. Assert.That(entityManager.TryGetComponent(machine, out machineWiresPanel!), $"Machine has no {nameof(WiresPanelComponent)}");
  188. });
  189. var systemMachine = entitySystemManager.GetEntitySystem<VendingMachineSystem>();
  190. // Test that the panel needs to be opened first.
  191. Assert.Multiple(() =>
  192. {
  193. Assert.That(systemMachine.TryAccessMachine(packageRight, restockRightComponent, machineComponent, user, machine), Is.False, "Right package is able to restock without opened access panel");
  194. Assert.That(systemMachine.TryAccessMachine(packageWrong, restockWrongComponent, machineComponent, user, machine), Is.False, "Wrong package is able to restock without opened access panel");
  195. });
  196. var systemWires = entitySystemManager.GetEntitySystem<WiresSystem>();
  197. // Open the panel.
  198. systemWires.TogglePanel(machine, machineWiresPanel, true);
  199. Assert.Multiple(() =>
  200. {
  201. // Test that the right package works for the right machine.
  202. Assert.That(systemMachine.TryAccessMachine(packageRight, restockRightComponent, machineComponent, user, machine), Is.True, "Correct package is unable to restock with access panel opened");
  203. // Test that the wrong package does not work.
  204. Assert.That(systemMachine.TryMatchPackageToMachine(packageWrong, restockWrongComponent, machineComponent, user, machine), Is.False, "Package with invalid canRestock is able to restock machine");
  205. // Test that the right package does work.
  206. Assert.That(systemMachine.TryMatchPackageToMachine(packageRight, restockRightComponent, machineComponent, user, machine), Is.True, "Package with valid canRestock is unable to restock machine");
  207. // Make sure there's something in there to begin with.
  208. Assert.That(systemMachine.GetAvailableInventory(machine, machineComponent), Has.Count.GreaterThan(0),
  209. "Machine inventory is empty before emptying.");
  210. });
  211. // Empty the inventory.
  212. systemMachine.EjectRandom(machine, false, true, machineComponent);
  213. Assert.That(systemMachine.GetAvailableInventory(machine, machineComponent), Has.Count.EqualTo(0),
  214. "Machine inventory is not empty after ejecting.");
  215. // Test that the inventory is actually restocked.
  216. systemMachine.TryRestockInventory(machine, machineComponent);
  217. Assert.That(systemMachine.GetAvailableInventory(machine, machineComponent), Has.Count.GreaterThan(0),
  218. "Machine available inventory count is not greater than zero after restock.");
  219. mapManager.DeleteMap(testMap.MapId);
  220. });
  221. await pair.CleanReturnAsync();
  222. }
  223. [Test]
  224. public async Task TestRestockBreaksOpen()
  225. {
  226. await using var pair = await PoolManager.GetServerClient();
  227. var server = pair.Server;
  228. await server.WaitIdleAsync();
  229. var prototypeManager = server.ResolveDependency<IPrototypeManager>();
  230. var mapManager = server.ResolveDependency<IMapManager>();
  231. var entityManager = server.ResolveDependency<IEntityManager>();
  232. var entitySystemManager = server.ResolveDependency<IEntitySystemManager>();
  233. var damageableSystem = entitySystemManager.GetEntitySystem<DamageableSystem>();
  234. var testMap = await pair.CreateTestMap();
  235. EntityUid restock = default;
  236. await server.WaitAssertion(() =>
  237. {
  238. var coordinates = testMap.GridCoords;
  239. var totalStartingRamen = 0;
  240. foreach (var meta in entityManager.EntityQuery<MetaDataComponent>())
  241. if (!meta.Deleted && meta.EntityPrototype?.ID == "TestRamen")
  242. totalStartingRamen++;
  243. Assert.That(totalStartingRamen, Is.EqualTo(0),
  244. "Did not start with zero ramen.");
  245. restock = entityManager.SpawnEntity("TestRestockExplode", coordinates);
  246. var damageSpec = new DamageSpecifier(prototypeManager.Index<DamageTypePrototype>("Blunt"), 100);
  247. var damageResult = damageableSystem.TryChangeDamage(restock, damageSpec);
  248. #pragma warning disable NUnit2045
  249. Assert.That(damageResult, Is.Not.Null,
  250. "Received null damageResult when attempting to damage restock box.");
  251. Assert.That((int) damageResult!.GetTotal(), Is.GreaterThan(0),
  252. "Box damage result was not greater than 0.");
  253. #pragma warning restore NUnit2045
  254. });
  255. await server.WaitRunTicks(15);
  256. await server.WaitAssertion(() =>
  257. {
  258. Assert.That(entityManager.Deleted(restock),
  259. "Restock box was not deleted after being damaged.");
  260. var totalRamen = 0;
  261. foreach (var meta in entityManager.EntityQuery<MetaDataComponent>())
  262. if (!meta.Deleted && meta.EntityPrototype?.ID == "TestRamen")
  263. totalRamen++;
  264. Assert.That(totalRamen, Is.EqualTo(2),
  265. "Did not find enough ramen after destroying restock box.");
  266. mapManager.DeleteMap(testMap.MapId);
  267. });
  268. await pair.CleanReturnAsync();
  269. }
  270. [Test]
  271. public async Task TestRestockInventoryBounds()
  272. {
  273. await using var pair = await PoolManager.GetServerClient();
  274. var server = pair.Server;
  275. await server.WaitIdleAsync();
  276. var mapManager = server.ResolveDependency<IMapManager>();
  277. var entityManager = server.ResolveDependency<IEntityManager>();
  278. var entitySystemManager = server.ResolveDependency<IEntitySystemManager>();
  279. var vendingMachineSystem = entitySystemManager.GetEntitySystem<SharedVendingMachineSystem>();
  280. var testMap = await pair.CreateTestMap();
  281. await server.WaitAssertion(() =>
  282. {
  283. var coordinates = testMap.GridCoords;
  284. var machine = entityManager.SpawnEntity("VendingMachineTest", coordinates);
  285. Assert.That(vendingMachineSystem.GetAvailableInventory(machine), Has.Count.EqualTo(1),
  286. "Machine's available inventory did not contain one entry.");
  287. Assert.That(vendingMachineSystem.GetAvailableInventory(machine)[0].Amount, Is.EqualTo(1),
  288. "Machine's available inventory is not the expected amount.");
  289. vendingMachineSystem.RestockInventoryFromPrototype(machine);
  290. Assert.That(vendingMachineSystem.GetAvailableInventory(machine)[0].Amount, Is.EqualTo(2),
  291. "Machine's available inventory is not double its starting amount after a restock.");
  292. vendingMachineSystem.RestockInventoryFromPrototype(machine);
  293. Assert.That(vendingMachineSystem.GetAvailableInventory(machine)[0].Amount, Is.EqualTo(3),
  294. "Machine's available inventory is not triple its starting amount after two restocks.");
  295. vendingMachineSystem.RestockInventoryFromPrototype(machine);
  296. Assert.That(vendingMachineSystem.GetAvailableInventory(machine)[0].Amount, Is.EqualTo(3),
  297. "Machine's available inventory did not stay the same after a third restock.");
  298. });
  299. await pair.CleanReturnAsync();
  300. }
  301. }
  302. }
  303. #nullable disable