diff --git a/configure.py b/configure.py index fde3cef6..88920685 100644 --- a/configure.py +++ b/configure.py @@ -299,7 +299,7 @@ def Rel(lib_name: str, objects: List[Object]) -> Dict[str, Any]: Object(NonMatching, "SB/Core/x/xFX.cpp"), Object(Matching, "SB/Core/x/xGroup.cpp"), Object(Matching, "SB/Core/x/xhipio.cpp"), - Object(NonMatching, "SB/Core/x/xHud.cpp"), + Object(NonMatching, "SB/Core/x/xHud.cpp", extra_cflags=["-sym on"]), Object(NonMatching, "SB/Core/x/xHudFontMeter.cpp"), Object(NonMatching, "SB/Core/x/xHudMeter.cpp"), Object(NonMatching, "SB/Core/x/xHudModel.cpp"), @@ -497,7 +497,7 @@ def Rel(lib_name: str, objects: List[Object]) -> Dict[str, Any]: Object(NonMatching, "SB/Game/zNPCHazard.cpp"), Object(NonMatching, "SB/Game/zNPCGoalAmbient.cpp"), Object(NonMatching, "SB/Game/zNPCFXCinematic.cpp"), - Object(NonMatching, "SB/Core/x/xHudText.cpp"), + Object(Equivalent, "SB/Core/x/xHudText.cpp", extra_cflags=["-sym on"]), Object(NonMatching, "SB/Game/zCombo.cpp"), Object(NonMatching, "SB/Core/x/xCM.cpp"), ], diff --git a/src/SB/Core/x/xHud.cpp b/src/SB/Core/x/xHud.cpp index dda4981f..97db4b66 100644 --- a/src/SB/Core/x/xHud.cpp +++ b/src/SB/Core/x/xHud.cpp @@ -1,23 +1,469 @@ #include "xHud.h" +#include "xDebug.h" +#include "xEvent.h" +#include "xMath.h" +#include "xMathInlines.h" +#include "xstransvc.h" +#include "zEnt.h" + +#include #include -void xhud::widget::add_tweaks() +namespace xhud { -} -void xhud::widget::render() -{ -} + block_allocator* block_allocator::_head_alloc; + static bool inited; -void xhud::widget::debug_render() -{ -} + void block_allocator::flush_all() + { + for (block_allocator* allocator = _head_alloc; allocator != NULL; + allocator = allocator->_next_alloc) + { + allocator->flush(); + } + } -void xhud::widget::destroy() -{ -} + block_allocator::block_allocator(U32 a0, U32 a1) + { + _block_size = ALIGN(a0, 4) + 4; + _top = NULL; + _next_alloc = _head_alloc; + _head_alloc = this; + set_increment(a1); + } -void xhud::widget::init() -{ -} + void block_allocator::set_increment(U32 a0) + { + _alloc_size = _block_size * a0; + } + + void block_allocator::size_reserve(U32 size) + { + void** ppvVar1 = (void**)xMemAllocSize(size); + void** ppvVar2 = (void**)((U32)ppvVar1 + size); + for (; ppvVar1 < ppvVar2; ppvVar1 = (void**)((U32)ppvVar1 + _block_size)) + { + *ppvVar1 = _top; + _top = ppvVar1; + } + } + + void* block_allocator::alloc() + { + if (_top == NULL) + { + size_reserve(_alloc_size); + } + + void** ptr = (void**)_top; + _top = *ptr; + return ptr + 1; + } + + void block_allocator::free(void* ptr) + { + *(void**)((U32)ptr - 4) = _top; + _top = (void*)((U32)ptr - 4); + } + + void block_allocator::flush() + { + _top = NULL; + } + + block_allocator* widget::motive_allocator() + { + static block_allocator ba(40, 16); + return &ba; + } + + void init() + { + if (!inited) + { + inited = true; + } + else + { + widget::disable_all(true); + } + } + + void setup() + { + widget::setup_all(); + } + + void destroy() + { + xDebugRemoveTweak("HUD"); + widget::disable_all(true); + block_allocator::flush_all(); + } + + void update(F32 dt) + { + widget::update_all(dt); + } + + void render() + { + if (inited) + { + widget::render_all(); + } + } + + widget::widget(const asset& asset) + { + a = &asset; + _motive_top = NULL; + _motive_temp = NULL; + _motive_temp_tail = NULL; + flag.visible = 1; + flag.enabled = 0; + rc.loc = asset.loc; + rc.size = asset.size; + rc.r = rc.g = rc.b = rc.a = 1.0f; + start_rc = rc; + } + + void widget::init_base(xBase& b, const xBaseAsset& asset, unsigned long a2) + { + xBaseInit(&b, (xBaseAsset*)&asset); + b.eventFunc = cb_dispatch; + if (b.linkCount != 0) + { + b.link = (xLinkAsset*)((U32)&asset + a2); + } + } + + void widget::destruct() + { + disable(); + } + + void widget::presetup() + { + activity = ACT_NONE; + add_tweaks(); + } + + void widget::updater(F32 dt) + { + _motive_temp_tail = &_motive_temp; + motive_node** ppmVar2 = &_motive_top; + motive_node* top = _motive_top; + while (top != NULL) + { + bool unk = top->m.update(*this, dt); + if (!unk) + { + *ppmVar2 = top->next; + motive_allocator()->free(top); + } + else + { + ppmVar2 = &top->next; + } + top = *ppmVar2; + } + + if (_motive_temp != NULL) + { + *_motive_temp_tail = _motive_top; + _motive_top = _motive_temp; + _motive_temp = NULL; + } + + _motive_temp_tail = NULL; + if (_motive_top == NULL) + { + activity = ACT_NONE; + } + } + + void widget::dispatcher(xBase*, U32 event, const F32* toParam, xBase*) + { + switch (event) + { + case eEventEnable: + enable(); + break; + case eEventDisable: + disable(); + break; + case eEventVisible: + case eEventFastVisible: + flag.visible = 1; + break; + case eEventInvisible: + case eEventFastInvisible: + flag.visible = 0; + break; + case eEventDispatcher_ShowHud: + show(); + break; + case eEventDispatcher_HideHud: + hide(); + break; + } + } + + // Equivalent: scheduling + U32 widget::type() const + { + static U32 myid = xStrHash(a->type_name()); + return myid; + } + + bool widget::is(U32 id) const + { + return id == widget::type(); + } + + void widget::show() + { + clear_motives(); + + activity = ACT_SHOW; + + F32 dVar8 = start_rc.loc.x - rc.loc.x; + F32 dVar7 = start_rc.loc.y - rc.loc.y; + F32 fVar1 = dVar8 * dVar8 + dVar7 * dVar7; + if (fVar1 <= 0.000000009999999f) + { + rc.loc = start_rc.loc; + rc.a = start_rc.a; + } + else + { + F32 dVar4 = xsqrt(fVar1); + F32 dVar6 = 10.0f * dVar7; + fVar1 = 10.0f * dVar8; + F32 dVar5 = (-(fVar1 * fVar1 + (dVar6 * dVar6)) / (2.0f * dVar4)); + + add_motive(motive(&rc.loc.x, fVar1, dVar8, (dVar5 * dVar8) / dVar4, + accelerate_motive_update, NULL)); + + add_motive(motive(&rc.loc.y, dVar6, dVar7, (dVar5 * dVar7) / dVar4, + accelerate_motive_update, NULL)); + + fVar1 = start_rc.a - rc.a; + add_motive(motive(&rc.a, 3.0f * fVar1, fVar1, 0.0f, linear_motive_update, NULL)); + } + } + + // Nonmatching: not finished + void widget::hide() + { + activity = ACT_HIDE; + + F32 fVar1 = start_rc.size.x; + F32 fVar3 = start_rc.size.y; + F32 fVar7 = (start_rc.loc.x - 0.5f) + 0.5f * fVar1; + F32 fVar8 = (start_rc.loc.y - 0.5f) + 0.5f * fVar3; + if (iabs(iabs(fVar7) + iabs(fVar8)) <= 0.0001f) + { + rc.a = 0.0f; + } + else + { + F32 fVar5; + F32 fVar6; + if (iabs(fVar7) > iabs(fVar8)) + { + if (fVar8 >= 0.0f) + { + fVar6 = 0.5f + fVar3; + } + else + { + fVar6 = -0.5f - fVar3; + } + fVar5 = (fVar6 * fVar7) / fVar8; + } + else + { + if (fVar7 >= 0.5f) + { + fVar5 = 0.5f + fVar1; + } + else + { + fVar5 = -0.5f - fVar1; + } + fVar6 = (fVar5 * fVar8) / fVar7; + } + + F32 dVar11 = 255.0f + (fVar6 - 0.5f * fVar3) - rc.loc.y; + F32 dVar12 = 255.0f + (fVar5 - 0.5f * fVar1) - rc.loc.x; + F32 dVar10 = xsqrt(dVar12 * dVar12 + dVar11 * dVar11); + + add_motive( + motive(&rc.loc.x, 0.0f, dVar12, dVar12 * dVar10, accelerate_motive_update, NULL)); + + add_motive( + motive(&rc.loc.y, 0.0f, dVar11, dVar11 * dVar10, accelerate_motive_update, NULL)); + + fVar1 = -rc.a; + add_motive(motive(&rc.a, 0.4f * fVar1, dVar11, 0.0f, linear_motive_update, NULL)); + } + } + + namespace + { + + void fp_setup(widget& w) + { + w.setup(); + } + + void fp_render(widget& w) + { + if (w.visible()) + { + w.render(); + } + } + + } // namespace + + void widget::add_motive(const motive& m) + { + motive_node* node = (motive_node*)motive_allocator()->alloc(); + new (node) motive(m); + + if (_motive_temp_tail == NULL) + { + node->next = _motive_top; + _motive_top = node; + } + else + { + if (_motive_temp == NULL) + { + _motive_temp_tail = &node->next; + } + node->next = _motive_temp; + _motive_temp = node; + } + } + + void widget::clear_motives() + { + activity = ACT_NONE; + motive_node* node = _motive_top; + while (node != NULL) + { + node->m.finish(); + _motive_top = node->next; + motive_allocator()->free(node); + node = _motive_top; + } + } + + void widget::clear_motives(bool (*fp_update)(widget&, motive&, F32), void* context) + { + motive_node** ppmVar2 = &_motive_top; + motive_node* node = _motive_top; + + while (node != NULL) + { + if (node->m.fp_update == fp_update && node->m.context == context) + { + *ppmVar2 = node->next; + motive_allocator()->free(node); + } + else + { + ppmVar2 = &node->next; + } + node = *ppmVar2; + } + + if (_motive_top == NULL) + { + activity = ACT_NONE; + } + } + + bool linear_motive_update(widget& w, motive& m, F32 dt) + { + F32 fVar1 = dt * m.delta; + F32 fVar2 = m.max_offset - m.offset; + if ((fVar1 >= 0.0f && fVar1 >= fVar2) || (fVar1 < 0.0f && fVar1 <= fVar2)) + { + *m.value += fVar2; + m.offset = m.max_offset; + return false; + } + else + { + *m.value += fVar1; + m.offset += fVar1; + return true; + } + } + + // Equivalent: regalloc + bool accelerate_motive_update(widget& w, motive& m, F32 dt) + { + F32 fVar2; + F32 fVar1; + F32 delta; + + fVar1 = 0.5f * m.accel; + delta = m.delta; + m.delta = dt * m.accel + delta; + delta *= dt; + fVar1 *= dt; + fVar1 = dt * fVar1 + delta; + + fVar2 = m.max_offset - m.offset; + + if ((fVar1 >= 0.0f && fVar1 >= fVar2) || (fVar1 < 0.0f && fVar1 <= fVar2)) + { + *m.value += fVar2; + m.offset = m.max_offset; + return false; + } + else + { + *m.value += fVar1; + m.offset += fVar1; + return true; + } + } + + void __deadstripped_xHud() + { + // "%d" was used in a deadstripped function. This function forces it to be used. + xStrHash("%d"); + } + + xModelInstance* load_model(U32 modelID) + { + U32 size; + void* info = xSTFindAsset(xStrHashCat(modelID, ".minf"), &size); // xModelAssetInfo* + if (info != NULL) + { + return zEntRecurseModelInfo(info, NULL); + } + + info = xSTFindAsset(modelID, &size); // RpAtomic* + if (info == NULL) + { + info = xSTFindAsset(xStrHashCat(modelID, ".dff"), &size); // RpAtomic* + } + if (info == NULL) + { + return NULL; + } + + return xModelInstanceAlloc((RpAtomic*)info, NULL, 0, 0, NULL); + } + +} // namespace xhud diff --git a/src/SB/Core/x/xHud.h b/src/SB/Core/x/xHud.h index d8fafcf0..20a27f01 100644 --- a/src/SB/Core/x/xHud.h +++ b/src/SB/Core/x/xHud.h @@ -11,6 +11,25 @@ typedef struct widget; namespace xhud { + struct block_allocator + { + U32 _block_size; + U32 _alloc_size; + void* _top; // FIXME: This is a holder* + block_allocator* _next_alloc; + + static block_allocator* _head_alloc; + + block_allocator(U32 a0, U32 a1); + + static void flush_all(); + void flush(); + void set_increment(U32 a0); + void size_reserve(U32 size); + void* alloc(); + void free(void* ptr); + }; + struct color32u { U8 r; @@ -30,31 +49,20 @@ namespace xhud F32 a; }; - struct motive - { - F32* value; - F32 delta; - F32 start_delta; - F32 max_offset; - F32 offset; - F32 accel; - U8 (*fp_update)(widget&, motive&, F32); - void* context; - U8 inverse; - }; - - struct motive_node - { - motive m; - motive_node* next; - }; - struct asset : xDynAsset { xVec3 loc; xVec3 size; + + static const char* type_name() + { + return "hud"; + } }; + struct motive; + struct motive_node; + struct widget { struct @@ -64,7 +72,7 @@ namespace xhud } flag; render_context rc; render_context start_rc; - asset* a; + const asset* a; enum _enum { ACT_NONE, @@ -72,28 +80,125 @@ namespace xhud ACT_HIDE, MAX_ACT } activity; + + virtual void destroy() {} + virtual U32 type() const; + virtual bool is(U32 id) const; + virtual void init() {} + virtual void setup() { presetup(); } + virtual void update(F32 dt) { updater(dt); } + virtual void render() {} + virtual void dispatch(xBase* b1, U32 event, const F32* toParam, xBase* b2); + motive_node* _motive_top; motive_node* _motive_temp; motive_node** _motive_temp_tail; - widget(asset& a); - virtual void destroy(); - void init(); - void setup(); - void enable(); - void update(F32 dt); - void dispatch(U32 event); - void clear_motives(U8 (*fp_update)(widget&, motive&, F32), void* context); - void add_motive(motive& m); + static void disable_all(bool); + static void setup_all(); + static void update_all(F32 dt); + static void render_all(); + + static void init_base(xBase&, const xBaseAsset&, unsigned long); + static S32 cb_dispatch(xBase*, xBase*, U32, const F32*, xBase*); + + widget(const asset& a); + void destruct(); + void presetup(); + void updater(F32 dt); + void dispatcher(xBase*, U32, const F32*, xBase*); + + + void disable() + { + flag.enabled = 0; + } + + void add_tweaks() + { + + } + + void enable() + { + flag.enabled = 1; + } + + void clear_motives(); + void clear_motives(bool (*fp_update)(widget&, motive&, F32), void* context); + void add_motive(const motive& m); void hide(); void show(); - void updater(F32 dt); - void presetup(); - void destruct(); - void add_tweaks(); - void render(); - void debug_render(); + + bool visible() const + { + return flag.visible && enabled(); + } + + U8 enabled() const + { + return flag.enabled; + } + + static block_allocator* motive_allocator(); }; + + struct motive + { + F32* value; + F32 delta; + F32 start_delta; + F32 max_offset; + F32 offset; + F32 accel; + bool (*fp_update)(widget&, motive&, F32); + void* context; + U8 inverse; + + motive(F32* value, F32 delta, F32 max_offset, F32 accel, bool (*fp_update)(xhud::widget&, motive&, F32), void* context) + { + this->value = value; + this->delta = delta; + this->start_delta = delta; + this->max_offset = max_offset; + this->offset = 0.0f; + this->accel = accel; + this->fp_update = fp_update; + this->context = context; + } + motive(const motive& other); + + bool update(widget& w, F32 dt) + { + return fp_update(w, *this, dt); + } + + void finish() + { + if (value != NULL) + { + *value += (max_offset - offset); + } + offset = max_offset; + } + }; + + struct motive_node + { + motive m; + motive_node* next; + }; + + void init(); + void setup(); + void destroy(); + void update(F32 dt); + void render(); + + bool linear_motive_update(widget& w, motive& m, F32); + bool accelerate_motive_update(widget& w, motive& m, F32); + + xModelInstance* load_model(U32); }; // namespace xhud #endif diff --git a/src/SB/Core/x/xHudText.cpp b/src/SB/Core/x/xHudText.cpp index 59fd4faf..5c9597a5 100644 --- a/src/SB/Core/x/xHudText.cpp +++ b/src/SB/Core/x/xHudText.cpp @@ -1,3 +1,143 @@ #include "xHudText.h" +#include "xstransvc.h" +#include "xTextAsset.h" +#include "zScene.h" +#include "zTextBox.h" +#include +#include #include + +void xhud::text_widget::load(xBase& data, xDynAsset& asset, size_t) +{ + init_base(data, asset, sizeof(xBase) + sizeof(text_widget)); + text_widget* widget = (text_widget*)(&data + 1); + new (widget) text_widget((text_asset&)asset); +} + +xhud::text_widget::text_widget(const xhud::text_asset& asset) : widget(asset) +{ + text[0] = '\0'; +} + +void xhud::text_widget::destruct() +{ + widget::destruct(); +} + +void xhud::text_widget::setup() +{ + presetup(); + text_asset* asset = (text_asset*)a; + ztextbox* textbox = (ztextbox*)zSceneFindObject(asset->text_box); + if (textbox == NULL) + { + tb = xtextbox::create(); + return; + } + + textbox->refresh(); + tb = textbox->tb; + textbox->get_text(text, sizeof(text)); + if (asset->text != 0) + { + xTextAsset* tasset = (xTextAsset*)xSTFindAsset(asset->text, NULL); + if (tasset != NULL) + { + char* text_asset_text = xTextAssetGetText(tasset); + U32 len = strlen(text_asset_text); + if (len >= sizeof(text)) + { + len = sizeof(text) - 1; + } + memcpy(text, text_asset_text, len); + text[len] = '\0'; + } + } + + rc.r = tb.font.color.r / 255.0f; + rc.g = tb.font.color.g / 255.0f; + rc.b = tb.font.color.b / 255.0f; +} + +void xhud::text_widget::destroy() +{ + destruct(); +} + +// Equivalent: scheduling +U32 xhud::text_widget::type() const +{ + static U32 myid = xStrHash(text_asset::type_name()); + return myid; +} + +bool xhud::text_widget::is(U32 id) const +{ + return id == text_widget::type() || widget::is(id); +} + +void __deadstripped_xHudText(xhud::text_widget& w) +{ + // NOTE(tgsm): This function only exists to assure correct value ordering in .sdata2. + // I don't actually know what this function contains, and I wouldn't use it if I were you. + w.tb.font.width = 0.0f; +} + +void xhud::text_widget::update(F32 dt) +{ + updater(dt); + + tb.set_text(text); + tb.bounds.x = rc.loc.x; + tb.bounds.y = rc.loc.y; + tb.bounds.w = rc.size.x; + tb.bounds.h = rc.size.y; + tb.font.clip = tb.bounds; + + tb.font.color.r = (rc.r * 255.0f) + 0.5f; + tb.font.color.g = (rc.g * 255.0f) + 0.5f; + tb.font.color.b = (rc.b * 255.0f) + 0.5f; + tb.font.color.a = (rc.a * 255.0f) + 0.5f; +} + +void xhud::text_widget::render() +{ + F32 boundX = tb.bounds.x; + F32 boundY = tb.bounds.y; + U8 oldRed = tb.font.color.r; + U8 oldGreen = tb.font.color.g; + U8 oldBlue = tb.font.color.b; + U8 oldAlpha = tb.font.color.a; + + U8 alpha; + F32 fVar1 = (oldAlpha * 200.0f) / 255.0f; + if (fVar1 > 255.0f) + { + alpha = 0xFF; + } + else if (fVar1 < 0.0f) + { + alpha = 0; + } + else + { + alpha = fVar1; + } + + tb.bounds.x += 0.0075f; + tb.bounds.y += 0.0075f; + tb.font.color.r = 0x00; + tb.font.color.g = 0x46; + tb.font.color.b = 0x55; + tb.font.color.a = alpha; + tb.render(true); + + tb.bounds.x = boundX; + tb.bounds.y = boundY; + tb.font.color.r = oldRed; + tb.font.color.g = oldGreen; + tb.font.color.b = oldBlue; + tb.font.color.a = oldAlpha; + tb.render(true); +} diff --git a/src/SB/Core/x/xHudText.h b/src/SB/Core/x/xHudText.h index 4628499d..19c2e931 100644 --- a/src/SB/Core/x/xHudText.h +++ b/src/SB/Core/x/xHudText.h @@ -10,6 +10,11 @@ namespace xhud { U32 text_box; U32 text; + + static const char* type_name() + { + return "hud:text"; + } }; struct text_widget : widget @@ -22,11 +27,14 @@ namespace xhud static void load(xBase& data, xDynAsset& asset, size_t); - void render(); - void update(F32 dt); - U8 is(U32 id); - void destroy(); - void setup(); + void destruct(); + + virtual void destroy(); + virtual U32 type() const; + virtual bool is(U32 id) const; + virtual void setup(); + virtual void update(F32 dt); + virtual void render(); }; }; // namespace xhud diff --git a/src/SB/Game/zTextBox.cpp b/src/SB/Game/zTextBox.cpp index 7d4837d1..81a4797e 100644 --- a/src/SB/Game/zTextBox.cpp +++ b/src/SB/Game/zTextBox.cpp @@ -401,7 +401,7 @@ void ztextbox::refresh() } } -void ztextbox::get_text(char* buffer, U32 buffer_size) const +void ztextbox::get_text(char* buffer, size_t buffer_size) const { const char* const* it = segments; const char* const* end = it + segments_size; diff --git a/src/SB/Game/zTextBox.h b/src/SB/Game/zTextBox.h index 0e4f5304..203cd6e7 100644 --- a/src/SB/Game/zTextBox.h +++ b/src/SB/Game/zTextBox.h @@ -96,7 +96,7 @@ struct ztextbox : xBase void add_text(U32 id); void clear_text(); void refresh(); - void get_text(char* buffer, U32 buffer_size) const; + void get_text(char* buffer, size_t buffer_size) const; bool visible(); };