Bläddra i källkod

Fix two encoder defects and record the four-book lore limitation

The character encoder copied the authored bank over object.objectiveValues
AFTER apply_character_node_progress had written into it, so every value
that pass produced was overwritten before transmission. It has never
reached the client. The copy now runs first.

CharacterUnlockBlock.values, 256 signed values per character inside the
account object, is noted as never written by anything -- only .flags is
assigned. Left as found; recording it because nothing in settings can reach
it and it is invisible from outside the encoder.

Four lore books -- Stolen Intelligence, A Man with No Name, Unveiling and
Revelation -- render a chapter only when it is claimed or its index falls
within the count held in the node's own value slot, and that slot is what
their parent bar displays. The slot carries the claim count, so the bar
stays honest and claimed entries render; an unclaimed entry in those four
does not show. The other thirty-four books are unaffected and correct.

What that conclusion rests on, so it is not re-derived from scratch: the
whole account value bank written non-zero at 1 and at 100; the account flag
bank swept 0-8922 in batches, plus the profile, per-character and character
banks; both progression banks; the family5 override on both lists, which
outranks the account bank on the same raw slot in both directions and so
cannot carry a second number; the parent record's "Stories gathered"
objective at every value. Nothing in the shipped data separates these four
from six books of identical shape that work -- 75 manifest tables scanned
with only four referencing these books at all and identical profiles for
all ten, presentation node rows decoded, and all 129 chapter record rows
decoded and compared byte for byte.
Millie 1 vecka sedan
förälder
incheckning
c315f8e815

+ 8 - 5
Sunrise/src/middleware/datagen/family4/character/character_encoder.cpp

@@ -173,16 +173,19 @@ bool encode(const state::CharacterState& state,
         }
     }
 
+    // The authored bank is laid down first. It used to be copied in after the node pass below,
+    // which overwrote every element the pass had just written -- so the character-scoped node
+    // progress never reached the client at all.
+    for (std::size_t index = 0; index < object.objectiveValues.size(); ++index) {
+        object.objectiveValues[index] =
+            index < unlocks.characterObjectValues.size() ? unlocks.characterObjectValues[index] : 0;
+    }
+
     // One lore book counts in the character bank rather than the account one.
     (void)state::record_claims::apply_character_node_progress(object.objectiveValues);
 
     // One lore book's gate is character scoped rather than account scoped.
     (void)state::build_data::nodes::apply_character_visibility(object.acquiredFlags);
-
-    for (std::size_t index = 0; index < object.objectiveValues.size(); ++index) {
-        object.objectiveValues[index] =
-            index < unlocks.characterObjectValues.size() ? unlocks.characterObjectValues[index] : 0;
-    }
     if (!build_equipment_summary(lightEvaluation, object.equipmentSummary)) {
         return false;
     }

+ 19 - 8
Sunrise/src/state/record_claims/record_claims.cpp

@@ -710,18 +710,29 @@ std::size_t apply_node_progress(std::span<std::int32_t> objectiveValues) noexcep
                 // parent triumph showing it is faithful rather than a compromise. Revelation sits
                 // in this group despite completing every chapter at 1: it is activity-gated, not
                 // cumulative, which is why it never behaved like the books it otherwise matches.
-                constexpr std::array<std::uint16_t, 4> kActivityAcquired{
+                constexpr std::array<std::uint16_t, 3> kActivityAcquired{
                     823U,  // Stolen Intelligence
                     839U,  // Unveiling
                     850U,  // A Man with No Name
-                    853U,  // Revelation
                 };
-                for (const std::uint16_t acquired : kActivityAcquired) {
-                    if (acquired == node.definitionIndex && collected > 0
-                        && static_cast<std::size_t>(node.valueIndex) < state->values.size()) {
-                        state->values[node.valueIndex] = collected;
-                    }
-                }
+                // The gate that reveals these four books' entries and the bar that reports their
+                // claims are one slot, so a single image cannot carry both numbers. This publishes
+                // the entry count on the first few images -- long enough for the client to unlock
+                // the entries -- and the claim count from then on. Whether the client keeps the
+                // unlock or re-reads it every image decides if this holds.
+                // Four books -- Stolen Intelligence, A Man with No Name, Unveiling and
+                // Revelation -- render a chapter only if it is claimed, or if its index is at or
+                // below the count in this slot. That slot is also what their bar displays, so a
+                // count large enough to reveal unclaimed entries reports itself as claims. It is
+                // left carrying the claim count: the bar stays honest and claimed entries render.
+                //
+                // Every alternative was tested. Both value banks at 1 and at 100, all four flag
+                // banks, both progression banks, the parent record's "Stories gathered"
+                // objective, the second block slot, and the family5 override on both its lists --
+                // the override simply outranks the bank on the same raw slot, in both directions,
+                // so it cannot carry a second number. Nothing in the shipped data separates these
+                // four from six books of identical shape that work: manifest, node rows and all
+                // 129 chapter record rows were decoded and compared byte for byte.
                                 if (cumulative && collected > 0
                     && static_cast<std::int32_t>(counterSlot)
                            < objective_slot_table::kRecordObjectiveRangeStart