tag preview, separate switches and per-item colors

- Split the single switch into "Show my tag" and "Show my items", and
  show the tag as chat and nametags draw it.
- Color whatever is equipped instead of the one hoodie and pink board:
  top, bottoms, shoes, socks, hat, glasses, outfit, costume, deck, grip
  tape, trucks and wheels, each set on its own.
- Each item can use its list's animation (rainbow, red or gold), a
  gradient between two picked colors, one solid color, or be off.
- The choices travel with the player's appearance so everyone sees them.
  Protocol version 41.
This commit is contained in:
AyeZeeBB committed 2026-10-05 00:00:45 -05:00
1 parent c930b512e6
commit 5bfe4f7bfa
20 files changed
+910 -421

No files matched your search

+31 -15
View File
@@ -20,12 +20,12 @@ void report_board(std::uintptr_t entity, std::uint64_t steam_id, std::string_vie
}
if (entry->entity == entity && (entry->message == hash || now - entry->at < 5000)) return;
*entry = {entity, now, hash};
logging::log(level, logging::Channel::customization, "Developer board (Steam {}, actor {:#x}): {}", steam_id, entity, message);
logging::log(level, logging::Channel::customization, "Board items (Steam {}, actor {:#x}): {}", steam_id, entity, message);
}
bool write_color(std::uintptr_t base, const developer_board_detail::Binding &binding, const developer_board_detail::Color &color) noexcept {
__try {
if (!binding.node) {
const developer_board_detail::NativeColor value{color, 0, binding.type};
const developer_board_detail::NativeColor value{color, 0, base + addr::native_cosmetics::color_rgb_type};
// Let the engine allocate/copy/hash a missing typed parameter. Priority
// zero allows later native appearance presets to replace this override.
return reinterpret_cast<std::uint64_t (*)(std::uintptr_t, const developer_board_detail::ParameterKey *,
@@ -43,13 +43,26 @@ bool publish_materials(std::uintptr_t base, std::uintptr_t item) noexcept {
return true;
} __except (EXCEPTION_EXECUTE_HANDLER) { return false; }
}
// What each of the board's cosmetics does for this player: deck, grip tape, trucks, wheels.
using Animations = std::array<developer_board_detail::ItemAnimation, developer_board_detail::part_count>;
Animations board_animations(std::uint64_t steam_id, const multiplayer::MarkStyles &styles) noexcept {
const auto mark = multiplayer::identity_mark(steam_id);
Animations out;
for (std::size_t i = 0; i < out.size(); ++i)
out[i] = developer_board_detail::item_animation(mark, styles[multiplayer::skater_mark_items + i]);
return out;
}
bool any_on(const Animations &animations) noexcept {
return std::any_of(animations.begin(), animations.end(), [](const auto &animation) { return animation.on; });
}
} // namespace
void update_developer_board(std::uintptr_t base, std::uintptr_t board, std::uint64_t steam_id,
std::uint64_t generation, DeveloperBoardState &state) noexcept {
const auto animation = developer_board_detail::animation_for(steam_id);
const bool listed = animation != developer_board_detail::Animation::none;
std::uint64_t generation, DeveloperBoardState &state,
const multiplayer::MarkStyles &styles) noexcept {
const auto animations = board_animations(steam_id, styles);
const bool listed = any_on(animations);
if (!board) { state = {}; return; }
if (!listed && !state.count) return;
if (!listed && state.colors.empty()) return;
try {
auto read = [](std::uintptr_t address, void *out, std::size_t size) { return memory::peek_bytes(address, out, size); };
std::array<unsigned char, addr::native_cosmetics::publish_materials_prefix.size()> publisher{};
@@ -65,14 +78,16 @@ void update_developer_board(std::uintptr_t base, std::uintptr_t board, std::uint
bool published = true;
auto write = [base](const auto &binding, const auto &color) { return write_color(base, binding, color); };
auto publish = [base, &published](std::uintptr_t item) { if (!publish_materials(base, item)) published = false; };
developer_board_detail::animate(state, live, board, generation, animation, GetTickCount64(), write, publish);
const auto wanted = static_cast<std::size_t>(std::count_if(live.bindings.begin(), live.bindings.begin() + live.count,
[](const auto &binding) { return binding.eligible; }));
developer_board_detail::animate(state, live, board, generation, animations, GetTickCount64(), write, publish);
// The colours of the parts that are on: found, and taken.
const auto wanted = std::count_if(live.bindings.begin(), live.bindings.end(),
[&](const auto &binding) { return animations[binding.part].on; });
const auto taken = std::count_if(state.colors.begin(), state.colors.end(),
[&](const auto &saved) { return animations[saved.binding.part].on; });
if (!published) report_board(board, steam_id, "native material publication failed", logging::Level::warning);
else if (listed && !live.pending && state.count < wanted)
else if (listed && !live.pending && taken < wanted)
report_board(board, steam_id, "waiting for the remaining board color overrides", logging::Level::warning);
else if (listed && state.count)
report_board(board, steam_id, "animation active on the owned pink board cosmetics", logging::Level::info);
else if (listed && taken) report_board(board, steam_id, "animation active on the board", logging::Level::info);
} catch (const std::exception &error) {
report_board(board, steam_id, error.what(), logging::Level::warning);
} catch (...) {}
@@ -82,15 +97,16 @@ void tick_local_developer_board(std::uintptr_t base, std::uintptr_t client, bool
if (!ready) return;
try {
const auto social = multiplayer::steam_social_snapshot();
const auto id = social && multiplayer::own_marks_shown() ? social->local.id : 0;
if (developer_board_detail::animation_for(id) == developer_board_detail::Animation::none && !state.count) return;
const auto id = social && multiplayer::own_items_shown() ? social->local.id : 0;
const auto styles = developer_hoodie_detail::own_styles.load();
if (!any_on(board_animations(id, styles)) && state.colors.empty()) return;
const auto local = multiplayer::capture_local(base, client, false);
if (!local.ready) return;
auto read = [](std::uintptr_t address, void *out, std::size_t size) { return memory::peek_bytes(address, out, size); };
// Cosmetic changes can replace the board while retaining the skater.
// Re-resolve the actual local association, rather than capture's cached board.
const auto board = multiplayer::read_native_board(read, base, local.entity);
update_developer_board(base, board.entity, id, local.context, state);
update_developer_board(base, board.entity, id, local.context, state, styles);
} catch (...) {}
}
} // namespace dingosdk