Explorar o código

Map every lore book parent bar to its measured value index

The index a book's parent bar reads was taken from the record row's
expression at field 136. That is wrong for several books -- Ecdysis,
Trials and Tribulations, The Chronicon and For Every Rose all name an
index their bar does not read -- and for twelve more it names a flag
rather than a value, so those bars never moved however correct the
count was.

Measure them instead. A marker sweep authored distinct values across the
bank and each book displayed the one belonging to its own slot, naming
it outright; four rounds of subdivision resolved all of them. The
allocation runs in content ship order, which is what let the last few be
placed by exclusion: the Year 1 books hold a contiguous run at 1931-1941
and Letters from a Renegade filled its only gap, while Confessions
landed beside The Chronicon, both Season of Opulence.

The table is keyed by presentation node rather than by parent record.
The Lawless Frontier's parent record is named "The Tangled Shore" and
one book has no parent record at all, so the record is not a reliable
key; the node always is.

35 of 38 books mapped. Wall of Wishes and Eva's Journey have no parent
bar -- their equivalents are objectives, not bars -- and The Pigeon and
the Phoenix resolves through the derived fallback.

Still unexplained: what makes a lore book render at all. Thirteen books
show with no claims and no bar value, Confessions appeared when its bar
was filled, the field-136 gate flags are clear on books that render, and
authoring claims reduced the visible set rather than growing it. No rule
fits all four observations, so nothing here depends on one.
Millie hai 2 semanas
pai
achega
a82e2171d0

+ 48 - 26
Sunrise/src/state/record_claims/parent_bar_table.h

