#include "UI/Chat/UINewChatWnd.h" #include "Client/Input/Mouse.h" #include "Client/Mode/CMode.h" #include "Client/Replay.h" #include "Client/Session.h" #include "Contents/Configuration/ServiceConfiguration.h" #include "Contents/Jobs/JobInheritance.h" #include "Contents/MessageTable.h" #include "Network/TextEncoding.h" #include "Render/Renderer.h" #include "UI/Buttons/UITextButton.h" #include "UI/Chat/UISubChatHisWnd.h" #include "UI/Edit/UIEditCtrl.h" #include "UI/Edit/UIItemTagCtrl.h" #include "UI/Lists/UIChatHisBox.h" #include "UI/Lists/UITabControl.h" #include "UI/Text/NavigationLink.h" #include "UI/Text/TextMarkup.h" #include "UI/Text/TextWrapping.h" #include "Client/Mode/ModeMgr.h" #include "UI/Window/UIWindowMgr.h" #include "Utility/Text/FormatString.h" #include #include #include #include #include #include #include // OFFSET: 0x00936360 // FUNCTION: fn_12750 int UINewChatWnd::SendMsg(UIWindow* sender, int message, std::intptr_t a, std::intptr_t b, std::intptr_t c, std::intptr_t d) { if (g_replayAnimationScaling != 0 && Replay::Instance().header.options.recordRestricted == 0) { return 1; } switch (message) { case 0x9A: for (std::map::iterator it = g_session.m_noSubChatWnds.begin(); it != g_session.m_noSubChatWnds.end(); ++it) { static_cast(this->m_tabs[it->first])->ResolvePlayerTags(reinterpret_cast(a)); } return 0; case 0x75: { if (!g_session.m_canDragWnd || g_session.m_noSubChatWnds.size() <= 1) { return 0; } std::map& names = g_session.m_noSubChatWnds; std::map::iterator found = names.find(this->m_currentTab); if (found == names.end()) { return 0; } int detached = static_cast(g_session.m_subChatWnds.size()); g_session.m_subChatWnds[detached] = found->second; const int key = found->first; names.erase(found); if (key != names.size()) { std::map saved(names); names.clear(); int index = 0; for (std::map::iterator it = saved.begin(); it != saved.end(); ++it, ++index) { names[index] = it->second; } } this->m_tabControl->RemoveTab(this->m_currentTab); this->m_tabWidth = this->m_tabControl->geometry.width / names.size(); UISubChatHisWnd* window = static_cast(g_windowMgr.sub_A97580(static_cast(g_windowMgr.retainedWindows.size()), -1, -1, 0, 0)); if (window == nullptr) { return 0; } std::vector::iterator tab = this->m_tabs.begin() + this->m_currentTab; if (tab != this->m_tabs.end()) { window->ChatBox()->CopyHistory(*static_cast(*tab)); this->RemoveChild(*tab, 1); this->m_tabs.erase(tab); } RECT rectangle; rectangle.left = 3; rectangle.top = 0; rectangle.right = this->m_tabWidth + 3 + this->m_tabControl->geometry.width; rectangle.bottom = this->m_tabControl->geometry.height; this->FillSurface(0xFFFF00FF, &rectangle); if (this->m_currentTab > 0) { --this->m_currentTab; this->m_tabControl->unknown_007C = this->m_currentTab; } this->RefreshTabs(true); if (sender != this) { int x = 0; int y = 0; this->ToScreen(x, y); g_windowMgr.ReleaseCapture(); window->Move(x, y); window->Slot25(g_mouse.x - this->m_tabWidth * a - x, g_mouse.y - y); } this->ApplyHeaderLayout(); return 0; } case 0x76: { if (!g_session.m_canDragWnd || this->m_tabControl->geometry.width + this->m_tabWidth > this->geometry.width - 0x5A) { return 0; } int index = static_cast(sender)->m_index; std::map& windows = g_session.m_subChatWnds; std::map::iterator found = windows.find(index); if (found == windows.end()) { return 0; } int count = static_cast(g_session.m_noSubChatWnds.size()); if (this->TextWidth(found->second.name.c_str(), 0, 0, 12, 0, 0) * (count + 1) > this->geometry.width - 0x5A) { g_windowMgr.SendNotice(3, 0, 0, 0, 0, 0); g_windowMgr.SendNotice(1, reinterpret_cast(LookupMessage(0x6F0)), 0xFF, 0, 0, 0); g_windowMgr.Promote(sender); return 0; } g_session.m_noSubChatWnds[count] = found->second; this->m_tabControl->AddTab(found->second.name.c_str(), ""); this->m_tabWidth = this->m_tabControl->geometry.width / g_session.m_noSubChatWnds.size(); const int key = found->first; windows.erase(found); if (key != windows.size()) { std::map saved(windows); windows.clear(); int renumbered = 0; for (std::map::iterator it = saved.begin(); it != saved.end(); ++it, ++renumbered) { windows[renumbered] = it->second; } } int height = this->geometry.height - this->m_inputOffsetY - this->m_inputAreaHeight - 4; if (height < 0) { height = this->m_inputAreaHeight; } UIChatHisBox* source = static_cast(g_windowMgr.retainedWindows[index])->ChatBox(); UIChatHisBox* box = new UIChatHisBox(); box->Configure(0, 0, this->geometry.width - 8, height, 0); box->Move(-100 - this->geometry.width, -100); this->AddChild(box); box->CopyHistory(*source); this->m_tabs.push_back(box); g_windowMgr.ReleaseRetainedWindow(index); if (index != g_windowMgr.retainedWindows.size()) { std::map saved(g_windowMgr.retainedWindows); g_windowMgr.retainedWindows.clear(); int renumbered = 0; for (std::map::iterator it = saved.begin(); it != saved.end(); ++it, ++renumbered) { static_cast(it->second)->m_index = renumbered; g_windowMgr.retainedWindows[renumbered] = it->second; } } RECT rectangle; rectangle.left = 3; rectangle.top = 0; rectangle.right = this->m_tabControl->geometry.width + 3; rectangle.bottom = this->m_tabControl->geometry.height; this->FillSurface(0xFFFF00FF, &rectangle); this->m_currentTab = count; this->m_tabControl->unknown_007C = count; this->RefreshTabs(true); this->ApplyHeaderLayout(); return 0; } case 0x74: if (this->geometry.height <= this->m_inputOffsetY + this->m_inputAreaHeight) { return 0; } if (sender == this->m_tabControl) { if (a != 0) { g_session.m_noSubChatWnds[b].name = reinterpret_cast(a); this->m_tabWidth = this->m_tabControl->geometry.width / g_session.m_noSubChatWnds.size(); g_windowMgr.SetFocus(this->m_inputEdit); if (UIWindow* window = g_windowMgr.LookupWindow(0x84)) { window->SendMsg(this, 6, 0xC1, this->m_currentTab, 1, 2); } } RECT rectangle; rectangle.left = 3; rectangle.top = 0; rectangle.right = (c != 0 ? c : this->m_tabControl->geometry.width) + 3; rectangle.bottom = this->m_tabControl->geometry.height; this->FillSurface(0xFFFF00FF, &rectangle); this->ApplyHeaderLayout(); this->Invalidate(); return 0; } else { RECT rectangle; rectangle.left = 5; rectangle.top = this->m_inputOffsetY + 2; rectangle.right = this->geometry.width - 6; rectangle.bottom = this->geometry.height - this->m_inputAreaHeight - 2; this->FillSurface(0xFFFF00FF, &rectangle); return 0; } case 0x16: if (a == this->m_currentTab) { this->RefreshTabs(false); } else { this->m_currentTab = a; this->RefreshTabs(true); } return 0; case 0x46: { std::string name(reinterpret_cast(a)); const int count = static_cast(static_cast(this->unknown_00C0)->history.size()); int index = 0; for (std::vector::iterator it = static_cast(this->unknown_00C0)->history.begin(); it != static_cast(this->unknown_00C0)->history.end(); ++it, ++index) { if (*it == name) { UIWhisperEditCtrl* whisper = static_cast(this->unknown_00C0); whisper->history.erase(it); if (static_cast(this->unknown_00C0)->historyIndex == index) { static_cast(this->unknown_00C0)->historyIndex = count - 1; } else if (static_cast(this->unknown_00C0)->historyIndex > index) { static_cast(this->unknown_00C0)->historyIndex = static_cast(this->unknown_00C0)->historyIndex - 1; } return 0; } } return 0; } case 0x27: switch (this->unknown_00F0) { case 1: *reinterpret_cast(&g_session.unknown_5490[0x8]) = a; switch (a) { case 0: static_cast(this->m_chatEdit)->TextColor(RGB(0, 0, 0), 0xFFFFFFFF); break; case 1: static_cast(this->m_chatEdit)->TextColor(RGB(128, 0, 128), 0xFFFFFFFF); break; case 2: static_cast(this->m_chatEdit)->TextColor(RGB(0, 128, 128), 0xFFFFFFFF); break; case 4: static_cast(this->m_chatEdit)->TextColor(RGB(148, 0, 211), 0xFFFFFFFF); break; } this->Invalidate(); return 0; case 0: { const int count = static_cast(static_cast(this->unknown_00C0)->history.size()); if (a >= 0 && a <= count) { static_cast(this->unknown_00C0)->SetText(a == count ? "" : static_cast(this->unknown_00C0)->history[a].c_str()); static_cast(this->unknown_00C0)->historyIndex = a; } g_windowMgr.SetFocus(static_cast(this->unknown_00C0)); break; } default: return 0; } case 0x36: g_windowMgr.SetFocus(static_cast(this->m_chatEdit)); return 0; case 0x25: case 0x73: if (message != 0x73) { std::vector lines; char name[25] = {}; if (d != 0) { std::strcpy(name, reinterpret_cast(d)); } switch (c) { case 1: case 3: case 4: case 21: { std::string text(reinterpret_cast(a)); const int length = static_cast(std::strlen(reinterpret_cast(a))); const int position = static_cast(text.find(" :")); if (position > 0 && position < length && position < 24) { std::memcpy(name, reinterpret_cast(a), position); name[position] = '\0'; } break; } } if (g_serviceType == 10 && std::strlen(name) > 24) { return 0; } if (std::strcmp(name, g_session.DecodePlayerName()) == 0) { name[0] = '\0'; } bool hasLink = false; std::string plain = RemoveTextColors(reinterpret_cast(a)); if (g_navigationLinkParser->HasLink(plain)) { hasLink = true; lines.push_back(plain); } else { WrapPlainText( RemoveTextColors(reinterpret_cast(a)).c_str(), lines, 0x5E ); } const bool everyTab = c == 25 || (c == 0 && b == 0xFF); int index = 0; for (std::map::iterator it = g_session.m_noSubChatWnds.begin(); it != g_session.m_noSubChatWnds.end(); ++it, ++index) { if ((it->second.options[c] || everyTab) && this->m_tabs[index] != nullptr) { static_cast(this->m_tabs[index])->AddLinkRecord(reinterpret_cast(a), b, name); for (unsigned int line = 0; line < lines.size(); ++line) { UIWindow* tab = this->m_tabs[index]; const int width = this->TextWidth(lines[line].c_str(), 0, 0, 12, 0, 0); if (width <= tab->geometry.width - 20 || hasLink) { static_cast(this->m_tabs[index])->AddText(lines[line].c_str(), b, name); } else { static_cast(this->m_tabs[index])->AddTextWrapped(lines[line].c_str(), b, name); } } } } index = 0; for (std::map::iterator it = g_session.m_subChatWnds.begin(); it != g_session.m_subChatWnds.end(); ++it, ++index) { if (it->second.options[c] || everyTab) { g_windowMgr.retainedWindows[index]->SendMsg(this, 0x25, a, b, reinterpret_cast(name), 0); } } } return 0; case 0x22: if (g_windowMgr.unknown_0030 < 0 || g_windowMgr.unknown_0034 < 0 || g_windowMgr.unknown_0030 > g_renderer->viewport.width - this->geometry.width || g_windowMgr.unknown_0034 > g_renderer->viewport.height - this->geometry.height) { g_windowMgr.unknown_0030 = 0; g_windowMgr.unknown_0034 = g_renderer->viewport.height - this->geometry.height; } this->geometry.x = g_windowMgr.unknown_0030; this->geometry.y = g_windowMgr.unknown_0034; this->ResizeRows(g_windowMgr.unknown_004C); this->Move(this->geometry.x, this->geometry.y); return 0; case 6: switch (a) { case 0xC1: if (UIWindow* window = g_windowMgr.sub_A978D0(0x84)) { window->SendMsg(this, 6, 0xC1, this->m_currentTab, 1, 0); } return 0; case 0x15B: g_windowMgr.SelectWhisperName(reinterpret_cast(b)); return 0; case 0x165: *reinterpret_cast(&g_windowMgr.unknown_03F4) = 1; this->m_currentTab = 0; if (this->unknown_0118 == 0) { this->unknown_0118 = 1; } this->ResizeRows(this->unknown_0118); this->Invalidate(); return 0; case 0x166: this->SendMsg(this, 0x75, this->m_currentTab, 0, 0, 0); return 0; case 0xEE: { this->unknown_00F0 = 1; if (g_windowMgr.unknown_0214 != nullptr) { g_windowMgr.CloseWindow(0x1A); } UIWindow* menu = g_windowMgr.sub_A978D0(0x1A); menu->SendMsg(nullptr, 0x1F, reinterpret_cast(LookupMessage(0x55)), 0, 0, 0); menu->SendMsg(nullptr, 0x1F, reinterpret_cast(LookupMessage(0x56)), 0, 0, 0); menu->SendMsg(nullptr, 0x1F, reinterpret_cast(LookupMessage(0x1B5)), 0, 0, 0); menu->SendMsg(nullptr, 0x1F, reinterpret_cast(LookupMessage(0x939)), 0, 0, 0); menu->SendMsg(nullptr, 0x1F, reinterpret_cast(LookupMessage(0xFC0)), 0, 0, 0); menu->SendMsg(nullptr, 0x28, *reinterpret_cast(&g_session.unknown_5490[0x8]), 0, 0, 0); menu->Resize(0x70, 0x30); menu->SendMsg(nullptr, 0x53, 1, 0, 0, 0); int x = this->geometry.width - 0x73; int y = this->geometry.height - this->m_inputAreaHeight; this->ToScreen(x, y); menu->Move(x + 1, y - 0x2F); return 0; } case 0xE1: { this->unknown_00F0 = 0; if (g_windowMgr.unknown_0214 != nullptr) { g_windowMgr.CloseWindow(0x1A); } const int count = static_cast(static_cast(this->unknown_00C0)->history.size()); if (count == 0) { g_windowMgr.SendNotice(3, 0, 0, 0, 0, 0); g_windowMgr.SendNotice(1, reinterpret_cast(LookupMessage(0xC0)), 0xFF, 0, 0, 0); return 0; } UIWindow* menu = g_windowMgr.sub_A978D0(0x1A); for (int i = 0; i < count; ++i) { menu->SendMsg(nullptr, 0x1F, reinterpret_cast(static_cast(this->unknown_00C0)->history[i].c_str()), 0, 0, 0); } menu->SendMsg(nullptr, 0x1F, reinterpret_cast(""), 0, 0, 0); int height = (count + 1) * 16; if (height > 0x50) { height = 0x50; } menu->SendMsg(nullptr, 0x28, static_cast(this->unknown_00C0)->historyIndex, 0, 0, 0); menu->Resize(0x70, height); menu->SendMsg(nullptr, 0x53, 1, 0, 0, 0); int x = 0; int y = this->geometry.height - this->m_inputAreaHeight; this->ToScreen(x, y); menu->Move(x + 1, y - height + 1); return 0; } case 0xB8: { if (!g_windowMgr.inputSuggestionsEnabled && g_windowMgr.chatModeChanged) { return 0; } char buffer[0x16A]; std::memset(buffer, 0, sizeof(buffer)); std::string text(static_cast(this->m_chatEdit)->Text()); const std::size_t lineEnd = text.find('\n', 0); if (lineEnd != std::string::npos) { text = text.substr(0, lineEnd); } UIChatEditCtrl* edit = static_cast(this->m_chatEdit); bool rejected = HasItemLinkTag(std::string(edit->Text())); if (!rejected) { UIItemTagOnChat* tags = static_cast(this->m_chatEdit)->unknown_0144; if (tags != nullptr && tags->unknown_0140.size() != 0) { text = std::string(tags->unknown_0140); static_cast(this->m_chatEdit)->unknown_0144->unknown_0140.clear(); static_cast(this->m_chatEdit)->unknown_0144->ClearTags(); rejected = static_cast(this->m_chatEdit)->LinkedText().size() != 0; } if (!rejected) { if (static_cast(this->m_chatEdit)->LinkedText().size() != 0) { text = static_cast(this->m_chatEdit)->LinkedText(); } edit = static_cast(this->m_chatEdit); rejected = HasNaviLinkTag(std::string(edit->Text())); } } if (rejected) { static_cast(this->m_chatEdit)->SetText(""); g_windowMgr.SendNotice(1, reinterpret_cast(LookupMessage(0xBE2)), 0xFF, 0, 0, 0); return 0; } StringFormat(buffer, sizeof(buffer), static_cast(-1), "%s", text.c_str()); static_cast(this->m_chatEdit)->PushHistory(); this->m_inputEdit = static_cast(this->m_chatEdit); g_windowMgr.SetFocus(static_cast(this->m_chatEdit)); static_cast(this->m_chatEdit)->linkedText.clear(); if (std::strlen(buffer) == 0) { return 0; } if (g_serviceType == 1 && !ValidateCharacterName(buffer)) { g_windowMgr.SendNotice(1, reinterpret_cast(LookupMessage(0xBE)), 0xFF, 0, 0, 0); return 0; } if (static_cast(this->unknown_00C0)->Length() != 0 && buffer[0] != '/') { g_windowMgr.unknown_04DC = buffer; g_modeMgr.gameMode->SendMsg(0xB, reinterpret_cast(static_cast(this->unknown_00C0)->Text()), 0, 0, 0); return 0; } if (g_session.ProcessEmotionShortcut(buffer) != 0) { return 0; } std::string parts[3]; int talkType; const int offset = g_session.GetTalkType(buffer, &talkType, parts); if (talkType == 0x37 || talkType == 0x1A || talkType == 0x12) { if (_mbsstr(reinterpret_cast(buffer), reinterpret_cast("")) != nullptr || _mbsstr(reinterpret_cast(buffer), reinterpret_cast("")) != nullptr) { g_windowMgr.SendNotice(1, reinterpret_cast(LookupMessage(0xAFC)), 0x64FFFF, 0, 0, 0); return 0; } } if (offset != -1) { g_windowMgr.SetUnknown04DC(buffer + offset); } g_modeMgr.gameMode->SendMsg(0x2A, talkType, reinterpret_cast(parts), 0, 0); return 0; } case 0xD4: this->SendMsg(nullptr, 2, 0, 0, 0, 0); return 0; case 0x176: { int noSubCount = static_cast(g_session.m_noSubChatWnds.size()); int subCount = static_cast(g_session.m_subChatWnds.size()); const int total = noSubCount + subCount; if (total == this->unknown_0110) { g_windowMgr.Confirm(LookupMessage(0x62A), 0, true, false, 0, "Message", 0x118, 0x78, 0); return 0; } SubChatWndInfo info = {}; info.name = "NewTab_"; char number[16]; info.name += _itoa(total + 1, number, 10); if ((noSubCount + 1) * this->m_tabWidth > this->geometry.width - 0x5A) { g_session.m_subChatWnds[subCount] = info; g_windowMgr.sub_A97580(static_cast(g_windowMgr.retainedWindows.size()), -1, -1, 0, 0); } else { g_session.m_noSubChatWnds[noSubCount] = info; this->m_tabControl->AddTab(info.name.c_str(), ""); this->m_tabWidth = this->m_tabControl->geometry.width / g_session.m_noSubChatWnds.size(); UIChatHisBox* box = new UIChatHisBox(); static_cast(box)->Configure(0, 0, this->geometry.width - 8, this->geometry.height - this->m_inputAreaHeight - this->m_inputOffsetY - 4, 0); box->Move(-100 - this->geometry.width, -100); this->AddChild(box); this->m_tabs.push_back(box); RECT rectangle; rectangle.left = 3; rectangle.top = 0; rectangle.right = this->m_tabControl->geometry.width + 3; rectangle.bottom = this->m_tabControl->geometry.height; this->FillSurface(0xFFFF00FF, &rectangle); this->m_currentTab = noSubCount; this->m_tabControl->unknown_007C = noSubCount; this->RefreshTabs(true); this->ApplyHeaderLayout(); } return 0; } case 0xCA: { std::map& names = g_session.m_noSubChatWnds; if (names.size() <= 1) { return 0; } std::map::iterator found = names.find(this->m_currentTab); if (found == names.end()) { return 0; } const int key = found->first; names.erase(found); if (key != names.size()) { std::map saved(names); names.clear(); int index = 0; for (std::map::iterator it = saved.begin(); it != saved.end(); ++it, ++index) { names[index] = it->second; } } std::vector::iterator tab = this->m_tabs.begin() + this->m_currentTab; if (tab != this->m_tabs.end()) { this->RemoveChild(*tab, 1); this->m_tabs.erase(tab); } this->m_tabControl->RemoveTab(this->m_currentTab); this->m_tabWidth = this->m_tabControl->geometry.width / names.size(); RECT rectangle; rectangle.left = 3; rectangle.top = 0; rectangle.right = this->m_tabWidth + this->m_tabControl->geometry.width + 3; rectangle.bottom = this->m_tabControl->geometry.height; this->FillSurface(0xFFFF00FF, &rectangle); if (this->m_currentTab > 0) { --this->m_currentTab; this->m_tabControl->unknown_007C = this->m_currentTab; } this->RefreshTabs(true); this->ApplyHeaderLayout(); if (UIWindow* window = g_windowMgr.LookupWindow(0x84)) { window->SendMsg(this, 6, 0xC1, this->m_currentTab, 1, 1); } return 0; } default: return 0; } case 0xE: if (a == 2 && static_cast(g_windowMgr.unknown_03F4) != 0) { const int top = this->m_inputOffsetY + 0x38; if (c < top) { if (this->geometry.unknown_0030 != 0) { this->ResizeRows(0); } } else { int rows = (c - top) / 14 + 1; if (rows > 15) { rows = 15; } if (this->geometry.unknown_0030 != rows) { this->ResizeRows(rows); } } } return 0; case 2: if (static_cast(g_windowMgr.unknown_03F4) != 0) { const int step = a == 0 ? 1 : -1; int rows = step; if (this->geometry.unknown_0030 != 0) { rows = static_cast(this->geometry.unknown_0030) + step * 3; } if (rows > 0) { this->ResizeRows(rows / 3 * 3 + 1); } else { this->ResizeRows(this->geometry.unknown_0030 != 0 ? 0 : rows); } } return 0; case 4: { UIListBox* tab = static_cast(this->m_tabs[this->m_currentTab]); if (tab != nullptr && tab->verticalOverflow != 0) { if (HIBYTE(GetKeyState(VK_CONTROL))) { tab->SendMsg(nullptr, 7, tab->LineCount(), 0, 0, 0); } else { tab->SendMsg(nullptr, 10, 0, 0, 0, 0); } } return 0; } case 3: { UIListBox* tab = static_cast(this->m_tabs[this->m_currentTab]); if (tab != nullptr && tab->verticalOverflow != 0) { if (HIBYTE(GetKeyState(VK_CONTROL))) { tab->SendMsg(nullptr, 7, -tab->LineCount(), 0, 0, 0); } else { tab->SendMsg(nullptr, 9, 0, 0, 0, 0); } } return 0; } case 0xF: this->ResizeRows(a); return 0; case 0x10: if (static_cast(g_windowMgr.unknown_03F4) != 0 && this->geometry.unknown_0030 == 0) { this->ResizeRows(1); } return 0; } return this->UIFrameWnd::SendMsg(sender, message, a, b, c, d); }