diff --git a/dwarf/SB/Game/zTalkBox.cpp b/dwarf/SB/Game/zTalkBox.cpp index e30593a53..d3aef6bca 100644 --- a/dwarf/SB/Game/zTalkBox.cpp +++ b/dwarf/SB/Game/zTalkBox.cpp @@ -4637,4 +4637,3 @@ static enum state_enum update() { // Range: 0x322690 -> 0x322698 } } - diff --git a/src/SB/Core/x/containers.h b/src/SB/Core/x/containers.h index 5904a73b9..a2a467a95 100644 --- a/src/SB/Core/x/containers.h +++ b/src/SB/Core/x/containers.h @@ -42,7 +42,8 @@ template struct static_queue U32 _max_size_mask; T* _buffer; - struct iterator { + struct iterator + { U32 _it; static_queue* _owner; @@ -68,12 +69,12 @@ template struct static_queue return this; } }; - + bool empty() const { return size() == 0; } - + U32 size() const { return _size; @@ -93,7 +94,7 @@ template struct static_queue _buffer = (T*)xMemAlloc(gActiveHeap, sizeof(T) * (size), 0); clear(); } - + void clear() { _size = 0; @@ -140,7 +141,7 @@ template struct static_queue { return size() == max_size(); } - + U32 max_size() const { return _max_size - 1; @@ -160,7 +161,7 @@ template struct static_queue _size -= mod_max_size(other._it - it._it); } } - + iterator end() const { iterator it; @@ -190,11 +191,11 @@ template struct fixed_queue } void pop_front() { - _first = (_first + 1) & N; + _first = (_first + 1) % (N + 1); } void push_front() { - _first = (_first + N) & N; + _first = (_first + N) & N; } void push_front(const T& data) { @@ -202,7 +203,10 @@ template struct fixed_queue T& new_front = front(); new_front = data; } - void push_back(); + void push_back() + { + _last = (_last + 1) % (N + 1); + } U32 max_size() const { return N; @@ -229,7 +233,8 @@ template struct fixed_queue return _last - _first; } - struct iterator { + struct iterator + { U32 _it; fixed_queue* _owner; @@ -245,8 +250,8 @@ template struct fixed_queue iterator* operator+=(S32 value) { - value += _it; - _it = (value + N) & N; + value = _it + value; + _it = (value + N + 1) % (N + 1); return this; } @@ -296,5 +301,4 @@ template struct fixed_queue return create_iterator(_last); } }; - #endif diff --git a/src/SB/Core/x/xFont.h b/src/SB/Core/x/xFont.h index b43a621df..d6a35a50c 100644 --- a/src/SB/Core/x/xFont.h +++ b/src/SB/Core/x/xFont.h @@ -96,8 +96,8 @@ struct xtextbox void* context; basic_rect bounds; basic_rect render_bounds; - const callback* cb; - tag_type* tag; + const callback* cb; //0x34 + tag_type* tag; //0x38 void intersect_flags(const jot& other); void reset_flags(); @@ -138,9 +138,9 @@ struct xtextbox struct tag_entry { substr name; - char op; - substr* args; - size_t args_size; + char op; //0x08 + substr* args; //0x0C + size_t args_size; //0x10 }; struct tag_entry_list @@ -274,6 +274,7 @@ struct xtextbox::layout F32 yextent(F32 max, S32& size, S32 begin_jot, S32 end_jot) const; bool changed(const xtextbox& ctb); size_t jots_size() const; + jot* jots() const; }; void render_fill_rect(const basic_rect& bounds, iColor_tag color); diff --git a/src/SB/Core/x/xSnd.h b/src/SB/Core/x/xSnd.h index 38527867e..b1d06586e 100644 --- a/src/SB/Core/x/xSnd.h +++ b/src/SB/Core/x/xSnd.h @@ -95,6 +95,69 @@ template struct sound_queue void play(U32 id, F32 vol, F32 pitch, U32 priority, U32 flags, U32 parentID, sound_category snd_category); void push(U32 id); + + void pop() + { + xSndStop(_playing[head]); + head = (head + 1) % (N + 1); + } + S32 size() const + { + if (tail >= head) + { + return tail - head; + } + return tail + (N + 1) - head; + } + void clear() + { + while (size() > 0) + { + pop(); + } + } + U32 recent(S32 index) const + { + S32 i = tail - index - 1; + if (i < 0) + { + i += (N + 1); + } + return _playing[i]; + } + bool playing(S32 index, bool streaming) const + { + S32 count = size(); + S32 i; + + if (index < 0 || index > count) + { + index = count; + } + + if (streaming) + { + for (i = 0; i < index; i++) + { + if (!xSndIsPlayingByHandle(recent(i))) + { + return false; + } + } + return true; + } + else + { + for (i = 0; i < index; i++) + { + if (xSndIsPlayingByHandle(recent(i))) + { + return true; + } + } + return false; + } + } }; enum sound_effect diff --git a/src/SB/Game/zTalkBox.cpp b/src/SB/Game/zTalkBox.cpp index 3e9ac6250..1c063d4dd 100644 --- a/src/SB/Game/zTalkBox.cpp +++ b/src/SB/Game/zTalkBox.cpp @@ -2,13 +2,34 @@ #include "zTalkBox.h" #include "xDebug.h" #include "zEntPlayer.h" +#include "zGame.h" +#include "zGlobals.h" +#include "zMusic.h" #include - +#include "zGame.h" +#include "xSnd.h" +#include "xEvent.h" +#include "zBase.h" + +U32 xGroupGetCount(xGroup* g); +xBase* xGroupGetItemPtr(xGroup* g, U32 index); +void zEntPlayerSpeakStart(U32 sound, U32 param_2, S32 param_3); +void zEntPlayer_SNDStopStream(); +U8 xSndStreamLock(U32 lock, sound_category category, bool enable); +void xSndStreamUnlock(U32 lock); +U8 xSndStreamReady(U32 lock); +S32 zGameIsPaused(); namespace { shared_type shared; + static void update_prompt_status(F32 dt); + static void update_quit_status(F32 dt); + static void stop(); + static U32* pad_pressed(); - void stop_audio_effect(); + static U32 new_tags_size = 9; + static F32 music_fade = 0.25f; + static F32 music_fade_delay = 0.5f; void speak_stop() { @@ -27,14 +48,12 @@ namespace static void trigger(U32 event) { - // shared.delay_events = 0; // -0x7286 - // shared.triggered._first = 0; // -0x7280 - - if (shared.delay_events == 0) + if (shared.delay_events) { shared.triggered.push_back(); - shared.triggered.back(); - shared.active->id = 0; + trigger_pair& tp = shared.triggered.back(); + tp.origin = shared.active; + tp.event = event; } else { @@ -102,362 +121,573 @@ namespace } } - void flush_triggered() + static void flush_triggered() { + while (!shared.triggered.empty()) + { + trigger_pair tp = shared.triggered.front(); + shared.triggered.pop_front(); + zEntEvent(tp.origin, tp.origin, tp.event); + } } - void deactivate() + static bool read_bool(const substr& s, bool def) { - stop_audio_effect(); + static const substr positive[6] = { { "yes", 3 }, { "y", 1 }, { "1", 1 }, + { "true", 4 }, { "t", 1 }, { "on", 2 } - ztalkbox* active = shared.active; - if (active == NULL) + }; + static const substr negative[6] = { { "no", 3 }, { "n", 1 }, { "0", 1 }, + { "false", 5 }, { "f", 1 }, { "off", 3 } + + }; + if (def) { - return; + for (U32 i = 0; i < 6; i++) + { + if (icompare(s, negative[i]) == 0) + { + return false; + } + } + return true; } - - if (active->prompt_box != NULL) + else { - active->prompt_box->deactivate(); + for (U32 i = 0; i < 6; i++) + { + if (icompare(s, positive[i]) == 0) + { + return true; + } + } + return false; } - shared.active = NULL; - active->dialog_box->flag.visible = false; } - void stop_wait(ztalkbox& talkbox, const F32*, U32); + static void reset_auto_wait() + { + const ztalkbox::asset_type* a = shared.active->asset; - static void stop(); + shared.auto_wait.type.time = a->auto_wait.type.time; + shared.auto_wait.type.prompt = a->auto_wait.type.prompt; + shared.auto_wait.type.sound = a->auto_wait.type.sound; + shared.auto_wait.type.event = a->auto_wait.type.event; - // Equivalent: see fixme - S32 cb_dispatch(xBase*, xBase* to, U32 event, const F32* argf, xBase*) - { - //shared.unk8D7A = 1; - ztalkbox& talkbox = *(ztalkbox*)to; + shared.auto_wait.delay = a->auto_wait.delay; - switch (event) + shared.auto_wait.need = 0; + + if (a->auto_wait.which_event <= 0 || a->auto_wait.which_event >= 32) { - case 10: - case 88: - talkbox.reset(); - break; - case 4: - case 504: - talkbox.hide(); - break; - case 3: - case 503: - talkbox.show(); - break; - case 335: + shared.auto_wait.event_mask = -1; + } + else { - U32 textID = ((U32*)argf == NULL) ? 0 : *(U32*)argf; - talkbox.start_talk(textID, NULL, NULL); - flush_triggered(); - break; + shared.auto_wait.event_mask = 1u << a->auto_wait.which_event; } - case 336: - talkbox.stop_talk(); - flush_triggered(); - break; - case 352: - if (argf == NULL) + + shared.auto_wait.query = Q_SKIP; + } + + static void load_wait_context(wait_context& c, const xtextbox::tag_entry_list& el) + { + static S32 v[32]; + + c.reset_type(); + c.delay = 0.0f; + + xtextbox::tag_entry* e = xtextbox::find_entry(el, substr::create("wait", 4)); + + if (e && xtextbox::read_list(*e, &c.delay, 1) == 1) + { + c.type.time = true; + } + + xtextbox::tag_entry* prompt = xtextbox::find_entry(el, substr::create("prompt", 6)); + + if (prompt) + { + c.type.prompt = true; + if (prompt->op == '=' && prompt->args_size == 1 && + icompare(*prompt->args, substr::create("yesno", 5)) == 0) { - stop_wait(talkbox, NULL, 0); + c.query = Q_YESNO; } else { - stop_wait(talkbox, argf, 4); + c.query = Q_SKIP; } - flush_triggered(); - break; - case 334: - if ((U32*)argf != NULL) + } + + e = xtextbox::find_entry(el, substr::create("sound", 5)); + c.type.sound = (e != NULL); + + e = xtextbox::find_entry(el, substr::create("event", 5)); + if (e) + { + c.type.event = true; + U32 r = xtextbox::read_list(*e, v, 32); + if (r == 0) { - talkbox.set_text(*(U32*)argf); + c.event_mask = -1; } - break; - case 338: - if ((U32*)argf != NULL) + else if (e->op == '=') { - talkbox.add_text(*(U32*)argf); + for (U32 i = 0; i < r; i++) + { + if ((U32)v[i] < 32) + { + c.event_mask |= (1 << v[i]); + } + } + if (c.event_mask & 1) + { + c.event_mask = -1; + } } - break; - case 339: - talkbox.clear_text(); - break; - - // FIXME: Figure out the right no-op cases - case 75: - case 76: - case 342: - case 343: - case 344: - case 345: - case 346: - case 347: - case 348: - case 349: - case 350: - case 351: - case 353: - case 356: - case 357: - case 358: - case 359: - // case 452: - // case 453: - // case 454: - case 465: - case 466: - break; } + e = xtextbox::find_entry(el, substr::create("need", 4)); - //shared.unk8D7A = 0; - return 1; + if (e && (e->op != '=' || e->args_size == 0 || !read_bool(*e->args, true))) + { + c.need = true; + } + else + { + c.need = false; + } } - char* load_text(U32 id) + void wait_context::reset_type() { - if (id == 0) + *(U16*)&this->type = 0; + } + static void parse_tag_auto_wait(xtextbox::jot& j, const xtextbox& ctb, const xtextbox& tb, + const xtextbox::split_tag& ti) + { + if (!shared.active) { - return NULL; + return; } - - // What type is this? - void* asset = xSTFindAsset(id, NULL); - if (asset == NULL) + if (&shared.active->dialog_box->tb != &tb) { - return NULL; + return; } - // HACK - return (char*)(asset) + 4; - } - -} // namespace + wait_context* c = (wait_context*)j.context; + j.context_size = sizeof(wait_context); -void ztalkbox::load(const asset_type& tasset) -{ - xBaseInit(this, (xBaseAsset*)&tasset); - baseType = eBaseTypeTalkBox; - asset = &tasset; - eventFunc = cb_dispatch; - if (linkCount != 0) - { - link = (xLinkAsset*)(&tasset + 1); - } + xtextbox::tag_entry_list el = xtextbox::read_tag(ti.tag); + xtextbox::tag_entry& e = *el.entries; - dialog_box = (ztextbox*)zSceneFindObject(tasset.dialog_box); + if (e.op == '=' && e.args_size != 0 && !read_bool(*e.args, true)) + { + c->reset_type(); + c->delay = 0.0f; + } + else + { + el.size--; + el.entries++; + load_wait_context(*c, el); + } - if (tasset.prompt_box == 0) - { - prompt_box = NULL; + shared.auto_wait = *c; } - else + wait_context& wait_context::operator=(const wait_context& rhs) { - prompt_box = (ztextbox*)zSceneFindObject(tasset.prompt_box); + *(U16*)&type = *(const U16*)&rhs.type; + need = rhs.need; + delay = rhs.delay; + event_mask = rhs.event_mask; + query = rhs.query; + return *this; } - - if (tasset.quit_box == 0) + static void reset_tag_auto_wait(xtextbox::jot& j, const xtextbox&, const xtextbox& ctb, + const xtextbox::split_tag&) { - quit_box = NULL; + if (!shared.active) + { + return; + } + if (&shared.active->dialog_box->tb != &ctb) + { + return; + } + + wait_context& c = *(wait_context*)j.context; + j.context_size = sizeof(wait_context); + reset_auto_wait(); + c = shared.auto_wait; } - else + static U8 trigger_auto_wait(const xtextbox::jot& j) { - quit_box = (ztextbox*)zSceneFindObject(tasset.quit_box); + shared.auto_wait = *(const wait_context*)j.context; + + return 1; } + static void parse_tag_signal(xtextbox::jot& j, const xtextbox& ctb, const xtextbox& tb, + const xtextbox::split_tag& ti) + { + if (!shared.active) + { + return; + } + if (&shared.active->dialog_box->tb != &tb) + { + return; + } - prompt.skip = load_text(tasset.prompt.skip); - prompt.noskip = load_text(tasset.prompt.noskip); - prompt.quit = load_text(tasset.prompt.quit); - prompt.noquit = load_text(tasset.prompt.noquit); - prompt.yesno = load_text(tasset.prompt.yesno); + S32 v[20]; - reset(); -} + signal_context& c = *(signal_context*)&j.context; + c.flags = 0; -void ztalkbox::reset() -{ - flag.visible = true; - if (shared.active == this) - { - deactivate(); - } -} + xtextbox::tag_entry_list el = xtextbox::read_tag(ti.tag); -namespace -{ - void state_type::start() - { - } + if (el.entries->op != ':') + { + return; + } - void state_type::stop() - { - } -} // namespace + U32 r = xtextbox::read_list(*el.entries, v, 20); + if (r == 0) + { + c.flags = 0x7FFFFFFF; + } + else + { + for (U32 i = 0; i < r; i++) + { + if ((U32)v[i] < 20) + { + c.flags |= (1 << v[i]); + } + } + } -void ztalkbox::clear_text() -{ - set_text((const char*)NULL); -} + el.entries++; + el.size--; -void ztalkbox::stop_talk() -{ - if (shared.active == this) - { - stop(); + xtextbox::tag_entry* e = xtextbox::find_entry(el, substr::create("need", 4)); + if (e) + { + if (e->op != '=' || !e->args_size || !read_bool(*e->args, true)) + { + c.flags |= 0x80000000; + } + } } -} - -void ztalkbox::stop_wait(U32 x) -{ - if (shared.active == this) + static bool trigger_signal(const xtextbox::jot& j) { - shared.wait_event_mask = shared.wait_event_mask | x; - } -} + signal_context& c = *(signal_context*)&j.context; + static const U32 signals[20] = { 0x156, 0x157, 0x158, 0x159, 0x15A, 0x15B, 0x15C, + 0x15D, 0x15E, 0x15F, 0x1C9, 0x1CA, 0x1CB, 0x1CC, + 0x1CD, 0x1CE, 0x1CF, 0x1D0, 0x1D1, 0x1D2 }; -void ztalkbox::show() -{ - flag.visible = true; - - if (shared.active != this) - { - return; - } + if (shared.quitting && !(c.flags & 0x80000000)) + { + return true; + } - if (prompt_box != NULL) - { - prompt_box->activate(); - } + U32 flags = c.flags; - // if (shared.unk8D78 != 0 && prompt.quit != 0 && quit_box != NULL) - // { - // quit_box->activate(); - // } -} + for (U32 i = 0; i < 20; i++) + { + if (flags & (1 << i)) + { + trigger(signals[i]); + } + } -void ztalkbox::hide() -{ - flag.visible = false; + if (shared.cb) + { + shared.cb->on_signal(c.flags & 0x7FFFFFFF); + } - if (prompt_box != NULL) - { - prompt_box->deactivate(); + return true; } - - if (quit_box != NULL) + static bool load_sound_type(sound_context& c, const xtextbox::tag_entry_list& el) { - quit_box->deactivate(); - } -} + xtextbox::tag_entry* e = xtextbox::find_entry(el, substr::create("volume", 6)); + if (e) + { + U32 r = xtextbox::read_list(*e, &c.volume.left, 2); + if (r == 1) + { + c.volume.right = c.volume.left; + } + if (r != 0) + { + c.type = sound_context::TYPE_VOLUME; + return true; + } + } -ztalkbox* ztalkbox::get_active() -{ - return shared.active; -} + c.volume.right = c.volume.left = 1.0f; + c.type = sound_context::TYPE_VOLUME; + return true; + } + static void parse_tag_sound(xtextbox::jot& j, const xtextbox& ctb, const xtextbox& tb, + const xtextbox::split_tag& ti) + { + if (!shared.active) + { + return; + } + if (&shared.active->dialog_box->tb != &tb) + { + return; + } -void ztalkbox::permit(U32 add_flags, U32 remove_flags) -{ - shared.permit &= ~remove_flags; - shared.permit |= add_flags; -} + sound_context* c = (sound_context*)j.context; -void ztalkbox::load(xBase& data, xDynAsset& asset, u32) -{ - ((ztalkbox&)data).load((const ztalkbox::asset_type&)asset); -} + xtextbox::tag_entry_list el = xtextbox::read_tag(ti.tag); + ::st_PKR_ASSET_TOCINFO ainfo; -void ztalkbox::load_settings(xIniFile& ini) -{ - shared.volume = xIniGetFloat(&ini, "talk_box.volume", 2.0f); - xDebugAddTweak("Talk Box|\01GlobalsGlobals|volume", &shared.volume, 0.1f, 10.0f, NULL, NULL, 0); -} + c->action = sound_context::ACTION_SET; -namespace -{ - static U8 read_bool(const substr& s, bool def) - { - extern const substr negative[6]; - extern const substr positive[6]; - if (def) + xtextbox::tag_entry* e = xtextbox::find_entry(el, substr::create("action", 6)); + if (e && e->args_size != 0) { - for (U32 i = 0; i < 6; ++i) + substr& action = *e->args; + if (icompare(action, substr::create("add", 3)) == 0) { - if (icompare(s, negative[i]) == 0) - return 0; + c->action = sound_context::ACTION_PUSH; } - return 1; + else if (icompare(action, substr::create("remove", 6)) == 0) + { + c->action = sound_context::ACTION_POP; + } + } + + xtextbox::tag_entry* first = el.entries; + if (c->action == sound_context::ACTION_POP) + { + c->id = 0; + } + else if (first->op == ':' && first->args_size != 0) + { + c->id = xStrHash(first->args->text, first->args->size); } else { - for (U32 i = 0; i < 6; ++i) + return; + } + + if (c->id != 0 && c->action != sound_context::ACTION_POP) + { + load_sound_type(*c, el); + } + + c->speaker = 2; + + e = xtextbox::find_entry(el, substr::create("speaker", 7)); + if (e && e->args_size != 0) + { + substr& action = *e->args; + if (icompare(action, substr::create("pc", 2)) == 0) { - if (icompare(s, positive[i]) == 0) - return 1; + c->speaker = 1; + } + else if (icompare(action, substr::create("none", 4)) == 0) + { + c->speaker = 0; + } + else if (action.size != 0) + { + c->speaker = xStrHash(action.text, action.size); } } - return 0; - } - static void parse_tag_pause(xtextbox::jot&, const xtextbox&, const xtextbox&, - const xtextbox::split_tag&) - { - } - static void parse_tag_trap(xtextbox::jot& j, const xtextbox&, const xtextbox&, - const xtextbox::split_tag& ti) - { - bool c = 0; + c->anim = 0; + + e = xtextbox::find_entry(el, substr::create("anim", 4)); + if (e && e->args_size != 0) + { + S32 anim = 0; + xtextbox::read_list(*e, &anim, 1); + c->anim = (U8)anim; + } - if (ti.action.size == 1 && ti.action.text[0] == '=') + if (c->id != 0) { - if (read_bool(ti.value, 1) != 0) + S32 result = xSTGetAssetInfo(c->id, &ainfo); + if (result) { - c = 1; + U32 source = ainfo.typeref->typetag; + if (source == 0x534E4420) + { + c->source = sound_context::SOURCE_MEMORY; + } + else if (source == 0x534E4453) + { + c->source = sound_context::SOURCE_STREAM; + } + else + { + return; + } + + j.context_size = sizeof(sound_context); } } - - *(bool*)&j.context = c; } - - static void reset_tag_pause(xtextbox::jot&, const xtextbox&, const xtextbox&, - const xtextbox::split_tag&) + void __deadstripped_zTalkbox() { + xprintf("pointer"); + xprintf("location"); } - static void reset_tag_sound(xtextbox::jot& j, const xtextbox&, const xtextbox& tb, - const xtextbox::split_tag&) + static void reset_tag_sound(xtextbox::jot& j, const xtextbox& ctb, const xtextbox& tb, + const xtextbox::split_tag& ti) { if (!shared.active) { return; } - if (&shared.active->dialog_box->tb != &tb) { return; } sound_context& c = *(sound_context*)j.context; - - j.context_size = 24; - + j.context_size = sizeof(sound_context); c.id = 0; c.action = sound_context::ACTION_SET; } - static void reset_tag_trap(xtextbox::jot& j, const xtextbox&, const xtextbox& ctb, - const xtextbox::split_tag&) + + static bool trigger_sound(const xtextbox::jot& j) { - if (!shared.active) + if (shared.quitting) { - return; + shared.sounds.clear(); + speak_stop(); + return true; } - if (&shared.active->dialog_box->tb != &ctb) + sound_context& c = *(sound_context*)j.context; + + switch (c.action) { - return; + case sound_context::ACTION_PUSH: + break; + case sound_context::ACTION_POP: + if (shared.sounds.size() > 0) + { + shared.sounds.pop(); + return 1; + } + break; + case sound_context::ACTION_SET: + shared.sounds.clear(); + speak_stop(); + return 1; } - *(bool*)&j.context = (shared.active->asset->trap != 0); + if (c.id == 0) + { + return true; + } + + F32 vol = c.volume.left; + if (!(vol > c.volume.right)) + { + vol = c.volume.right; + } + + shared.sounds.play(c.id, shared.volume * vol, 0.0f, 0x80, 0, + (U32)&shared.stream_locked[shared.next_stream], SND_CAT_DIALOG); + + shared.next_stream ^= 1; + + zNPCCommon* npc = NULL; + xEnt* player = NULL; + ztalkbox& talk = *shared.active; + + switch (c.speaker) + { + case 0: + break; + case 1: + player = (xEnt*)&globals.player.ent; + break; + case 2: + npc = talk.npc; + break; + default: + { + xBase* obj = zSceneFindObject(c.speaker); + if (obj) + { + if (obj->baseType == 0x2B) + { + npc = (zNPCCommon*)obj; + } + else if (obj->baseType == 0x03) + { + player = (xEnt*)&globals.player.ent; + } + else if (obj->baseType == 0x11) + { + U32 size = xGroupGetCount((xGroup*)&obj); + for (U32 i = 0; i < size; i++) + { + xBase* entry = xGroupGetItemPtr((xGroup*)&obj, i); + if (entry && entry->baseType == 0x2B) + { + npc = (zNPCCommon*)entry; + if (xEntIsVisible((const xEnt*)entry)) + { + break; + } + } + } + } + } + break; + } + } + + if (npc) + { + npc->SpeakStart(c.id, 0, c.anim - 1); + shared.speak_npc = npc; + } + else if (player) + { + zEntPlayerSpeakStart(c.id, 0, c.anim - 1); + shared.speak_player = 1; + } + + return true; + } + + static void parse_tag_pause(xtextbox::jot&, const xtextbox&, const xtextbox&, + const xtextbox::split_tag&) + { + } + + static void reset_tag_pause(xtextbox::jot&, const xtextbox&, const xtextbox&, + const xtextbox::split_tag&) + { + } + static U8 trigger_pause(const xtextbox::jot&) + { + return 1; + } + + static void parse_tag_allow_quit(xtextbox::jot& j, const xtextbox&, const xtextbox&, + const xtextbox::split_tag& ti) + { + U8 c = 0; + + if (ti.action.size != 1 || ti.action.text[0] != '=' || read_bool(ti.value, 1) != 0) + { + c = 1; + } + + *(U8*)&j.context = c; } static void reset_tag_allow_quit(xtextbox::jot& j, const xtextbox&, const xtextbox& ctb, @@ -476,134 +706,1291 @@ namespace *(bool*)&j.context = (shared.active->asset->allow_quit != 0); } - U8 trigger_pause(const xtextbox::jot&) + static U8 trigger_allow_quit(const xtextbox::jot& j) { + shared.allow_quit = (j.context != NULL); + return 1; } - U8 trigger_allow_quit(const xtextbox::jot& j) + static void parse_tag_teleport(xtextbox::jot& j, const xtextbox& ctb, const xtextbox& tb, + const xtextbox::split_tag& ti) { - shared.allow_quit = (j.context != NULL); + if (!shared.active) + { + return; + } - return 1; + U32 id = shared.active->asset->teleport; + teleport_context& c = *(teleport_context*)j.context; + + bool has_value = ti.action.size == 1 && *ti.action.text == ':' && ti.value.size != 0; + if (has_value) + { + id = xStrHash(ti.value.text, ti.value.size); + } + + if (id == 0) + { + return; + } + + c.use_yaw = false; + c.use_loc = false; + + xDynAsset* a = (xDynAsset*)xSTFindAsset(id, NULL); + if (!a) + { + return; + } + + if (a->type == xStrHash(location_asset::type_name())) + { + location_asset& ta = *(location_asset*)a; + c.use_loc = true; + c.loc = ta.loc; + } + else if (a->type == xStrHash(pointer_asset::type_name())) + { + pointer_asset& ta = *(pointer_asset*)a; + c.use_yaw = true; + c.use_loc = true; + c.loc = ta.loc; + c.yaw = ta.yaw * (3.14159265f / 180.0f); + } + + j.context_size = sizeof(teleport_context); } - U8 trigger_auto_wait(const xtextbox::jot& j) + static void move_player(const xVec3& loc) { - shared.auto_wait = *(const wait_context*)j.context; + RwMatrix* dst_mat = globals.player.ent.model->Mat; + xEntFrame* out_frame = globals.player.ent.frame; - return 1; + xVec3* dst_pos = (xVec3*)&dst_mat->pos; + xVec3* src_pos = &out_frame->mat.pos; + + dst_pos->operator=(*src_pos = loc); } - state_type::state_type(state_enum t) + + static void turn_player(F32 yaw) { - type = t; + xMat3x3& m = *(xMat3x3*)globals.player.ent.model->Mat; + xVec3 ang; + xMat3x3GetEuler(&m, &ang); + ang.x = yaw; + xMat3x3Euler(&m, &ang); } - wait_context& wait_context::operator=(const wait_context& rhs) //FIXME + static U8 trigger_teleport(const xtextbox::jot& j) { - type = rhs.type; - need = rhs.need; - delay = rhs.delay; - event_mask = rhs.event_mask; - query = rhs.query; - return *this; + if (j.context_size != sizeof(teleport_context)) + { + return 1; + } + + teleport_context* c = (teleport_context*)j.context; + + if (c->use_loc) + { + move_player(c->loc); + } + + if (c->use_yaw) + { + turn_player(c->yaw); + } + return 1; + } + + static void parse_tag_trap(xtextbox::jot& j, const xtextbox& ctb, const xtextbox& tb, + const xtextbox::split_tag& ti) + { + U8 c = false; + + if (ti.action.size == 1 && *ti.action.text == '=' && !read_bool(ti.value, true)) + { + } + else + { + c = true; + } + + *(U8*)&j.context = c; } - void stop_audio_effect() + static void reset_tag_trap(xtextbox::jot& j, const xtextbox&, const xtextbox& ctb, + const xtextbox::split_tag&) { if (!shared.active) { return; } - if (shared.active->asset == 0) + if (&shared.active->dialog_box->tb != &ctb) { return; } - } - static void reset_auto_wait() - { - const ztalkbox::asset_type* a = shared.active->asset; - - shared.auto_wait.type.time = a->auto_wait.type.time; - shared.auto_wait.type.prompt = a->auto_wait.type.prompt; - shared.auto_wait.type.sound = a->auto_wait.type.sound; - shared.auto_wait.type.event = a->auto_wait.type.event; - - shared.auto_wait.delay = a->auto_wait.delay; - shared.auto_wait.need = 0; + *(bool*)&j.context = (shared.active->asset->trap != 0); + } - if (a->auto_wait.which_event <= 0 || a->auto_wait.which_event >= 32) + static U8 trigger_trap(const xtextbox::jot& j) + { + if (j.context != 0) { - shared.auto_wait.event_mask = -1; + zEntPlayerControlOff(CONTROL_OWNER_TALK_BOX); } else { - shared.auto_wait.event_mask = 1u << a->auto_wait.which_event; + zEntPlayerControlOn(CONTROL_OWNER_TALK_BOX); } - - shared.auto_wait.query = Q_SKIP; + return 1; } - static void reset_tag_auto_wait(xtextbox::jot& j, const xtextbox&, const xtextbox& ctb, - const xtextbox::split_tag&) + + static void parse_tag_wait(xtextbox::jot& j, const xtextbox&, const xtextbox& ctb, + const xtextbox::split_tag& ti) { if (!shared.active) { return; } - if (&shared.active->dialog_box->tb != &ctb) + + if ((const void*)&shared.active->dialog_box->tb != (const void*)&ctb) { return; } wait_context& c = *(wait_context*)j.context; j.context_size = sizeof(wait_context); - reset_auto_wait(); - c = shared.auto_wait; - } -} // namespace + const substr& s = *(const substr*)&ti; + xtextbox::tag_entry_list el = xtextbox::read_tag(s); -void start_state_type::stop() -{ -} + if (el.size == 1 && el.entries->args_size == 0) + { + c = shared.auto_wait; + } + else + { + load_wait_context(c, el); + } + } + static U8 trigger_wait(const xtextbox::jot& j) + { + wait_context& c = *(wait_context*)j.context; -S8 start_state_type::update(xScene& scn, F32 dt) -{ - return 2; -} + if (shared.quitting != 0 && shared.allow_quit != 0 && c.need == 0) + { + return 1; + } -void next_state_type::stop() -{ -} + shared.wait = c; -void stop_state_type::start() -{ -} + return 0; + } + xtextbox::tag_type new_tags[] = { + { { "allow_quit", 10 }, + parse_tag_allow_quit, + reset_tag_allow_quit, + (void*)trigger_allow_quit }, + { { "aq", 2 }, parse_tag_allow_quit, reset_tag_allow_quit, (void*)trigger_allow_quit }, + { { "auto_wait", 9 }, parse_tag_auto_wait, reset_tag_auto_wait, (void*)trigger_auto_wait }, + { { "pause", 5 }, parse_tag_pause, reset_tag_pause, (void*)trigger_pause }, + { { "signal", 6 }, parse_tag_signal, NULL, (void*)trigger_signal }, + { { "sound", 5 }, parse_tag_sound, reset_tag_sound, (void*)trigger_sound }, + { { "teleport", 8 }, parse_tag_teleport, NULL, (void*)trigger_teleport }, + { { "trap", 4 }, parse_tag_trap, reset_tag_trap, (void*)trigger_trap }, + { { "wait", 4 }, parse_tag_wait, NULL, (void*)trigger_wait }, + }; + static void start_audio_effect(ztalkbox& talk) + { + static bool registered = false; -void stop_state_type::stop() -{ -} + if (!registered) + { + registered = true; + } -S8 stop_state_type::update(xScene& scn, F32 dt) -{ - return -1; -} + xDebugAddTweak("Temp|Talk Music Fade", &music_fade, 0.0f, 1.0f, NULL, NULL, 0); + xDebugAddTweak("Temp|Talk Music Fade Delay", &music_fade_delay, 0.0f, 10.0f, NULL, NULL, 0); -void wait_context::reset_type() -{ - *(U16*)&this->type = 0; -} + switch (talk.asset->audio_effect) + { + case 0: -static U8 trigger_trap(const xtextbox::jot& j) -{ - if (j.context != 0) + break; + case 1: + zMusicSetVolume(music_fade, music_fade_delay); + break; + } + + if (talk.asset->trap) + { + zEntPlayer_SNDStopStream(); + } + } + static void stop_audio_effect() + { + if ((shared.active) && (shared.active->asset->audio_effect != 1)) + { + zMusicSetVolume(1.0f, music_fade_delay); + return; + } + } + + static void deactivate() + { + stop_audio_effect(); + + ztalkbox* active = shared.active; + if (active == NULL) + { + return; + } + + if (active->prompt_box != NULL) + { + active->prompt_box->deactivate(); + } + shared.active = NULL; + active->dialog_box->flag.visible = false; + } + + static void activate(ztalkbox& t) + { + deactivate(); + + shared.active = &t; + + if (t.dialog_box->flag.active) + { + t.dialog_box->deactivate(); + } + + if (t.prompt_box != NULL && t.flag.visible) + { + t.prompt_box->activate(); + } + + start_audio_effect(t); + + t.dialog_box->flag.visible = 1; + } + static bool is_wait_jot(const xtextbox::jot& j) + { + return j.tag && j.tag->parse_tag == parse_tag_wait; + } + static bool layout_contains_streams() + { + tag_type* sound_tag = (tag_type*)xtextbox::find_format_tag(substr::create("sound", 5)); + jot* jots = (jot*)((xtextbox::layout*)&shared.lt)->jots(); + jot* end = jots + ((xtextbox::layout*)&shared.lt)->jots_size(); + + for (; jots != end; jots++) + { + if (jots->tag == sound_tag && jots->context_size == sizeof(sound_context)) + { + sound_context* c = (sound_context*)jots->context; + if (c->source == sound_context::SOURCE_STREAM) + { + return true; + } + } + } + + return false; + } + + static void lock_stream() + { + shared.stream_locked[0] = + xSndStreamLock((U32)&shared.stream_locked[0], SND_CAT_DIALOG, true); + shared.stream_locked[1] = + xSndStreamLock((U32)&shared.stream_locked[1], SND_CAT_DIALOG, true); + } + static void unlock_stream() + { + xSndStreamUnlock((U32)&shared.stream_locked[0]); + xSndStreamUnlock((U32)&shared.stream_locked[1]); + shared.stream_locked[0] = 0; + shared.stream_locked[1] = 0; + } + static void refresh_prompts() + { + ztalkbox& active = *shared.active; + + if (active.prompt_box) + { + char* message; + + if (shared.wait.type.prompt && shared.prompt_ready) + { + char* queries[2] = { (char*)active.prompt.skip, (char*)active.prompt.yesno }; + message = queries[shared.wait.query]; + } + else + { + message = (char*)active.prompt.noskip; + } + + if (message) + { + active.prompt_box->set_text(message); + if (active.flag.visible) + { + active.prompt_box->activate(); + } + } + else + { + active.prompt_box->deactivate(); + } + } + + if (active.quit_box) + { + if (shared.allow_quit && active.prompt.quit && shared.quit_ready) + { + active.quit_box->set_text(active.prompt.quit); + if (active.flag.visible) + { + active.quit_box->activate(); + } + } + else if (shared.allow_quit && shared.quit_delay <= 0.0f) + { + active.quit_box->deactivate(); + } + else if (active.prompt.noquit) + { + active.quit_box->set_text(active.prompt.noquit); + if (active.flag.visible) + { + active.quit_box->activate(); + } + } + else + { + active.quit_box->deactivate(); + } + } + } + static void update_prompt_status(F32 dt) + { + if (!shared.wait.type.prompt) + { + return; + } + + if (shared.prompt_ready) + { + return; + } + + shared.prompt_delay -= dt; + if (shared.prompt_delay > 0.0f) + { + return; + } + + if (!shared.stream_locked[shared.next_stream] || + xSndStreamReady((U32)&shared.stream_locked[shared.next_stream])) + { + shared.prompt_ready = true; + refresh_prompts(); + } + } + static void update_quit_status(F32 dt) + { + if (shared.quit_ready) + { + return; + } + + shared.quit_delay -= dt; + if (shared.quit_delay > 0.0f) + { + return; + } + + shared.quit_ready = true; + refresh_prompts(); + } + static void hide_prompts() + { + ztalkbox& active = *(ztalkbox*)shared.active; + + if (active.prompt_box) + { + active.prompt_box->deactivate(); + } + if (active.quit_box) + { + active.quit_box->deactivate(); + } + } + static void stop_wait(ztalkbox& e, const F32* args, u32 args_size) + { + if (shared.active != &e) + { + return; + } + + U32 mask = 0; + + for (U32 i = 0; i < args_size; i++) + { + U32 v = (U32)args[i]; + + if (v != 0 && v < 32) + { + mask |= (1 << v); + } + } + + if (mask == 0) + { + mask = 0xFFFFFFFF; + } + + e.stop_wait(mask); + } + + S32 cb_dispatch(xBase*, xBase* to, U32 event, const F32* argf, xBase*) + { + shared.delay_events = true; + ztalkbox& e = *(ztalkbox*)to; + + switch (event) + { + case eEventReset: + case eEventSceneEnd: + e.reset(); + break; + case eEventInvisible: + case eEventFastInvisible: + e.hide(); + break; + case eEventVisible: + case eEventFastVisible: + e.show(); + break; + case eEventStartConversation: + { + U32 textID = ((U32*)argf == NULL) ? 0 : *(U32*)argf; + e.start_talk(textID, NULL, NULL); + flush_triggered(); + break; + } + case eEventEndConversation: + e.stop_talk(); + flush_triggered(); + break; + case eEventTalkBox_StopWait: + if (argf == NULL) + { + stop_wait(e, NULL, 0); + } + else + { + stop_wait(e, argf, 4); + } + flush_triggered(); + break; + case eEventSetText: + if ((U32*)argf != NULL) + { + e.set_text(*(U32*)argf); + } + break; + case eEventAddText: + if ((U32*)argf != NULL) + { + e.add_text(*(U32*)argf); + } + break; + case eEventClearText: + e.clear_text(); + break; + + case eEventPadPressRight: + case eEventPadPressLeft: + case eEventOpenTBox: + case eEventCloseTBox: + case eEventTalkBox_OnSignal18: + case eEventTalkBox_OnSignal19: + case eEventTalkBox_OnStart: + case eEventTalkBox_OnYes: + case eEventTalkBox_OnNo: + break; + } + + shared.delay_events = false; + return 1; + } + + char* load_text(U32 id) + { + if (id == 0) + { + return NULL; + } + + // What type is this? + void* asset = xSTFindAsset(id, NULL); + if (asset == NULL) + { + return NULL; + } + + // HACK + return (char*)(asset) + 4; + } +} // namespace +void ztalkbox::load(const asset_type& tasset) +{ + xBaseInit(this, (xBaseAsset*)&tasset); + baseType = eBaseTypeTalkBox; + asset = &tasset; + eventFunc = cb_dispatch; + if (linkCount != 0) + { + link = (xLinkAsset*)(&tasset + 1); + } + + dialog_box = (ztextbox*)zSceneFindObject(tasset.dialog_box); + + if (tasset.prompt_box == 0) + { + prompt_box = NULL; + } + else + { + prompt_box = (ztextbox*)zSceneFindObject(tasset.prompt_box); + } + + if (tasset.quit_box == 0) { - zEntPlayerControlOff(CONTROL_OWNER_TALK_BOX); + quit_box = NULL; } else { - zEntPlayerControlOn(CONTROL_OWNER_TALK_BOX); + quit_box = (ztextbox*)zSceneFindObject(tasset.quit_box); + } + + prompt.skip = load_text(tasset.prompt.skip); + prompt.noskip = load_text(tasset.prompt.noskip); + prompt.quit = load_text(tasset.prompt.quit); + prompt.noquit = load_text(tasset.prompt.noquit); + prompt.yesno = load_text(tasset.prompt.yesno); + + reset(); +} + +void ztalkbox::reset() +{ + flag.visible = true; + if (shared.active == this) + { + deactivate(); } - return 1; } +void ztalkbox::set_text(const char* s) +{ + ztextbox& d = *dialog_box; + + if (s) + { + d.set_text(s); + } + else + { + d.clear_text(); + } + + d.refresh(); + + if (shared.active != this) + { + return; + } + + if (shared.state) + { + shared.state->stop(); + shared.state = NULL; + } + + ((xtextbox::layout*)&shared.lt)->refresh(d.tb, false); + + if (layout_contains_streams()) + { + lock_stream(); + } + + shared.state = shared.states[1]; + shared.state->start(); +} +void ztalkbox::set_text(U32 id) +{ + if (id == 0) + { + return; + } + xTextAsset* ta = (xTextAsset*)xSTFindAsset(id, 0); + + if (!ta) + { + clear_text(); + return; + } + + set_text((const char*)(ta + 1)); +} +void ztalkbox::add_text(const char* text) +{ + dialog_box->add_text(text); + + if (shared.active == this) + { + ((xtextbox::layout*)&shared.lt)->refresh_end(dialog_box->tb); + } +} +void ztalkbox::add_text(U32 textID) +{ + if (!textID) + { + return; + } + + xTextAsset* a = (xTextAsset*)xSTFindAsset(textID, NULL); + if (!a) + { + return; + } + + add_text((const char*)(a + 1)); +} +void ztalkbox::clear_text() +{ + set_text((const char*)NULL); +} +void ztalkbox::start_talk(const char* s, callback* cb, zNPCCommon* npc) +{ + this->npc = npc; + + if (shared.active) + { + shared.active->stop_talk(); + } + + activate(*this); + + shared.cb = cb; + shared.wait_event_mask = 0; + + reset_auto_wait(); + + shared.allow_quit = (asset->allow_quit != 0); + shared.quitting = false; + + if (asset->trap) + { + zEntPlayerControlOff((zControlOwner)0x10); + } + else + { + zEntPlayerControlOn((zControlOwner)0x10); + } + MasterTellSlaves(1); + refresh_prompts(); + + ztextbox& d = *dialog_box; + + if (d.flag.active) + { + d.deactivate(); + } + + if (s) + { + d.set_text(s); + } + + d.refresh(); + + ((xtextbox::layout*)&shared.lt)->refresh(d.tb, false); + + if (layout_contains_streams()) + { + lock_stream(); + } + + shared.state = shared.states[1]; + trigger(0x161); + + if (cb) + { + cb->on_start(); + } + + shared.state->start(); +} +void ztalkbox::start_talk(U32 text_id, callback* cb, zNPCCommon* npc) +{ + if (!text_id) + { + start_talk((const char*)NULL, cb, npc); + return; + } + + xTextAsset* ta = (xTextAsset*)xSTFindAsset(text_id, NULL); + if (ta) + { + start_talk((const char*)(ta + 1), cb, npc); + } +} +void ztalkbox::stop_talk() +{ + if (shared.active == this) + { + stop(); + } +} +namespace +{ + static void stop() + { + if (!shared.state) + { + return; + } + + shared.state->stop(); + hide_prompts(); + shared.active->MasterTellSlaves(0); + zEntPlayerControlOn((zControlOwner)0x10); + trigger(0x162); + + if (shared.cb) + { + shared.cb->on_stop(); + } + + shared.state = NULL; + deactivate(); + shared.sounds.clear(); + speak_stop(); + unlock_stream(); + } +} // namespace + +void ztalkbox::stop_wait(U32 x) +{ + if (shared.active == this) + { + shared.wait_event_mask = shared.wait_event_mask | x; + } +} + +void ztalkbox::show() +{ + flag.visible = true; + + if (shared.active != this) + { + return; + } + + if (prompt_box) + { + prompt_box->activate(); + } + + if (shared.allow_quit && prompt.quit && quit_box) + { + quit_box->activate(); + } +} + +void ztalkbox::hide() +{ + flag.visible = false; + + if (prompt_box != NULL) + { + prompt_box->deactivate(); + } + + if (quit_box != NULL) + { + quit_box->deactivate(); + } +} +void ztalkbox::MasterTellSlaves(S32 isBeginning) +{ + for (S32 i = 0; i < (S32)linkCount; i++) + { + xLinkAsset* link = &this->link[i]; + + if (link->dstEvent == 0x133) + { + xSceneID2Name(globals.sceneCur, id); + xSceneID2Name(globals.sceneCur, link->dstAssetID); + + xBase* mychild = zSceneFindObject(link->dstAssetID); + if (mychild) + { + MasterLoveSlave(mychild, isBeginning); + } + } + } +} +void ztalkbox::MasterLoveSlave(xBase* slave, S32 starting) +{ + switch (slave->baseType) + { + case eBaseTypeGroup: + { + xGroup* grp = (xGroup*)slave; + S32 cnt = xGroupGetCount(grp); + + for (S32 i = 0; i < cnt; i++) + { + xBase* grpitem = xGroupGetItemPtr(grp, i); + if (grpitem) + { + MasterLoveSlave(grpitem, starting); + } + } + break; + } + case eBaseTypeNPC: + { + zNPCCommon* npc = (zNPCCommon*)slave; + if (starting) + { + npc->SpeakBegin(); + } + else + { + npc->SpeakEnd(); + } + break; + } + } +} +void ztalkbox::load_settings(xIniFile& ini) +{ + shared.volume = xIniGetFloat(&ini, "talk_box.volume", 2.0f); + xDebugAddTweak("Talk Box|\01Globals|volume", &shared.volume, 0.0f, 10.0f, NULL, NULL, 0); +} +void ztalkbox::init() +{ + xtextbox::register_tags(new_tags, new_tags_size); + + static start_state_type start_state; + shared.states[1] = &start_state; + + static next_state_type next_state; + shared.states[2] = &next_state; + + static wait_state_type wait_state; + shared.states[3] = &wait_state; + + static stop_state_type stop_state; + shared.states[4] = &stop_state; + + ztalkbox::reset_all(); +} +namespace +{ + stop_state_type::stop_state_type() : state_type((state_enum)4) + { + } + state_type::state_type(state_enum t) + { + type = t; + } + wait_state_type::wait_state_type() : state_type((state_enum)3) + { + } + next_state_type::next_state_type() : state_type((state_enum)2) + { + } + start_state_type::start_state_type() : state_type((state_enum)1) + { + } + +} // namespace + +void ztalkbox::load(xBase& data, xDynAsset& asset, u32) +{ + ((ztalkbox&)data).load((const ztalkbox::asset_type&)asset); +} + +void ztalkbox::update_all(xScene& s, F32 dt) +{ + if (zGameIsPaused()) + { + return; + } + + while (shared.state) + { + shared.delay_events = true; + state_enum newtype = shared.state->update(s, dt); + + if (newtype == shared.state->type) + { + break; + } + + shared.state->stop(); + + if (newtype == (state_enum)-1) + { + stop(); + break; + } + + shared.state = shared.states[newtype]; + shared.state->start(); + shared.delay_events = false; + flush_triggered(); + } + + if (shared.state && shared.active) + { + trigger_pads_enum tp = (trigger_pads_enum)shared.active->asset->trigger_pads; + + if (tp == TP_ACTIVE && !globals.cmgr) + { + trigger_pads(*pad_pressed()); + } + else if (tp == TP_TRAPPED && globals.cmgr) + { + trigger_pads(*pad_pressed()); + } + } + + shared.delay_events = false; + flush_triggered(); +} +namespace +{ + static U32* pad_pressed() + { + if (shared.permit & 2) + { + return &globals.pad0->pressed; + } + + static U32 zero; + zero = 0; + return &zero; + } + +}; // namespace +void ztalkbox::render_all() +{ + if (!(shared.permit & 1)) + { + return; + } + + if (!shared.state) + { + return; + } + + if (!shared.active->flag.visible) + { + return; + } + + ztextbox& d = *shared.active->dialog_box; + + if (d.flag.active) + { + d.deactivate(); + } + + if (d.flag.show_backdrop) + { + d.render_backdrop(); + } + + d.tb.render(*(xtextbox::layout*)&shared.lt, shared.begin_jot, shared.end_jot); +} +void ztalkbox::reset_all() +{ + shared.flags = 0; + shared.permit = 0xFFFFFFFF; + shared.active = NULL; + shared.state = NULL; + shared.cb = NULL; + shared.delay_events = false; + shared.speak_npc = NULL; + shared.triggered.reset(); + shared.quit_ready = false; + shared.prompt_ready = false; + shared.next_stream = 0; + shared.stream_locked[1] = 0; + shared.stream_locked[0] = 0; + ((xtextbox::layout*)&shared.lt)->clear(); +} + +ztalkbox* ztalkbox::get_active() +{ + return shared.active; +} + +void ztalkbox::permit(U32 add_flags, U32 remove_flags) +{ + shared.permit &= ~remove_flags; + shared.permit |= add_flags; +} + +namespace +{ + void stop_state_type::start() + { + } + void stop_state_type::stop() + { + } + + state_enum stop_state_type::update(xScene& scn, F32 dt) + { + return (state_enum)-1; + } + void state_type::start() + { + } + + void state_type::stop() + { + } + void wait_state_type::start() + { + this->answer_yes = false; + refresh_prompts(); + } + + void wait_state_type::stop() + { + if (!shared.wait.type.time || shared.wait.type.prompt) + { + shared.prompt_delay = 0.1f; + shared.prompt_ready = false; + } + + shared.quit_delay = 0.0f; + shared.quit_ready = true; + + if (shared.wait.type.prompt && shared.wait.query == Q_YESNO) + { + if (shared.cb) + { + ztalkbox::answer_enum answer = (ztalkbox::answer_enum)2; + if (this->answer_yes) + { + answer = (ztalkbox::answer_enum)1; + } + shared.cb->on_answer(answer); + } + + if (this->answer_yes) + { + trigger(0x1C5); + } + else + { + trigger(0x1C6); + } + } + + shared.wait.reset_type(); + shared.wait.type.time = true; + shared.wait.delay = 0.0f; + shared.wait.need = false; + } + state_enum wait_state_type::update(xScene& scn, F32 dt) + { + update_prompt_status(dt); + update_quit_status(dt); + U32* pressed = pad_pressed(); + + if (shared.quitting) + { + if (shared.allow_quit && !shared.wait.need) + { + return (state_enum)2; + } + shared.quitting = false; + } + + if (shared.wait.type.time) + { + shared.wait.delay -= dt; + if (shared.wait.delay <= 0.0f) + { + return (state_enum)2; + } + } + + if (shared.wait.type.prompt && shared.prompt_ready) + { + switch (shared.wait.query) + { + case Q_YESNO: + if (*pressed & 0x10000) + { + *pressed &= ~0x10000; + this->answer_yes = true; + return (state_enum)2; + } + if (*pressed & 0x40000) + { + *pressed &= ~0x40000; + return (state_enum)2; + } + break; + case Q_SKIP: + default: + if (*pressed & 0x10000) + { + *pressed &= ~0x10000; + return (state_enum)2; + } + break; + } + } + + if (shared.allow_quit && (*pressed & 0x80000) && shared.quit_ready && (*pressed & 0x80000)) + { + shared.quitting = true; + *pressed &= ~0x80000; + return (state_enum)2; + } + + if (shared.wait.type.sound) + { + if (!shared.sounds.playing(-1, true)) + { + return (state_enum)2; + } + } + + if (shared.wait.type.event) + { + if (shared.wait_event_mask & shared.wait.event_mask) + { + shared.wait_event_mask &= ~shared.wait.event_mask; + return (state_enum)2; + } + } + + return (state_enum)3; + } + static bool trigger_jot(const xtextbox::jot& j) + { + if (!j.tag) + { + return true; + } + + if (j.tag->context) + { + return ((bool (*)(const xtextbox::jot&))j.tag->context)(j); + } + + return true; + } + static bool trigger_jot(S32 index) + { + xtextbox::jot* jots = ((xtextbox::layout*)&shared.lt)->jots(); + return trigger_jot(jots[index]); + } + void next_state_type::start() + { + if (shared.end_jot == shared.page_end_jot) + { + xtextbox& tb = shared.active->dialog_box->tb; + S32 jots_size = ((xtextbox::layout*)&shared.lt)->jots_size(); + ((xtextbox::layout*)&shared.lt)->jots(); + + shared.begin_jot = shared.end_jot; + S32 size; + tb.yextent(tb.bounds.h, size, *(xtextbox::layout*)&shared.lt, shared.begin_jot, -1); + + if (size == 0 && jots_size > shared.begin_jot) + { + size = 1; + } + + shared.page_end_jot = shared.begin_jot + size; + } + + while (shared.end_jot < shared.page_end_jot) + { + shared.end_jot++; + if (!trigger_jot(shared.end_jot - 1)) + { + break; + } + } + + if (shared.end_jot == shared.page_end_jot) + { + xtextbox::jot* jots = ((xtextbox::layout*)&shared.lt)->jots(); + xtextbox::jot* last = &jots[shared.end_jot - 1]; + + if (last->flag.page_break && (S32)(shared.end_jot - 1) > shared.begin_jot) + { + last--; + } + + if (!is_wait_jot(*last)) + { + shared.wait = shared.auto_wait; + } + } + } + + void next_state_type::stop() + { + } + state_enum next_state_type::update(xScene& scn, F32 dt) + { + if (shared.begin_jot == shared.page_end_jot) + { + return (state_enum)4; + } + return (state_enum)3; + } + void start_state_type::start() + { + shared.page_end_jot = 0; + shared.end_jot = 0; + shared.begin_jot = 0; + shared.wait.reset_type(); + shared.wait.type.time = true; + shared.wait.delay = 0.0f; + shared.quit_delay = 0.25f; + shared.prompt_delay = 0.25f; + shared.quit_ready = false; + shared.prompt_ready = false; + refresh_prompts(); + } + void start_state_type::stop() + { + } + + state_enum start_state_type::update(xScene& scn, F32 dt) + { + return (state_enum)2; + } + +} // namespace diff --git a/src/SB/Game/zTalkBox.h b/src/SB/Game/zTalkBox.h index 93b33b165..56559e566 100644 --- a/src/SB/Game/zTalkBox.h +++ b/src/SB/Game/zTalkBox.h @@ -5,9 +5,10 @@ #include "zNPCTypeCommon.h" #include "xIni.h" #include "containers.h" -#include "zEntPlayer.h" - #include "xScene.h" +#include "xCamera.h" +#include "zCutsceneMgr.h" +#include "xSnd.h" struct ztalkbox : xBase { @@ -109,12 +110,17 @@ struct ztalkbox : xBase static ztalkbox* get_active(); void start_talk(U32 textID, callback*, zNPCCommon*); // FIXME: params not verified + void start_talk(const char* text, callback* cb, zNPCCommon* npc); + + void MasterTellSlaves(int event); + void MasterLoveSlave(xBase*, int); void load(const asset_type& tasset); void reset(); void set_text(const char* text); void set_text(U32 textID); void add_text(U32 textID); + void add_text(const char* text); void clear_text(); void stop_talk(); void stop_wait(U32 x); @@ -124,6 +130,12 @@ struct ztalkbox : xBase namespace { + enum trigger_pads_enum + { + TP_NEVER, + TP_TRAPPED, + TP_ACTIVE, + }; enum state_enum { STATE_INVALID = -1, @@ -149,26 +161,43 @@ namespace state_type(state_enum t); virtual void start(); virtual void stop(); + virtual state_enum update(xScene& scn, F32 dt) = 0; }; - struct next_state_type + struct start_state_type : state_type { - void stop(); + start_state_type(); + virtual void start(); + virtual void stop(); + virtual state_enum update(xScene& scn, F32 dt); }; - struct start_state_type + struct next_state_type : state_type { - void stop(); - S8 update(xScene& scn, F32 dt); + S32 prev_wait_jot; // offset 0x8, size 0x4 + + next_state_type(); + virtual void start(); + virtual void stop(); + virtual state_enum update(xScene& scn, F32 dt); }; - struct stop_state_type + struct wait_state_type : state_type { - void start(); - void stop(); - S8 update(xScene& scn, F32 dt); - }; + U8 answer_yes; // offset 0x8, size 0x1 + wait_state_type(); + virtual void start(); + virtual void stop(); + virtual state_enum update(xScene& scn, F32 dt); + }; + struct stop_state_type : state_type + { + stop_state_type(); + virtual void start(); + virtual void stop(); + virtual state_enum update(xScene& scn, F32 dt); + }; struct jot; struct callback { @@ -250,24 +279,25 @@ namespace U32 context_buffer_size; // 0x8470 U16 dynamics[64]; // 0x8474 U32 dynamics_size; // 0x84F4 + //refresh(d.tb, false) }; struct wait_context { struct { - U8 time : 1; - U8 prompt : 1; - U8 sound : 1; - U8 event : 1; - U16 pad : 12; - } type; + U8 time : 1; // bitfield size: 0x8 + U8 prompt : 1; // bitfield size: 0x8 + U8 sound : 1; // bitfield size: 0x8 + U8 event : 1; // bitfield size: 0x8 + U16 pad : 12; // bitfield size: 0x10 + } type; //offset 0x0, size 0x4 U8 need; //Offset 08d3a - F32 delay; + F32 delay; //Offset 08d3c U32 event_mask; //Offset 08d40 query_enum query; //Offset 08d44 - void reset_type(); - wait_context& operator=(const wait_context& rhs); + void reset_type(); //Offset 08d48 + wait_context& operator=(const wait_context& rhs); //Offset 08d4c }; struct trigger_pair @@ -291,16 +321,16 @@ namespace wait_context auto_wait; // 0x8538 U32 wait_event_mask; // 0x8548 F32 prompt_delay; // 0x854C - F32 quit_delay; + F32 quit_delay; // 0x8550 U8 prompt_ready; // 0x8554, size 0x1 - U8 quit_ready; + U8 quit_ready; // 0x8555, size 0x1 U8 stream_locked[2]; // 0x8556 S32 next_stream; // 0x8558 sound_queue<4> sounds; // 0x855C U8 allow_quit; // 0x8578 U8 quitting; // 0x8579 U8 delay_events; // 0x857A - callback* cb; // 0x857C + ztalkbox::callback* cb; // 0x857C fixed_queue triggered; // 0x8580 F32 volume; // 0x8690 zNPCCommon* speak_npc; // 0x8694 @@ -338,11 +368,58 @@ namespace float left; // offset 0x0, size 0x4 float right; // offset 0x4, size 0x4 } volume; // offset 0x8, size 0x8 - unsigned int target; // offset 0x8, size 0x4 - class xVec3 origin; // offset 0x8, size 0xC + U32 target; // offset 0x8, size 0x4 + xVec3 origin; // offset 0x8, size 0xC }; U32 speaker; // offset 0x14, size 0x4 }; + + struct teleport_context + { + // total size: 0x14 + U8 use_loc; // offset 0x0, size 0x1 + U8 use_yaw; // offset 0x1, size 0x1 + xVec3 loc; // offset 0x4, size 0xC + float yaw; // offset 0x10, size 0x4 + }; + struct tag_entry + { + // total size: 0x14 + substr name; // offset 0x0, size 0x8 + char op; // offset 0x8, size 0x1 + substr* args; // offset 0xC, size 0x4 + U32 args_size; // offset 0x10, size 0x4 + }; + struct tag_entry_list + { + // total size: 0x8 + tag_entry* entries; // offset 0x0, size 0x4 + U32 size; // offset 0x4, size 0x4 + }; + struct xTextAsset + { + U32 len; // offset 0x0, size 0x4 + }; + + struct signal_context + { + // total size: 0x4 + U32 flags; // offset 0x0, size 0x4 + }; + } // namespace +struct location_asset : xDynAsset +{ + xVec3 loc; // offset 0x10, size 0xC + static const char* type_name(); +}; +struct pointer_asset : xDynAsset +{ + xVec3 loc; // offset 0x10, size 0xC + float yaw; // offset 0x1C, size 0x4 + float pitch; // offset 0x20, size 0x4 + float roll; // offset 0x24, size 0x4 + static const char* type_name(); +}; #endif diff --git a/src/SB/Game/zTextBox.h b/src/SB/Game/zTextBox.h index a68ec58aa..ac6e4364e 100644 --- a/src/SB/Game/zTextBox.h +++ b/src/SB/Game/zTextBox.h @@ -69,12 +69,12 @@ struct ztextbox : xBase bool show_backdrop : 1; // bit 26 bool visible : 1; // bit 27 bool hack_invisible : 1; // bit 28 - } flag; - asset_type* asset; - xtextbox tb; + } flag; //0x10 + asset_type* asset; //0x14 + xtextbox tb; //0x18 const char* segments[16]; U32 segments_size; - ztextbox* next; + ztextbox* next; //0xc4 ztextbox* prev; RwRaster* bgtex;