Browse Source

Carry progress values in the directive lane set_directive publishes

The type-68 encoder wrote the four signed lane values as zero, so a counter
element could only show 0 / 0. The preset now carries them and the encoder
writes them as authored; the inactive lanes stay at zero and the body keeps its
shape. set_directive accepts `progress = { current, maximum, ... }`, one to four
int32 values, and refuses it on an element whose authored flags declare no
counter, since the HUD would ignore the values. The resolved element definition
carries the flag word for that check.
Fozkais 1 week ago
parent
commit
812d23f71a

+ 1 - 0
Sunrise/src/client/content/activity/activity_sdk_lua_contract.cpp

@@ -328,6 +328,7 @@ local EventKind = {
 ---@class SunriseDirectiveArguments
 ---@class SunriseDirectiveArguments
 ---@field directive SunriseDirective Generated mission directive declaration.
 ---@field directive SunriseDirective Generated mission directive declaration.
 ---@field state? integer Defaults to 0, the native enter state.
 ---@field state? integer Defaults to 0, the native enter state.
+---@field progress? integer[] One to four lane values, current then maximum first; the element must declare a counter.
 ---@field audience? SunriseSlot Authored type-70 engagement sensor for the mission banner.
 ---@field audience? SunriseSlot Authored type-70 engagement sensor for the mission banner.
 ---@field navpoint? SunriseSlot Authored type-47 navigation marker.
 ---@field navpoint? SunriseSlot Authored type-47 navigation marker.
 ---@field waypoint? SunriseSlot Authored type-60 volume; inside it the HUD marker hides.
 ---@field waypoint? SunriseSlot Authored type-60 volume; inside it the HUD marker hides.

+ 16 - 11
Sunrise/src/middleware/bap/activity_message/activity_scriptable_auth_mission_body_codec.cpp

@@ -3,6 +3,7 @@
 #include <bit>
 #include <bit>
 #include <cmath>
 #include <cmath>
 #include <limits>
 #include <limits>
+#include <span>
 
 
 #include "scriptable_auth_internal.h"
 #include "scriptable_auth_internal.h"
 
 
@@ -74,22 +75,25 @@ constexpr std::uint32_t kWaypointMarkerMode = 2;
 }
 }
 
 
 /** Writes one of the three complete type-68 state lanes. */
 /** Writes one of the three complete type-68 state lanes. */
-[[nodiscard]] bool write_directive_entry(bits::Writer& writer,
-                                         std::uint32_t nameHash,
-                                         std::int32_t elementIndex,
-                                         std::int8_t state,
-                                         const Type2LaneClientRef& target = {},
-                                         std::uint32_t targetNameHash = kClientRefAbsentKey,
-                                         std::uint32_t targetBubbleHash = 0,
-                                         const Type2LaneClientRef& waypoint = {}) noexcept {
+[[nodiscard]] bool
+write_directive_entry(bits::Writer& writer,
+                      std::uint32_t nameHash,
+                      std::int32_t elementIndex,
+                      std::int8_t state,
+                      const Type2LaneClientRef& target = {},
+                      std::uint32_t targetNameHash = kClientRefAbsentKey,
+                      std::uint32_t targetBubbleHash = 0,
+                      const Type2LaneClientRef& waypoint = {},
+                      std::span<const std::int32_t, kType68ProgressCount> progress =
+                          std::array<std::int32_t, kType68ProgressCount>{}) noexcept {
     if (!writer.write(nameHash, 32)
     if (!writer.write(nameHash, 32)
         || !writer.write(std::bit_cast<std::uint32_t>(elementIndex) + kSigned32Bias, 32)
         || !writer.write(std::bit_cast<std::uint32_t>(elementIndex) + kSigned32Bias, 32)
         || !writer.write(static_cast<std::uint32_t>(state) + 1U, kDirectiveStateWidth)
         || !writer.write(static_cast<std::uint32_t>(state) + 1U, kDirectiveStateWidth)
         || !write_neutral_timed_state(writer)) {
         || !write_neutral_timed_state(writer)) {
         return false;
         return false;
     }
     }
-    for (std::size_t index = 0; index < 4; ++index) {
-        if (!writer.write(kSigned32Bias, 32)) {
+    for (const std::int32_t value : progress) {
+        if (!writer.write(std::bit_cast<std::uint32_t>(value) + kSigned32Bias, 32)) {
             return false;
             return false;
         }
         }
     }
     }
@@ -388,7 +392,8 @@ bool encode_type68(const Type68Preset& preset,
                                               preset.navpoint,
                                               preset.navpoint,
                                               preset.navpointNameHash,
                                               preset.navpointNameHash,
                                               preset.navpointBubbleHash,
                                               preset.navpointBubbleHash,
-                                              preset.waypoint)
+                                              preset.waypoint,
+                                              preset.progress)
                       : write_directive_entry(writer, kClientRefAbsentKey, 0, -1);
                       : write_directive_entry(writer, kClientRefAbsentKey, 0, -1);
     }
     }
     encoded = encoded && writer.write(preset.visible ? 1U : 0U, kDirectiveActiveIndexWidth);
     encoded = encoded && writer.write(preset.visible ? 1U : 0U, kDirectiveActiveIndexWidth);

