Browse Source

Keep the cue row on the shared authored window

The dialogue cue row now starts with the same four fields the catalog
already defines: slot, cue, definition hash and the authored window in
seconds, validated the same way. List tag, line count and flags follow.

Milliseconds are derived where they are needed, through one rounding
helper: the Lua DialogueDefinition constants, the take durations of the
text rows and the dialogue_staged event.
Fozkais 1 month ago
parent
commit
6568f05df6

+ 1 - 1
Sunrise/src/client/content/activity/activity_sdk_authored_scene_inventory.h

@@ -84,7 +84,7 @@ struct DialogueCue final {
     std::uint32_t cueIndex{};
     std::uint32_t listTag{};
     std::uint32_t definitionHash{};
-    std::uint32_t durationMs{};
+    float authoredWindowSeconds{};
     std::uint32_t lineCount{};
     std::uint32_t flags{};
 };

+ 3 - 2
Sunrise/src/client/content/activity/activity_sdk_lua_missions.cpp

@@ -13,7 +13,7 @@
 #include <vector>
 
 #include "../../../middleware/bap/activity_message/auth_schema_catalog.h"
-#include "../../../state/activity_sdk/scene_triggers.h"
+#include "../../../state/activity_sdk/runtime.h"
 #include "activity_sdk_lua_missions_internal.h"
 
 namespace sunrise::client::content::activity::sdk_generation::lua_artifacts::internal {
@@ -557,7 +557,8 @@ bool render_mission(const Source& source,
                 dialogueDefinitionConstants.append("] = { hash = ");
                 append_hex(dialogueDefinitionConstants, row.definitionHash);
                 dialogueDefinitionConstants.append(", duration_ms = ");
-                append_uint(dialogueDefinitionConstants, row.durationMs);
+                append_uint(dialogueDefinitionConstants,
+                            state::activity_sdk::authored_milliseconds(row.authoredWindowSeconds));
                 dialogueDefinitionConstants.append(", lines = ");
                 append_uint(dialogueDefinitionConstants, row.lineCount);
                 dialogueDefinitionConstants.append(" },\n");

+ 11 - 17
Sunrise/src/client/content/activity/activity_sdk_native_pack_text.cpp

@@ -17,6 +17,7 @@
 
 #include "../../../core/logging/log.h"
 #include "../../../middleware/content/packages/tables/activity_display_name_reader.h"
+#include "../../../state/activity_sdk/runtime.h"
 #include "activity_sdk_dialogue_group_index.h"
 #include "activity_sdk_dialogue_list.h"
 #include "activity_sdk_native_pack_internal.h"
@@ -185,14 +186,6 @@ void log_dialogue_list(const squads::DescriptorFact& descriptor,
     }
 }
 
-/** Rounds seconds to the nearest millisecond, saturating at the row's width. */
-[[nodiscard]] std::uint32_t milliseconds(float seconds) noexcept {
-    const double value = std::round(static_cast<double>(seconds) * 1000.0);
-    return value >= static_cast<double>((std::numeric_limits<std::uint32_t>::max)())
-               ? (std::numeric_limits<std::uint32_t>::max)()
-               : static_cast<std::uint32_t>(value);
-}
-
 } // namespace
 
 /**
@@ -408,7 +401,7 @@ bool attach_authored_text(const topology_inventory::Snapshot& topology,
                     cueRow.cueIndex = static_cast<std::uint32_t>(cue);
                     cueRow.listTag = resourceTag;
                     cueRow.definitionHash = row.definitionHash;
-                    cueRow.durationMs = milliseconds(row.seconds);
+                    cueRow.authoredWindowSeconds = row.seconds;
                     if (row.status == dialogue_list::CueStatus::resolved) {
                         cueRow.lineCount = row.lineCount;
                         cueRow.flags = format::kDialogueCueLinesExact;
@@ -442,14 +435,15 @@ bool attach_authored_text(const topology_inventory::Snapshot& topology,
                                 || container->classId != display::kStringContainerClass) {
                                 continue;
                             }
-                            dialogueCandidates.push_back({{take.containerTag, take.stringHash},
-                                                          descriptor.slotIndex,
-                                                          static_cast<std::uint32_t>(cue),
-                                                          row.definitionHash,
-                                                          lineIndex,
-                                                          takeIndex,
-                                                          take.audioTag,
-                                                          milliseconds(take.seconds)});
+                            dialogueCandidates.push_back(
+                                {{take.containerTag, take.stringHash},
+                                 descriptor.slotIndex,
+                                 static_cast<std::uint32_t>(cue),
+                                 row.definitionHash,
+                                 lineIndex,
+                                 takeIndex,
+                                 take.audioTag,
+                                 state::activity_sdk::authored_milliseconds(take.seconds)});
                         }
                     }
                 }

+ 1 - 1
Sunrise/src/client/content/activity/activity_sdk_pack_composer_rows.cpp

@@ -658,7 +658,7 @@ translate_native_rows(const Inputs& inputs, const detail::StringResolver& linker
         target.cueIndex = input.cueIndex;
         target.listTag = input.listTag;
         target.definitionHash = input.definitionHash;
-        target.durationMs = input.durationMs;
+        target.authoredWindowSeconds = input.authoredWindowSeconds;
         target.lineCount = input.lineCount;
         target.flags = input.flags;
         if (!link_text(linker, input.id, target.id)) {

+ 2 - 2
Sunrise/src/server/ui/activity_host/activity_host_sdk_mission_view.cpp

@@ -481,10 +481,10 @@ void draw_dialogues(const sdk::BoundView& view, const mission::Snapshot& snapsho
                     ImGui::SameLine();
                     if (cueRow < cueRows.size()) {
                         const sdk::format::DialogueCue& definition = cueRows[cueRow];
-                        ImGui::Text("Cue %u  definition %08X  %u ms  %u lines",
+                        ImGui::Text("Cue %u  definition %08X  %.3f s  %u lines",
                                     static_cast<unsigned>(cue),
                                     static_cast<unsigned>(definition.definitionHash),
-                                    static_cast<unsigned>(definition.durationMs),
+                                    static_cast<double>(definition.authoredWindowSeconds),
                                     static_cast<unsigned>(definition.lineCount));
                     } else {
                         ImGui::Text("Cue %u  no definition", static_cast<unsigned>(cue));

+ 12 - 0
Sunrise/src/state/activity_sdk/activity_sdk_catalog.cpp

@@ -2,6 +2,8 @@
 
 #include <algorithm>
 #include <atomic>
+#include <cmath>
+#include <limits>
 
 #include "internal.h"
 #include "runtime.h"
@@ -793,6 +795,16 @@ std::span<const format::DialogueCue> slot_dialogue_cues(const Catalog& catalog,
                           static_cast<std::size_t>(last - first));
 }
 
+std::uint32_t authored_milliseconds(float seconds) noexcept {
+    const double value = std::round(static_cast<double>(seconds) * 1000.0);
+    if (!(value > 0.0)) {
+        return 0;
+    }
+    return value >= static_cast<double>((std::numeric_limits<std::uint32_t>::max)())
+               ? (std::numeric_limits<std::uint32_t>::max)()
+               : static_cast<std::uint32_t>(value);
+}
+
 /** Relies on slot-index ordering to return one contiguous zero-copy resource range. */
 std::span<const format::AuthoredSceneResource>
 slot_authored_scene_resources(const Catalog& catalog, const format::Slot& slot) noexcept {

+ 1 - 0
Sunrise/src/state/activity_sdk/activity_sdk_relation_validation.cpp

@@ -118,6 +118,7 @@ constexpr std::uint32_t kAbilityTargetComponentClass = 0x80807D9BU;
         const format::DialogueCue& row = cues[index];
         if (row.slotIndex >= slots.size() || (row.flags & ~format::kDialogueCueFlagMask) != 0
             || row.reserved != 0 || row.definitionHash == 0 || row.definitionHash == 0x811C9DC5U
+            || !std::isfinite(row.authoredWindowSeconds) || row.authoredWindowSeconds < 0.0F
             || row.listTag == 0 || row.listTag == format::kAbsentIndex
             || (row.lineCount != 0 && (row.flags & format::kDialogueCueLinesExact) == 0)) {
             return false;

+ 6 - 6
Sunrise/src/state/activity_sdk/format.h

@@ -1295,18 +1295,18 @@ struct CombatObjectiveGroup final {
 };
 
 /**
- * One type-53 cue, in slot then cue order, with one row for every cue of the slot.
- * Lines are present only when kDialogueCueLinesExact is set.
+ * A delayed cue expires after this authored window; it is not a playback duration.
+ * One row per cue of a type-53 slot, in slot then cue order; lines are present only when
+ * kDialogueCueLinesExact is set.
  */
 struct DialogueCue final {
-    StringRef id{};
     std::uint32_t slotIndex{};
     std::uint32_t cueIndex{};
+    std::uint32_t definitionHash{};
+    float authoredWindowSeconds{};
+    StringRef id{};
     /** Dialogue list the cue was read from, the row's provenance. */
     std::uint32_t listTag{};
-    std::uint32_t definitionHash{};
-    /** Authored play time rounded to the nearest millisecond, whether or not lines were read. */
-    std::uint32_t durationMs{};
     std::uint32_t lineCount{};
     std::uint32_t flags{};
     std::uint32_t reserved{};

+ 2 - 0
Sunrise/src/state/activity_sdk/runtime.h

@@ -512,6 +512,8 @@ squad_anchors(const Catalog& catalog, const format::Squad& squad) noexcept;
 /** Returns every cue of one type-53 slot, indexed by cue. */
 [[nodiscard]] std::span<const format::DialogueCue>
 slot_dialogue_cues(const Catalog& catalog, const format::Slot& slot) noexcept;
+/** Rounds authored seconds to the nearest millisecond, saturating at 32 bits. */
+[[nodiscard]] std::uint32_t authored_milliseconds(float seconds) noexcept;
 /** Returns all exact authored resources retained for one type-43 slot. */
 [[nodiscard]] std::span<const format::AuthoredSceneResource>
 slot_authored_scene_resources(const Catalog& catalog, const format::Slot& slot) noexcept;