@@ -6,39 +6,61 @@
 namespace sunrise::state::record_claims::parent_bar_table {
 
 /**
- * The value bank index a lore book's parent-triumph bar reads.
+ * The value bank index a lore book's parent-triumph bar reads, keyed by presentation node.
  *
- * Taken from the record row's own expression at field 136, read out of the shipped tables and
- * logged from the live build. Eighteen books name a value there; the other twelve name a flag
- * instead and are absent from this table -- their bars are driven by something else entirely.
+ * Measured in game for most of these: a marker sweep authored distinct values across the bank and
+ * each book displayed the one belonging to its own slot, naming it outright. That was necessary
+ * because the expression at record field 136 names the wrong index for several books -- Ecdysis,
+ * Trials and Tribulations, The Chronicon, For Every Rose -- and names only a flag for twelve more,
+ * which is why those bars never moved however correct the count was.
  *
- * This replaces writing to the node's valueIndex, which resolves for only some books and left
- * Ecdysis, Trials and Tribulations and The Singular Exegete static despite correct counts.
+ * Keyed by node rather than by parent record: The Tangled Shore has no parent record at all, so
+ * there is nothing to key it on, yet its bar is at a measured slot like any other.
+ *
+ * The allocation runs in content ship order -- the Year 1 books hold a contiguous run at 1931-1941,
+ * Year 2 seasons follow in the 2200-2400s, Year 3 later still.
  */
 struct Bar {
-    std::uint16_t recordRow;
+    std::uint16_t nodeIndex;
     std::uint16_t valueIndex;
 };
 
-inline constexpr std::array<Bar, 18> kBars{{
-    {1588U, 2344U},  // node 823 — Stolen Intelligence
-    {1608U, 2346U},  // node 824 — The Warlock Aunor
-    {1840U, 2520U},  // node 825 — Luna's Lost
-    {1851U, 2521U},  // node 826 — Letters from Eris
-    {2003U, 2574U},  // node 828 — Constellations
-    {2209U, 2664U},  // node 829 — Duress and Egress
-    {1412U, 4619U},  // node 835 — The Book of Unmaking
-    {1578U, 2343U},  // node 836 — For Every Rose, a Thorn
-    {1797U, 2514U},  // node 839 — Unveiling
-    {1809U, 2517U},  // node 840 — Last Days on Kraken Mare
-    {1819U, 2519U},  // node 841 — Inquisition of the Damned
-    {2075U, 2586U},  // node 842 — Trials and Tribulations
-    {2194U, 2662U},  // node 843 — The Singular Exegete
-    {1598U, 2345U},  // node 849 — Ecdysis
-    {1558U, 2341U},  // node 850 — A Man with No Name
-    {1866U, 2516U},  // node 852 — Aspect
-    {1876U, 2518U},  // node 853 — Revelation
-    {2085U, 2584U},  // node 854 — The Liar
+inline constexpr std::array<Bar, 35> kBars{{
+    {838U, 2398U},  // Confessions (measured)
+    {815U, 1932U},  // The Lawless Frontier — parent record is named "The Tangled Shore" (measured)
+    {816U, 1933U},  // The Man They Call Cayde (measured)
+    {817U, 1940U},  // Ghost Stories (measured)
+    {818U, 1941U},  // Most Loyal (measured)
+    {819U, 1939U},  // Letters from a Renegade (measured)
+    {821U, 2273U},  // Dawning Delights (measured)
+    {822U, 2342U},  // Dust (measured)
+    {823U, 2344U},  // Stolen Intelligence (field136)
+    {824U, 2346U},  // The Warlock Aunor (field136)
+    {825U, 2520U},  // Luna's Lost (field136)
+    {826U, 2521U},  // Letters from Eris (field136)
+    {828U, 2574U},  // Constellations (field136)
+    {829U, 2664U},  // Duress and Egress (field136)
+    {831U, 1931U},  // The Forsaken Prince (measured)
+    {832U, 1936U},  // Truth to Power (measured)
+    {833U, 1938U},  // A Drifter's Gambit (measured)
+    {835U, 4619U},  // The Book of Unmaking (field136)
+    {836U, 2347U},  // For Every Rose, a Thorn (measured)
+    {837U, 2399U},  // The Chronicon (measured)
+    {839U, 2514U},  // Unveiling (field136)
+    {840U, 2517U},  // Last Days on Kraken Mare (field136)
+    {841U, 2519U},  // Inquisition of the Damned (field136)
+    {842U, 2585U},  // Trials and Tribulations (measured)
+    {843U, 2663U},  // The Singular Exegete (measured)
+    {845U, 1934U},  // The Dreaming City (measured)
+    {846U, 1935U},  // Marasenna (measured)
+    {847U, 1937U},  // The Awoken of the Reef (measured)
+    {848U, 2267U},  // The Black Armory Papers (measured)
+    {849U, 2348U},  // Ecdysis (measured)
+    {850U, 2341U},  // A Man with No Name (field136)
+    {851U, 2397U},  // Nothing Ends (measured)
+    {852U, 2516U},  // Aspect (field136)
+    {853U, 2518U},  // Revelation (field136)
+    {854U, 2584U},  // The Liar (field136)
 }};
 
 } // namespace sunrise::state::record_claims::parent_bar_table

+ 6 - 4
Sunrise/src/state/record_claims/record_claims.cpp

@@ -539,9 +539,9 @@ std::size_t apply_node_progress(std::span<std::int32_t> objectiveValues) noexcep
             // the shipped tables rather than derived: writing the parent record's objective slot
             // was tried and moved nothing, and the node's valueIndex resolves for only some books.
             std::int32_t parentSlot = -1;
-            if (haveParent) {
+            {
                 for (const auto& bar : parent_bar_table::kBars) {
-                    if (bar.recordRow != parent.definitionIndex) {
+                    if (bar.nodeIndex != node.definitionIndex) {
                         continue;
                     }
                     if (static_cast<std::size_t>(bar.valueIndex) < state->values.size()) {
@@ -567,10 +567,12 @@ std::size_t apply_node_progress(std::span<std::int32_t> objectiveValues) noexcep
             std::array<char, 160> line{};
             const int told = std::snprintf(line.data(), line.size(),
                                            "ev=claims stage=node_bar node=%u children=%u "
-                                           "chapters=%d parent_slot=%d value_slot=%u",
+                                           "chapters=%d parent_slot=%d value_slot=%u char_value=%u char_parent=%u",
                                            static_cast<unsigned>(node.definitionIndex),
                                            static_cast<unsigned>(node.childCount), chapters,
-                                           parentSlot, static_cast<unsigned>(node.valueIndex));
+                                           parentSlot, static_cast<unsigned>(node.valueIndex),
+                                           static_cast<unsigned>(node.characterValueIndex),
+                                           static_cast<unsigned>(node.parentCharacterValueIndex));
             if (told > 0) {
                 core::log::write(core::log::Channel::state, core::log::Level::info,
                                  {line.data(), static_cast<std::size_t>(told)});