+ 4 - 0
Sunrise/src/middleware/bap/activity_message/scriptable_auth_body.h

@@ -501,6 +501,8 @@ inline constexpr std::size_t kType53ByteCount = (kType53BitCount + 7) / 8;
 inline constexpr std::uint8_t kType68SlotType = 68;
 inline constexpr std::uint8_t kType68SlotType = 68;
 inline constexpr std::uint32_t kType68Schema = 0x80804F67;
 inline constexpr std::uint32_t kType68Schema = 0x80804F67;
 inline constexpr std::size_t kType68EntryCount = 3;
 inline constexpr std::size_t kType68EntryCount = 3;
+/** Signed progress values each lane carries; a counter element shows the first two. */
+inline constexpr std::size_t kType68ProgressCount = 4;
 inline constexpr std::size_t kType68BitCount = 4'802;
 inline constexpr std::size_t kType68BitCount = 4'802;
 inline constexpr std::size_t kType68ByteCount = (kType68BitCount + 7) / 8;
 inline constexpr std::size_t kType68ByteCount = (kType68BitCount + 7) / 8;
 /** ClientRef slot type of an authored navigation marker a directive may point at. */
 /** ClientRef slot type of an authored navigation marker a directive may point at. */
@@ -553,6 +555,8 @@ struct Type68Preset final {
     /** Native directive state: 0 enters, 1 completes, and 2 uses the alternate exit state. */
     /** Native directive state: 0 enters, 1 completes, and 2 uses the alternate exit state. */
     std::int8_t state{};
     std::int8_t state{};
     bool visible{true};
     bool visible{true};
+    /** Lane progress values, current then maximum first; the HUD reads them as a counter. */
+    std::array<std::int32_t, kType68ProgressCount> progress{};
     /** Authored type-47 destination; absent removes the explicit guidance marker. */
     /** Authored type-47 destination; absent removes the explicit guidance marker. */
     Type2LaneClientRef navpoint{};
     Type2LaneClientRef navpoint{};
     /** The navpoint's slot name hash; the client resolves it to a position when the object is
     /** The navpoint's slot name hash; the client resolves it to a position when the object is

+ 27 - 2
Sunrise/src/server/activity/mission/mission_script_lua_presentation_api.cpp

@@ -84,8 +84,8 @@ namespace {
     const auto* const handle =
     const auto* const handle =
         static_cast<const SlotHandle*>(luaL_checkudata(state, 1, kSlotMetatable));
         static_cast<const SlotHandle*>(luaL_checkudata(state, 1, kSlotMetatable));
     // Named arguments this call accepts. Any other key is refused.
     // Named arguments this call accepts. Any other key is refused.
-    static constexpr std::array<std::string_view, 5> kDeclared{
-        "directive", "state", "navpoint", "audience", "waypoint"};
+    static constexpr std::array<std::string_view, 6> kDeclared{
+        "directive", "state", "navpoint", "audience", "waypoint", "progress"};
     refuse_unknown_arguments(state, kDeclared);
     refuse_unknown_arguments(state, kDeclared);
     SlotDefinition slot{};
     SlotDefinition slot{};
     if (!current_slot(state, *handle, slot)) {
     if (!current_slot(state, *handle, slot)) {
@@ -122,6 +122,31 @@ namespace {
                                          .elementIndex = resolved.elementIndex,
                                          .elementIndex = resolved.elementIndex,
                                          .state = static_cast<std::int8_t>(directiveState),
                                          .state = static_cast<std::int8_t>(directiveState),
                                          .visible = true};
                                          .visible = true};
+    // Progress rides only on an element that declares a counter; the HUD ignores it otherwise.
+    if (push_argument(state, "progress") != LUA_TNIL) {
+        luaL_checktype(state, -1, LUA_TTABLE);
+        const std::size_t count = lua_rawlen(state, -1);
+        if ((resolved.flags & format::kDirectiveElementCounter) == 0) {
+            return luaL_error(state, "directive progress requires an element with a counter");
+        }
+        if (count == 0 || count > preset.progress.size()) {
+            return luaL_error(state, "directive progress holds one to four integers");
+        }
+        for (std::size_t index = 1; index <= count; ++index) {
+            lua_rawgeti(state, -1, static_cast<lua_Integer>(index));
+            if (!lua_isinteger(state, -1)) {
+                return luaL_error(state, "directive progress values must be integers");
+            }
+            const lua_Integer value = lua_tointeger(state, -1);
+            if (value < (std::numeric_limits<std::int32_t>::min)()
+                || value > (std::numeric_limits<std::int32_t>::max)()) {
+                return luaL_error(state, "directive progress is outside its native field width");
+            }
+            preset.progress[index - 1] = static_cast<std::int32_t>(value);
+            lua_pop(state, 1);
+        }
+    }
+    lua_pop(state, 1);
     if (!optional_slot_reference(
     if (!optional_slot_reference(
             state, "audience", scriptable_auth::kType70SlotType, preset.audience)) {
             state, "audience", scriptable_auth::kType70SlotType, preset.audience)) {
         return luaL_error(state,
         return luaL_error(state,

+ 1 - 0
Sunrise/src/server/activity/mission/mission_script_sdk_bridge.cpp

@@ -421,6 +421,7 @@ resolve_slot_id(const void* context, std::string_view id, lua_vm::SlotDefinition
     output.nameHash = row.nameHash;
     output.nameHash = row.nameHash;
     output.elementIndex = row.elementIndex;
     output.elementIndex = row.elementIndex;
     output.elementCount = row.elementCount;
     output.elementCount = row.elementCount;
+    output.flags = row.flags;
     return !output.id.empty();
     return !output.id.empty();
 }
 }
 
 

+ 2 - 0
Sunrise/src/server/activity/mission/mission_script_vm.h

@@ -124,6 +124,8 @@ struct DirectiveElementDefinition final {
     std::int32_t elementIndex{};
     std::int32_t elementIndex{};
     /** Rows that sub-array holds, so the bound is the array's, never the wire lane's width. */
     /** Rows that sub-array holds, so the bound is the array's, never the wire lane's width. */
     std::uint32_t elementCount{};
     std::uint32_t elementCount{};
+    /** Authored flag word; the counter bit says the HUD shows the lane's progress values. */
+    std::uint32_t flags{};
 };
 };
 
 
 /** One state name a type-42 performance sensor may start on the actor it drives. */
 /** One state name a type-42 performance sensor may start on the actor it drives. */