Merge remote-tracking branch 'origin/main' into rando-mod

This commit is contained in:
Luke Street
2026-08-22 23:36:24 -06:00
12 changed files with 183 additions and 19 deletions
+33
View File
@@ -121,6 +121,25 @@ struct jmessage_tReference : public JMessage::TReference {
void setDemoFrame(u32 i_frame) { mDemoFrame = i_frame; }
void setTopColorType(u8 i_colorType) { mTopColorType = i_colorType; }
void setNowColorType(u8 i_colorType) { mNowColorType = i_colorType; }
#if TARGET_PC
void resetColors() {
mNowCCColor = 0xFFFFFFFF;
mNowGCColor = 0xFFFFFFFF;
mTopCCColor = 0xFFFFFFFF;
mTopGCColor = 0xFFFFFFFF;
mNowFullColor = false;
mTopFullColor = false;
}
void setFullColor(u32 ccColor, u32 gcColor) {
mNowCCColor = ccColor;
mNowGCColor = gcColor;
mNowFullColor = true;
}
void clearNowFullColor() { mNowFullColor = false; }
bool hasTopFullColor() { return mTopFullColor; }
u32 getTopCCColor() { return mTopCCColor; }
u32 getTopGCColor() { return mTopGCColor; }
#endif
void setTopTagScale(u16 i_tagScale) { mTopTagScale = i_tagScale; }
void setNowTagScale(u16 i_tagScale) { mNowTagScale = i_tagScale; }
void setRevoMessageID(u32 i_msgID) { mRevoMessageID = i_msgID; }
@@ -365,6 +384,14 @@ struct jmessage_tReference : public JMessage::TReference {
/* 0x1274 */ bool mSelectSetCancelFlag;
/* 0x1275 */ bool mBombNameUseFlag;
/* 0x1276 */ u8 mBatchColorFlag;
#if TARGET_PC
u32 mNowCCColor;
u32 mNowGCColor;
u32 mTopCCColor;
u32 mTopGCColor;
bool mNowFullColor;
bool mTopFullColor;
#endif
}; // Size: 0x1278
struct jmessage_tMeasureProcessor : public JMessage::TRenderingProcessor {
@@ -462,6 +489,9 @@ struct jmessage_tRenderingProcessor : public JMessage::TRenderingProcessor {
void do_selwidthcenter(int);
void do_heightcenter();
void do_color(u8);
#if TARGET_PC
void do_fullcolor(u32 ccColor, u32 gcColor);
#endif
void do_scale(f32);
void do_linedown(s16);
void do_transY(s16, bool);
@@ -640,6 +670,9 @@ struct jmessage_string_tRenderingProcessor : public JMessage::TRenderingProcesso
void do_rubystrcat(char*);
void do_outfont(u8);
void do_color(u8);
#if TARGET_PC
void do_fullcolor(u32 ccColor, u32 gcColor);
#endif
void do_scale(f32);
void do_linedown(s16);
void do_numset(s16);
+8 -6
View File
@@ -177,18 +177,20 @@ MOD_EXPORT ModResult mod_initialize(ModError* outError) {
if (result == MOD_OK) {
result = add_message(mods::flow::MessageBuilder{kResponseStyle}
.text_color(MESSAGE_COLOR_DEFAULT)
.text("Hello from ")
.text_color(MESSAGE_COLOR_RED)
.text("FlowService")
.text("Custom ")
.text_color(0xFF0000FF)
.text("colors")
.text_color(MESSAGE_COLOR_DEFAULT)
.text(" and even ")
.text_color(0x00FF00FF, 0x00FFFFFF)
.text("gradients")
.text_color(MESSAGE_COLOR_DEFAULT)
.text(", ")
.player_name()
.text("!\n")
.character_delay(3)
.text("This text is slow. ")
.character_delay(0)
.pause(12)
.text_scale(125)
.text_scale(150)
.text("Big")
.text_scale(100)
.text(" text too.")
+14 -1
View File
@@ -500,7 +500,20 @@ public:
}
MessageBuilder& text_color(MessageTextColor color) {
const auto index = static_cast<uint8_t>(color);
return raw_tag(255, 0, {&index, 1});
return raw_tag(0xFF, 0, {&index, 1});
}
/* Full 32-bit text color (Dusklight extension) */
MessageBuilder& text_color(uint32_t rgba) {
std::array<uint8_t, 4> arguments{};
write_bits(arguments.data(), rgba);
return raw_tag(0xFF, 0, arguments);
}
/* Full 32-bit text vertical gradient colors (Dusklight extension) */
MessageBuilder& text_color(uint32_t upperRgba, uint32_t lowerRgba) {
std::array<uint8_t, 8> arguments{};
write_bits(arguments.data(), upperRgba);
write_bits(arguments.data() + sizeof(upperRgba), lowerRgba);
return raw_tag(0xFF, 0, arguments);
}
MessageBuilder& text_scale(uint16_t percent) { return timed_tag(255, 1, percent); }
MessageBuilder& character_delay(uint16_t frames) { return timed_tag(0, 6, frames); }
+1 -1
View File
@@ -8,7 +8,7 @@
#define MESSAGE_SERVICE_ID "dev.twilitrealm.dusklight.message"
#define MESSAGE_SERVICE_MAJOR 1u
#define MESSAGE_SERVICE_MINOR 0u
#define MESSAGE_SERVICE_MINOR 1u
typedef uint16_t MessageId;
typedef uint64_t MessageHandle;
+5 -3
View File
@@ -9,7 +9,7 @@
#define UI_SERVICE_ID "dev.twilitrealm.dusklight.ui"
#define UI_SERVICE_MAJOR 1u
#define UI_SERVICE_MINOR 4u
#define UI_SERVICE_MINOR 5u
/*
* UI primitives: a panel inside the host Mods window, mod-owned windows, dialogs, toasts,
@@ -102,7 +102,7 @@ typedef struct UiControlDesc {
/* Optional override for the modified indicator. CONFIG_VAR controls derive it from value !=
* default when this is NULL. */
UiPredicateFn is_modified;
/* Passed to every callback above. */
/* Passed to every callback. */
void* user_data;
/* NUMBER: inclusive clamp range and step. min == max means the defaults (0 .. INT32_MAX); step
* < 1 means 1. */
@@ -121,11 +121,13 @@ typedef struct UiControlDesc {
const char* const* color_presets;
size_t color_preset_count;
bool color_alpha; /* COLOR: use RRGGBBAA values instead of RRGGBB */
/* Optional selected state for BUTTON/GROUP. Added in UiService minor version 5. */
UiPredicateFn is_selected;
} UiControlDesc;
#define UI_CONTROL_DESC_INIT \
{sizeof(UiControlDesc), UI_CONTROL_BUTTON, NULL, NULL, UI_BINDING_CALLBACKS, 0u, NULL, NULL, \
NULL, NULL, NULL, NULL, 0, 0, 1, NULL, NULL, NULL, 0u, 0, NULL, 0u, false}
NULL, NULL, NULL, NULL, 0, 0, 1, NULL, NULL, NULL, 0u, 0, NULL, 0u, false, NULL}
/* Build pane contents. A non-MOD_OK result fails the mod. */
typedef ModResult (*UiPaneBuildFn)(
+93 -3
View File
@@ -16,6 +16,21 @@
#if TARGET_PC
#include "dusk/menu_pointer.h"
#include "dusk/scope_guard.hpp"
#include "helpers/bits.hpp"
static bool read_full_color(const void* data, u32 size, u32& ccColor, u32& gcColor) {
if (size == 4) {
ccColor = dusk::read_bits<u32>(data);
gcColor = ccColor;
return true;
}
if (size == 8) {
ccColor = dusk::read_bits<u32>(data);
gcColor = dusk::read_bits<u32>(static_cast<const u8*>(data) + sizeof(ccColor));
return true;
}
return false;
}
#endif
#if TARGET_PC
@@ -400,6 +415,9 @@ jmessage_tReference::jmessage_tReference() {
resetCharCountBuffer();
mNowColorType = 0;
mTopColorType = 0;
#if TARGET_PC
resetColors();
#endif
mButtonTagStopFlag = 0;
mPageEndCount = 0;
mSelectNum = 0;
@@ -594,6 +612,13 @@ void jmessage_tReference::pageSend() {
if (mNowColorType != mTopColorType) {
mTopColorType = mNowColorType;
}
#if TARGET_PC
mTopFullColor = mNowFullColor;
if (mNowFullColor) {
mTopCCColor = mNowCCColor;
mTopGCColor = mNowGCColor;
}
#endif
mTopWordCount = mNowWordCount;
mCharAlpha = 0.0f;
@@ -1917,6 +1942,9 @@ void jmessage_tSequenceProcessor::do_begin(void const* pEntry, char const* pszTe
pReference->resetCharCnt();
pReference->setNowColorType(0);
pReference->setTopColorType(0);
#if TARGET_PC
pReference->resetColors();
#endif
pReference->setNowWordCount(0);
pReference->setTopWordCount(0);
pReference->setBatchColorFlag(0);
@@ -2280,9 +2308,21 @@ bool jmessage_tSequenceProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_si
return true;
case MSGTAG_GROUP(255):
switch (i_tag) {
case MSGTAG_COLOR:
case MSGTAG_COLOR: {
#if TARGET_PC
u32 ccColor;
u32 gcColor;
if (read_full_color(i_data, i_size, ccColor, gcColor)) {
pReference->setFullColor(ccColor, gcColor);
} else if (i_size == 1) {
pReference->clearNowFullColor();
pReference->setNowColorType(*(u8*)i_data & 0xFF);
}
#else
pReference->setNowColorType(*(u8*)i_data & 0xFF);
#endif
return true;
}
case MSGTAG_SCALE:
pReference->setNowTagScale(*(BE(u16)*)i_data & 0xFFFF);
return true;
@@ -2863,7 +2903,15 @@ void jmessage_tRenderingProcessor::do_begin(void const* pEntry, char const* pszT
do_scale(field_0x44);
}
#if TARGET_PC
if (pReference->hasTopFullColor()) {
do_fullcolor(pReference->getTopCCColor(), pReference->getTopGCColor());
} else {
do_color(pReference->getTopColorType());
}
#else
do_color(pReference->getTopColorType());
#endif
pReference->resetDrawLightCount();
do_widthcenter();
@@ -3017,9 +3065,20 @@ bool jmessage_tRenderingProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_s
return 1;
case MSGTAG_GROUP(255):
switch (i_tag) {
case MSGTAG_COLOR:
case MSGTAG_COLOR: {
#if TARGET_PC
u32 ccColor;
u32 gcColor;
if (read_full_color(i_data, i_size, ccColor, gcColor)) {
do_fullcolor(ccColor, gcColor);
} else if (i_size == 1) {
do_color(*(u8*)i_data & 0xFF);
}
#else
do_color(*(u8*)i_data & 0xFF);
#endif
return 1;
}
case MSGTAG_SCALE:
field_0x13c = *(BE(u16)*)i_data & 0xFFFF;
do_scale(field_0x13c / 100.0f);
@@ -3630,6 +3689,18 @@ void jmessage_tRenderingProcessor::do_color(u8 i_colorNo) {
do_strcat(buffer, false, false, false);
}
#if TARGET_PC
void jmessage_tRenderingProcessor::do_fullcolor(u32 ccColor, u32 gcColor) {
mColorNo = 0xFF;
mCCColor = ccColor;
mGCColor = gcColor;
char buffer[40];
SAFE_SPRINTF(buffer, "\x1B" "CC[%08x]" "\x1B" "GC[%08x]", mCCColor, mGCColor);
do_strcat(buffer, false, false, false);
}
#endif
void jmessage_tRenderingProcessor::do_scale(f32 param_1) {
jmessage_tReference* pReference = (jmessage_tReference*)getReference();
@@ -4684,9 +4755,20 @@ bool jmessage_string_tRenderingProcessor::do_tag(u32 i_tag, void const* i_data,
switch(i_tag & 0xFF0000) {
case MSGTAG_GROUP(255):
switch(i_tag) {
case MSGTAG_COLOR:
case MSGTAG_COLOR: {
#if TARGET_PC
u32 ccColor;
u32 gcColor;
if (read_full_color(i_data, i_size, ccColor, gcColor)) {
do_fullcolor(ccColor, gcColor);
} else if (i_size == 1) {
do_color(*(u8*)i_data & 0xFF);
}
#else
do_color(*(u8*)i_data & 0xFF);
#endif
break;
}
case MSGTAG_SCALE:
do_scale(*(BE(u16)*)i_data / 100.0f);
break;
@@ -5336,6 +5418,14 @@ void jmessage_string_tRenderingProcessor::do_color(u8 i_colorNo) {
do_strcat(buffer);
}
#if TARGET_PC
void jmessage_string_tRenderingProcessor::do_fullcolor(u32 ccColor, u32 gcColor) {
char buffer[32];
SAFE_SPRINTF(buffer, "\x1b" "CC[%08x]" "\x1b" "GC[%08x]", ccColor, gcColor);
do_strcat(buffer);
}
#endif
void jmessage_string_tRenderingProcessor::do_scale(f32 i_scale) {
J2DTextBox::TFontSize fontSize;
mpReference->getPanePtr()->getFontSize(fontSize);
+12 -3
View File
@@ -11,6 +11,7 @@
#include "JSystem/JMessage/control.h"
#include "JSystem/JMessage/processor.h"
#include "JSystem/JMessage/resource.h"
#include "d/d_msg_class.h"
#include <algorithm>
#include <array>
@@ -467,11 +468,19 @@ bool valid_encoded_text(const uint8_t* text, size_t size) {
if (text[offset] == 0) {
return offset + 1 == size;
}
if (text[offset] == 0x1a) {
if (text[offset] == JMessage::data::gcTagBegin) {
if (offset + 2 > size || text[offset + 1] < 5 || text[offset + 1] > size - offset) {
return false;
}
offset += text[offset + 1];
const size_t tagSize = text[offset + 1];
const uint32_t tag =
MSGTAG_GROUP(text[offset + 2]) | read_bits<uint16_t>(text + offset + 3);
const size_t argumentSize = tagSize - 5;
if (tag == MSGTAG_COLOR && argumentSize != 1 && argumentSize != 4 && argumentSize != 8)
{
return false;
}
offset += tagSize;
} else {
++offset;
}
@@ -490,7 +499,7 @@ size_t encoded_text_size(const ResourceInfo& resource, const char* text) {
if (bytes[offset] == 0) {
return offset + 1;
}
if (bytes[offset] == 0x1a) {
if (bytes[offset] == JMessage::data::gcTagBegin) {
if (offset + 2 > maximum || bytes[offset + 1] < 5 ||
bytes[offset + 1] > maximum - offset)
{
+5 -1
View File
@@ -570,6 +570,9 @@ ModResult ui_pane_add_control(
case UI_CONTROL_GROUP:
spec.kind = desc.kind == UI_CONTROL_BUTTON ? ui::ModControlSpec::Kind::Button :
ui::ModControlSpec::Kind::Group;
if (desc.struct_size >= sizeof(UiControlDesc)) {
spec.isSelected = wrap_predicate(mod, desc.is_selected, desc.user_data, pane);
}
spec.onPressed = [modPtr = &mod, fn = desc.on_pressed, userData = desc.user_data,
guardHandle = pane] {
if (!slot_live(guardHandle)) {
@@ -1147,6 +1150,7 @@ bool valid_color_preset(const char* value, bool alpha) {
bool valid_control_desc(const UiControlDesc& desc) {
constexpr size_t kLegacyDescSize = offsetof(UiControlDesc, color_presets);
constexpr size_t kColorDescSize = offsetof(UiControlDesc, is_selected);
if (desc.struct_size < kLegacyDescSize || desc.label == nullptr) {
return false;
}
@@ -1160,7 +1164,7 @@ bool valid_control_desc(const UiControlDesc& desc) {
case UI_CONTROL_SELECT:
break;
case UI_CONTROL_COLOR:
if (desc.struct_size < sizeof(UiControlDesc)) {
if (desc.struct_size < kColorDescSize) {
return false;
}
break;
+6 -1
View File
@@ -4,15 +4,20 @@ namespace dusk::ui {
GroupButton::GroupButton(Rml::Element* parent, Props props)
: SelectButton{parent, {.key = std::move(props.text)}},
mIsDisabled{std::move(props.isDisabled)} {
mIsSelected{std::move(props.isSelected)}, mIsDisabled{std::move(props.isDisabled)} {
mRoot->SetClass("group-button", true);
}
void GroupButton::update() {
set_selected(selected());
set_disabled(disabled());
SelectButton::update();
}
bool GroupButton::selected() const {
return mIsSelected ? mIsSelected() : SelectButton::selected();
}
bool GroupButton::disabled() const {
return mIsDisabled ? mIsDisabled() : SelectButton::disabled();
}
+3
View File
@@ -8,15 +8,18 @@ class GroupButton : public SelectButton {
public:
struct Props {
Rml::String text;
std::function<bool()> isSelected;
std::function<bool()> isDisabled;
};
GroupButton(Rml::Element* parent, Props props);
void update() override;
bool selected() const override;
bool disabled() const override;
private:
std::function<bool()> mIsSelected;
std::function<bool()> mIsDisabled;
};
+2
View File
@@ -17,6 +17,7 @@ Component* build_mod_control(Pane& pane, Pane* helpPane, ModControlSpec spec) {
case ModControlSpec::Kind::Button:
control = &pane.add_button(ControlledButton::Props{
.text = s.label,
.isSelected = s.isSelected,
.isDisabled = s.isDisabled,
})
.on_pressed([shared] {
@@ -28,6 +29,7 @@ Component* build_mod_control(Pane& pane, Pane* helpPane, ModControlSpec spec) {
case ModControlSpec::Kind::Group:
control = &pane.add_group_button(GroupButton::Props{
.text = s.label,
.isSelected = s.isSelected,
.isDisabled = s.isDisabled,
})
.on_pressed([shared] {
+1
View File
@@ -29,6 +29,7 @@ struct ModControlSpec {
std::function<void(int)> setInt;
std::function<Rml::String()> getString;
std::function<void(Rml::String)> setString;
std::function<bool()> isSelected;
std::function<bool()> isDisabled;
std::function<bool()> isModified;
int min = 0;