diff --git a/LEGO1/lego/legoomni/include/historybook.h b/LEGO1/lego/legoomni/include/historybook.h index 76912d5d..9acbe8ac 100644 --- a/LEGO1/lego/legoomni/include/historybook.h +++ b/LEGO1/lego/legoomni/include/historybook.h @@ -46,6 +46,9 @@ class HistoryBook : public LegoWorld { MxStillPresenter* m_name[20][7]; // 0x164 MxStillPresenter* m_scores[20]; // 0x394 + + MxStillPresenter* m_intAlphabet[sizeOfArray(LegoGameState::g_intCharacters)]; + MxU32 m_intAlphabetOffset; }; #endif // HISTORYBOOK_H diff --git a/LEGO1/lego/legoomni/include/legogamestate.h b/LEGO1/lego/legoomni/include/legogamestate.h index 00102eae..d65d3a55 100644 --- a/LEGO1/lego/legoomni/include/legogamestate.h +++ b/LEGO1/lego/legoomni/include/legogamestate.h @@ -23,6 +23,12 @@ struct ColorStringStruct { const char* m_colorName; // 0x04 }; +struct InternationalCharacter { + unsigned char m_character; + const char* m_bitmap; + MxU32 m_buttonId; +}; + // VTABLE: LEGO1 0x100d74a8 // SIZE 0x30 class LegoBackgroundColor : public MxVariable { @@ -256,6 +262,8 @@ class LegoGameState { Area m_currentArea; // 0x424 Area m_previousArea; // 0x428 Area m_unk0x42c; // 0x42c + + static const InternationalCharacter g_intCharacters[8]; }; MxBool ROIColorOverride(const char* p_input, char* p_output, MxU32 p_copyLen); diff --git a/LEGO1/lego/legoomni/include/registrationbook.h b/LEGO1/lego/legoomni/include/registrationbook.h index 3474c7ce..b6e60ae0 100644 --- a/LEGO1/lego/legoomni/include/registrationbook.h +++ b/LEGO1/lego/legoomni/include/registrationbook.h @@ -64,6 +64,8 @@ class RegistrationBook : public LegoWorld { LPDIRECTDRAWSURFACE m_checkboxHilite; // 0x2c4 LPDIRECTDRAWSURFACE m_checkboxSurface; // 0x2c8 LPDIRECTDRAWSURFACE m_checkboxNormal; // 0x2cc + MxStillPresenter* m_intAlphabet[sizeOfArray(LegoGameState::g_intCharacters)]; + MxU32 m_intAlphabetOffset; MxLong HandleEndAction(MxEndActionNotificationParam& p_param); MxLong HandleKeyPress(SDL_Keycode p_key); diff --git a/LEGO1/lego/legoomni/src/common/legogamestate.cpp b/LEGO1/lego/legoomni/src/common/legogamestate.cpp index 8e8265dd..170842ea 100644 --- a/LEGO1/lego/legoomni/src/common/legogamestate.cpp +++ b/LEGO1/lego/legoomni/src/common/legogamestate.cpp @@ -143,6 +143,17 @@ const char* g_strEnable = "enable"; // STRING: LEGO1 0x100f3bf4 const char* g_strDisable = "disable"; +const InternationalCharacter LegoGameState::g_intCharacters[8] = { + {0xe4, "Aum_Bitmap", 29}, + {0xf6, "Oum_Bitmap", 30}, + {0xdf, "Beta_Bitmap", 31}, + {0xfc, "Uum_Bitmap", 32}, + {0xe5, "Ao_Bitmap", 29}, + {0xe6, "Ae_Bitmap", 30}, + {0xf8, "Oz_Bitmap", 31}, + {0xf1, "Ene_Bitmap", 29} +}; + // FUNCTION: LEGO1 0x10039550 LegoGameState::LegoGameState() { diff --git a/LEGO1/lego/legoomni/src/worlds/historybook.cpp b/LEGO1/lego/legoomni/src/worlds/historybook.cpp index 5aa1e49e..89de03d9 100644 --- a/LEGO1/lego/legoomni/src/worlds/historybook.cpp +++ b/LEGO1/lego/legoomni/src/worlds/historybook.cpp @@ -10,12 +10,15 @@ #include "mxstillpresenter.h" #include "mxtransitionmanager.h" +#include + DECOMP_SIZE_ASSERT(HistoryBook, 0x3e4) // FUNCTION: LEGO1 0x100822f0 HistoryBook::HistoryBook() { memset(m_alphabet, 0, sizeof(m_alphabet)); + memset(m_intAlphabet, 0, sizeof(m_intAlphabet)); memset(m_name, 0, sizeof(m_name)); memset(m_scores, 0, sizeof(m_scores)); NotificationManager()->Register(this); @@ -112,6 +115,20 @@ void HistoryBook::ReadyWorld() } } + for (i = 0; i < sizeOfArray(m_intAlphabet); i++) { + m_intAlphabet[i] = (MxStillPresenter*) Find("MxStillPresenter", LegoGameState::g_intCharacters[i].m_bitmap); + } + + m_intAlphabetOffset = 0; + for (i = 0; i < sizeOfArray(m_intAlphabet); i++) { + if (!m_intAlphabet[i]) { + m_intAlphabetOffset++; + } + else { + break; + } + } + MxStillPresenter* scoreboxMaster = (MxStillPresenter*) Find("MxStillPresenter", "ScoreBox"); MxU8 scoreColors[3] = {0x76, 0x4c, 0x38}; // yellow - #FFB900, blue - #00548C, red - #CB1220, background - #CECECE, border - #74818B @@ -167,7 +184,24 @@ void HistoryBook::ReadyWorld() for (MxS16 j = 0; j < (MxS16) sizeOfArray(m_name[0]) && score->m_name.m_letters[j] != -1; j++, scoreX += 0x17) #endif { - m_name[i][j] = m_alphabet[score->m_name.m_letters[j]]->Clone(); + MxStillPresenter** intoAlphabet = [this, + index = score->m_name.m_letters[j]]() mutable -> MxStillPresenter** { + if (index < sizeOfArray(m_alphabet)) { + return &m_alphabet[index]; + } + + index -= sizeOfArray(m_alphabet); + index += m_intAlphabetOffset; + + if (index >= sizeOfArray(m_intAlphabet) || !m_intAlphabet[index]) { + SDL_Log("Warning: international character not present in current game. Falling back to X"); + return &m_alphabet[SDLK_X - SDLK_A]; + } + + return &m_intAlphabet[index]; + }(); + + m_name[i][j] = (*intoAlphabet)->Clone(); assert(m_name[i][j]); m_name[i][j]->Enable(TRUE); diff --git a/LEGO1/lego/legoomni/src/worlds/registrationbook.cpp b/LEGO1/lego/legoomni/src/worlds/registrationbook.cpp index 8f152f91..adaf45d4 100644 --- a/LEGO1/lego/legoomni/src/worlds/registrationbook.cpp +++ b/LEGO1/lego/legoomni/src/worlds/registrationbook.cpp @@ -29,6 +29,8 @@ #include "regbook_actions.h" #include "scripts.h" +#include + DECOMP_SIZE_ASSERT(RegistrationBook, 0x2d0) // GLOBAL: LEGO1 0x100d9924 @@ -45,6 +47,7 @@ MxBool g_nextCheckbox = FALSE; RegistrationBook::RegistrationBook() : m_registerDialogueTimer(0x80000000), m_unk0xfc(1) { memset(m_alphabet, 0, sizeof(m_alphabet)); + memset(m_intAlphabet, 0, sizeof(m_intAlphabet)); memset(m_name, 0, sizeof(m_name)); m_unk0x280.m_cursorPos = 0; @@ -175,19 +178,31 @@ MxLong RegistrationBook::HandleKeyPress(SDL_Keycode p_key) // keycode is case-insensitive SDL_Keycode key = p_key; - if ((key < SDLK_A || key > SDLK_Z) && key != SDLK_BACKSPACE) { + MxStillPresenter** intoAlphabet = [this, key]() -> MxStillPresenter** { + if (key >= SDLK_A && key <= SDLK_Z) { + return &m_alphabet[key - SDLK_A]; + } + + for (int i = 0; i < sizeOfArray(m_intAlphabet); i++) { + if (m_intAlphabet[i] && LegoGameState::g_intCharacters[i].m_character == key) { + return &m_intAlphabet[i]; + } + } + + return nullptr; + }(); + + if (!intoAlphabet && key != SDLK_BACKSPACE) { if (key == SDLK_SPACE) { DeleteObjects(&m_atomId, RegbookScript::c_iic006in_RunAnim, RegbookScript::c_iic008in_PlayWav); BackgroundAudioManager()->RaiseVolume(); } } else if (key != SDLK_BACKSPACE && m_unk0x280.m_cursorPos < 7) { - m_name[0][m_unk0x280.m_cursorPos] = m_alphabet[key - SDLK_A]->Clone(); + m_name[0][m_unk0x280.m_cursorPos] = (*intoAlphabet)->Clone(); if (m_name[0][m_unk0x280.m_cursorPos] != NULL) { - m_alphabet[key - SDLK_A]->GetAction()->SetUnknown24( - m_alphabet[key - SDLK_A]->GetAction()->GetUnknown24() + 1 - ); + (*intoAlphabet)->GetAction()->SetUnknown24((*intoAlphabet)->GetAction()->GetUnknown24() + 1); m_name[0][m_unk0x280.m_cursorPos]->Enable(TRUE); m_name[0][m_unk0x280.m_cursorPos]->SetTickleState(MxPresenter::e_repeating); m_name[0][m_unk0x280.m_cursorPos]->SetPosition(m_unk0x280.m_cursorPos * 23 + 343, 121); @@ -196,7 +211,10 @@ MxLong RegistrationBook::HandleKeyPress(SDL_Keycode p_key) m_checkmark[0]->Enable(TRUE); } - m_unk0x280.m_letters[m_unk0x280.m_cursorPos] = key - SDLK_A; + m_unk0x280.m_letters[m_unk0x280.m_cursorPos] = + key >= SDLK_A && key <= SDLK_Z + ? key - SDLK_A + : (intoAlphabet - m_intAlphabet) + sizeOfArray(m_alphabet) - m_intAlphabetOffset; m_unk0x280.m_cursorPos++; } } @@ -224,13 +242,22 @@ MxLong RegistrationBook::HandleKeyPress(SDL_Keycode p_key) MxLong RegistrationBook::HandleControl(LegoControlManagerNotificationParam& p_param) { MxS16 buttonId = p_param.m_unk0x28; + const InternationalCharacter* intChar = NULL; - if (buttonId >= 1 && buttonId <= 28) { + for (int i = 0; i < sizeOfArray(m_intAlphabet); i++) { + if (m_intAlphabet[i] && LegoGameState::g_intCharacters[i].m_buttonId == buttonId) { + intChar = &LegoGameState::g_intCharacters[i]; + break; + } + } + + if ((buttonId >= 1 && buttonId <= 28) || intChar) { if (p_param.m_clickedObjectId == RegbookScript::c_Alphabet_Ctl) { // buttonId: // - [1, 26]: alphabet // - 27: backspace // - 28: go back to information center + // - 29+: international alphabet if (buttonId == 28) { DeleteObjects(&m_atomId, RegbookScript::c_iic006in_RunAnim, RegbookScript::c_iic008in_PlayWav); @@ -244,11 +271,19 @@ MxLong RegistrationBook::HandleControl(LegoControlManagerNotificationParam& p_pa TransitionManager()->StartTransition(MxTransitionManager::e_mosaic, 50, FALSE, FALSE); } else { - if (buttonId > 28) { + if (buttonId > 28 && !intChar) { return 1; } - HandleKeyPress(buttonId < 27 ? SDLK_A + buttonId - 1 : SDLK_BACKSPACE); + if (buttonId < 27) { + HandleKeyPress(SDLK_A + buttonId - 1); + } + else if (intChar) { + HandleKeyPress((SDL_Keycode) intChar->m_character); + } + else { + HandleKeyPress(SDLK_BACKSPACE); + } } } else { @@ -422,6 +457,20 @@ void RegistrationBook::ReadyWorld() } } + for (i = 0; i < sizeOfArray(m_intAlphabet); i++) { + m_intAlphabet[i] = (MxStillPresenter*) Find("MxStillPresenter", LegoGameState::g_intCharacters[i].m_bitmap); + } + + m_intAlphabetOffset = 0; + for (i = 0; i < sizeOfArray(m_intAlphabet); i++) { + if (!m_intAlphabet[i]) { + m_intAlphabetOffset++; + } + else { + break; + } + } + // Now we have to do the checkmarks char checkmarkBuffer[] = "Check0_Ctl"; for (i = 0; i < 10; i++) { @@ -443,8 +492,25 @@ void RegistrationBook::ReadyWorld() m_checkmark[i]->Enable(TRUE); } + MxStillPresenter** intoAlphabet = + [this, index = players[i - 1].m_letters[j]]() mutable -> MxStillPresenter** { + if (index < sizeOfArray(m_alphabet)) { + return &m_alphabet[index]; + } + + index -= sizeOfArray(m_alphabet); + index += m_intAlphabetOffset; + + if (index >= sizeOfArray(m_intAlphabet) || !m_intAlphabet[index]) { + SDL_Log("Warning: international character not present in current game. Falling back to X"); + return &m_alphabet[SDLK_X - SDLK_A]; + } + + return &m_intAlphabet[index]; + }(); + // Start building the player names using a two-dimensional array - m_name[i][j] = m_alphabet[players[i - 1].m_letters[j]]->Clone(); + m_name[i][j] = (*intoAlphabet)->Clone(); assert(m_name[i][j]); // Enable the presenter to actually show the letter in the grid