loadout_catalyst_regression_tests.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. #include <array>
  2. #include <cstdint>
  3. #include <cstdio>
  4. #include <optional>
  5. #include <span>
  6. #include "middleware/datagen/character_record/appearance/internal.h"
  7. #include "middleware/datagen/family4/loadout/loadout_item_resolver.h"
  8. #include "state/build_data/inventory/buckets/inventory_bucket_catalog.h"
  9. #include "state/build_data/items/catalysts/exotic_catalyst_catalog.h"
  10. #include "state/build_data/items/details/item_detail_catalog.h"
  11. #include "state/build_data/items/item_catalog.h"
  12. #include "state/build_data/runtime.h"
  13. #include "state/build_data/socket_entry_lists/socket_entry_list_catalog.h"
  14. extern int failures;
  15. namespace sunrise::state::build_data {
  16. bool find_item_definition_hash(std::uint32_t definitionHash,
  17. items::Definition& definition) noexcept {
  18. return items::find_hash(definitionHash, definition);
  19. }
  20. bool find_configured_item_detail(std::uint16_t definitionIndex,
  21. items::details::Definition& definition) noexcept {
  22. return items::details::find(definitionIndex, definition);
  23. }
  24. bool find_inventory_bucket_descriptor(
  25. std::uint8_t bucketId,
  26. inventory::buckets::Descriptor& descriptor) noexcept {
  27. return inventory::buckets::find(bucketId, descriptor);
  28. }
  29. bool find_socket_entry_list(std::uint16_t definitionIndex,
  30. socket_entry_lists::Definition& definition) noexcept {
  31. return socket_entry_lists::find(definitionIndex, definition);
  32. }
  33. bool find_socket_entry_table(std::uint16_t definitionIndex,
  34. socket_entry_lists::EntryTable& table) noexcept {
  35. return socket_entry_lists::find_entry_table(definitionIndex, table);
  36. }
  37. items::catalysts::ApplyResult complete_exotic_catalyst(
  38. std::uint16_t itemDefinitionIndex,
  39. std::uint32_t& flags,
  40. std::span<std::optional<std::uint16_t>> plugs) noexcept {
  41. return items::catalysts::apply_completed(itemDefinitionIndex, flags, plugs);
  42. }
  43. bool find_investment_constants(constants::InvestmentConstants& value) noexcept {
  44. value = {.extracted = true,
  45. .lightStatRow = 1,
  46. .characterStatRows = {2, 3, 4, 5, 6, 7}};
  47. return true;
  48. }
  49. std::uint16_t resolve_exotic_catalyst_effect(std::uint16_t itemDefinitionIndex,
  50. std::uint8_t socketLane,
  51. std::uint16_t plugDefinitionIndex) noexcept {
  52. return items::catalysts::resolve_effect(
  53. itemDefinitionIndex, socketLane, plugDefinitionIndex);
  54. }
  55. } // namespace sunrise::state::build_data
  56. namespace {
  57. namespace buckets = sunrise::state::build_data::inventory::buckets;
  58. namespace catalysts = sunrise::state::build_data::items::catalysts;
  59. namespace details = sunrise::state::build_data::items::details;
  60. namespace items = sunrise::state::build_data::items;
  61. namespace loadout = sunrise::middleware::datagen::family4::loadout;
  62. namespace appearance = sunrise::middleware::datagen::character_record::appearance;
  63. namespace character_layout = sunrise::middleware::datagen::character_record::layout;
  64. namespace socket_lists = sunrise::state::build_data::socket_entry_lists;
  65. constexpr std::uint32_t kWeaponHash = 0x12345678U;
  66. constexpr std::uint32_t kDefaultPlugHash = 0x23456789U;
  67. constexpr std::uint32_t kProgressPlugHash = 0x3456789AU;
  68. constexpr std::uint32_t kCompletedPlugHash = 0x456789ABU;
  69. constexpr std::uint32_t kEffectHash = 0x56789ABCU;
  70. constexpr std::uint32_t kCatalystCategory = 0x6789ABCDU;
  71. constexpr std::uint16_t kCatalystPerk = 77;
  72. constexpr std::uint8_t kCatalystStatRow = 24;
  73. constexpr std::int32_t kCatalystStatValue = 7;
  74. void expect(bool value, const char* label) noexcept {
  75. if (!value) {
  76. std::fprintf(stderr, "FAIL %s\n", label);
  77. ++failures;
  78. }
  79. }
  80. items::Definition item(std::uint16_t index,
  81. std::uint32_t hash,
  82. items::Tier tier = items::Tier::none) noexcept {
  83. items::Definition value{};
  84. value.definitionHash = hash;
  85. value.definitionIndex = index;
  86. value.tier = static_cast<std::uint8_t>(tier);
  87. return value;
  88. }
  89. void clear_catalogs() noexcept {
  90. catalysts::clear();
  91. socket_lists::clear();
  92. buckets::clear();
  93. details::clear();
  94. items::clear();
  95. }
  96. } // namespace
  97. void test_resolved_catalyst_output() noexcept {
  98. clear_catalogs();
  99. std::array<items::Definition, 5> itemRows{
  100. item(0, kWeaponHash, items::Tier::exotic),
  101. item(1, kDefaultPlugHash),
  102. item(2, kProgressPlugHash),
  103. item(3, kCompletedPlugHash),
  104. item(4, kEffectHash, items::Tier::exotic),
  105. };
  106. itemRows[0].bucketId = 0;
  107. itemRows[3].plugCategoryHash = kCatalystCategory;
  108. itemRows[4].plugCategoryHash = kCatalystCategory;
  109. details::Definition detail{};
  110. detail.definitionIndex = 0;
  111. detail.definitionHash = kWeaponHash;
  112. detail.bucketId = 0;
  113. detail.maxStackSize = 1;
  114. detail.instancedDefinitionState = details::InstancedDefinitionState::instanced;
  115. detail.equipmentSlot = std::int8_t{7};
  116. detail.ordinarySocketState = details::OrdinarySocketState::present;
  117. detail.ordinarySocketCount = 8;
  118. detail.initialPlugIndices[7] = 1;
  119. detail.socketEntryListIndex = 0;
  120. details::Definition effectDetail{};
  121. effectDetail.definitionIndex = 4;
  122. effectDetail.definitionHash = kEffectHash;
  123. effectDetail.bucketId = 0;
  124. effectDetail.maxStackSize = 1;
  125. effectDetail.instancedDefinitionState = details::InstancedDefinitionState::stackable;
  126. effectDetail.sandboxPerkCount = 1;
  127. effectDetail.sandboxPerks[0] = kCatalystPerk;
  128. effectDetail.statCount = 1;
  129. effectDetail.stats[0] = {kCatalystStatRow, kCatalystStatValue};
  130. const std::array detailRows{detail, effectDetail};
  131. const std::array bucketRows{
  132. buckets::Descriptor{0, buckets::ArraySelector::character, 0, 10, 7, 0},
  133. };
  134. const std::array socketListRows{
  135. socket_lists::Definition{0x12345678U, 0, 0, 0},
  136. };
  137. const std::array catalystRows{
  138. catalysts::Definition{kWeaponHash, 0, 3, 4, 7, catalysts::Availability::released, 0},
  139. };
  140. const bool ready = items::replace(itemRows) && details::replace(detailRows)
  141. && buckets::replace(bucketRows)
  142. && socket_lists::replace(socketListRows)
  143. && catalysts::replace(catalystRows);
  144. expect(ready, "resolved-output fixture publishes");
  145. if (!ready) {
  146. clear_catalogs();
  147. return;
  148. }
  149. sunrise::state::account::inventory::Item authored{};
  150. authored.instanceSoid = 1;
  151. authored.definitionHash = kWeaponHash;
  152. authored.level = 50;
  153. authored.quantity = 1;
  154. authored.flags = 3;
  155. sunrise::state::CharacterState character{};
  156. loadout::Candidate output{};
  157. expect(loadout::resolve_item(
  158. authored, character, itemRows.size(), socketListRows.size(), output),
  159. "resolved client item accepts catalyst completion");
  160. expect(output.item.flags == 7, "resolved client item carries all item-state bits");
  161. expect(output.item.instance.ordinarySockets.plugs[7] == 3,
  162. "resolved client item carries the completed catalyst plug");
  163. loadout::ResolvedInstances instances{};
  164. instances.itemCount = 1;
  165. instances.items[0].equipmentSlot = 7;
  166. instances.items[0].instance = output.item.instance;
  167. character_layout::Appearance appearanceRecord{};
  168. appearanceRecord.smallBankA.fill(character_layout::kEmptyDefinitionIndex);
  169. appearanceRecord.overflowHashes.fill(character_layout::kNoHash);
  170. appearance::apply_perk_banks(instances, appearanceRecord);
  171. expect(appearanceRecord.smallBankA[0] == kCatalystPerk,
  172. "Worldline display plug publishes the actual catalyst perk");
  173. appearance::apply_overflow_hashes(instances, appearanceRecord);
  174. expect(appearanceRecord.overflowHashes[0] == kEffectHash,
  175. "Worldline display plug publishes the actual catalyst hash");
  176. expect(appearance::apply_stats(instances, 50, appearanceRecord),
  177. "Worldline display plug builds catalyst stats");
  178. expect(appearanceRecord.weaponStats[0][0].key
  179. == static_cast<std::int8_t>(kCatalystStatRow)
  180. && appearanceRecord.weaponStats[0][0].value == kCatalystStatValue,
  181. "Worldline display plug publishes the actual catalyst stat");
  182. authored.flags = 0x8U;
  183. output.item.flags = 0xA5A5U;
  184. expect(!loadout::resolve_item(
  185. authored, character, itemRows.size(), socketListRows.size(), output),
  186. "resolved client item rejects an invalid accumulated state");
  187. expect(output.item.flags == 0xA5A5U,
  188. "failed resolved client item leaves caller output unchanged");
  189. clear_catalogs();
  190. }