Working on messenger in portrait mode

This commit is contained in:
Vladislav Khorev committed 2026-06-26 20:04:08 +03:00
1 parent 0b4df8ab2d
commit 8c42179576
10 files changed
+498 -65

No files matched your search

+155 -25
View File
@@ -7,6 +7,7 @@
#include <algorithm>
#include <string>
namespace ZL {
static int questStatusPriority(Quest::QuestStatus status) {
@@ -149,9 +150,12 @@ namespace ZL {
phoneChatListHintRoot = loadUiFromFile("resources/w/ui/screen_phone_chat_list_hint001.json", renderer, zipFile);
verticalPhoneChatListRoot = loadUiFromFile("resources/w/ui/portrait_screen_phone_chat_list.json", renderer, zipFile);
verticalPhoneChatListHintRoot = loadUiFromFile("resources/w/ui/portrait_screen_phone_chat_list_hint001.json", renderer, zipFile);
phoneChat1Root = loadUiFromFile("resources/w/ui/screen_phone_chat1.json", renderer, zipFile);
phoneChat2Root = loadUiFromFile("resources/w/ui/screen_phone_chat2.json", renderer, zipFile);
phoneChat3Root = loadUiFromFile("resources/w/ui/screen_phone_chat3.json", renderer, zipFile);
phoneChat1Root = loadUiFromFile("resources/w/ui/screen_phone_chat1.json", renderer, zipFile);
phoneChat2Root = loadUiFromFile("resources/w/ui/screen_phone_chat2.json", renderer, zipFile);
phoneChat3Root = loadUiFromFile("resources/w/ui/screen_phone_chat3.json", renderer, zipFile);
verticalPhoneChat1Root = loadUiFromFile("resources/w/ui/portrait_screen_phone_chat1.json", renderer, zipFile);
verticalPhoneChat2Root = loadUiFromFile("resources/w/ui/portrait_screen_phone_chat2.json", renderer, zipFile);
verticalPhoneChat3Root = loadUiFromFile("resources/w/ui/portrait_screen_phone_chat3.json", renderer, zipFile);
modalMenuRoot = loadUiFromFile("resources/w/ui/screen_modal_menu.json", renderer, zipFile);
newInventoryRoot = loadUiFromFile("resources/w/ui/screen_inventory.json", renderer, zipFile);
@@ -657,15 +661,15 @@ namespace ZL {
uiManager.setButtonCallback("phoneMain", [this](const std::string&) {});
uiManager.setTextButtonCallback("chat1button", [this](const std::string&) {
gameState_.chatUnread[0] = false;
openPhoneChatFromList(0, phoneChat1Root);
openPhoneChatFromList(0, isPortraitMode() ? verticalPhoneChat1Root : phoneChat1Root);
});
uiManager.setTextButtonCallback("chat2button", [this](const std::string&) {
gameState_.chatUnread[1] = false;
openPhoneChatFromList(1, phoneChat2Root);
openPhoneChatFromList(1, isPortraitMode() ? verticalPhoneChat2Root : phoneChat2Root);
});
uiManager.setTextButtonCallback("chat3button", [this](const std::string&) {
gameState_.chatUnread[2] = false;
openPhoneChatFromList(2, phoneChat3Root);
openPhoneChatFromList(2, isPortraitMode() ? verticalPhoneChat3Root : phoneChat3Root);
});
}
@@ -784,7 +788,33 @@ namespace ZL {
gameState_.tutorialPhoneChatScreenOpened = true;
uiManager.pushMenuFromSavedRoot(chatRoot);
rebuildChatBubblesFromHistory(chatIndex);
// Build a dedicated chatUiManager root sized to the FBO chat area
{
const bool portrait = isPortraitMode();
const float fboW = portrait ? CHAT_FBO_WIDTH_P : CHAT_FBO_WIDTH_L;
const float fboH = portrait ? (CHAT_FBO_OFFSET_UP_P + CHAT_FBO_OFFSET_DOWN_P)
: (CHAT_FBO_OFFSET_UP_L + CHAT_FBO_OFFSET_DOWN_L);
auto fboRoot = std::make_shared<UiNode>();
fboRoot->name = "chatFboRoot";
fboRoot->width = fboW;
fboRoot->height = fboH;
fboRoot->screenRect = { 0.0f, 0.0f, fboW, fboH };
auto fboContainer = std::make_shared<UiNode>();
fboContainer->name = "chatMessagesContainer";
fboContainer->width = fboW;
fboContainer->height = fboH;
fboContainer->screenRect = { 0.0f, 0.0f, fboW, fboH };
fboRoot->children.push_back(fboContainer);
chatUiManager.replaceRoot(fboRoot);
chatFrameBuffer_ = std::make_unique<FrameBuffer>(
static_cast<int>(fboW), static_cast<int>(fboH), false);
chatFboQuadLastProjW_ = -1.0f;
}
rebuildChatBubblesFromHistory(chatIndex, isPortraitMode());
uiManager.setButtonCallback("phoneExitButton", [this](const std::string&) {
closePhoneScreenFromChat();
@@ -808,6 +838,8 @@ namespace ZL {
activeChatIndex_ = -1;
currentPhoneSubScreen_ = PhoneSubScreen::ChatList;
phoneChatVisibleBubbles_.clear();
chatUiManager.replaceRoot(nullptr);
chatFrameBuffer_.reset();
uiManager.popMenu();
uiManager.updateAllLayouts();
if (gameState_.tutorialPhoneChatScreenOpened)
@@ -832,6 +864,8 @@ namespace ZL {
currentPhoneSubScreen_ = PhoneSubScreen::None;
uiState_ = GameUiState::Gameplay;
phoneChatVisibleBubbles_.clear();
chatUiManager.replaceRoot(nullptr);
chatFrameBuffer_.reset();
const int depth = uiManager.menuStackSize();
for (int i = 0; i < depth; ++i) uiManager.popMenu();
uiManager.updateAllLayouts();
@@ -1261,42 +1295,69 @@ namespace ZL {
if (i > 0) totalHeight += CHAT_SPACING;
}
const float available = CHAT_TOP_Y - CHAT_BOTTOM_Y;
const float topY = (totalHeight <= available)
? CHAT_TOP_Y
: CHAT_BOTTOM_Y + totalHeight;
const float fboH = CHAT_FBO_OFFSET_UP_L + CHAT_FBO_OFFSET_DOWN_L;
const float topY = (totalHeight <= fboH) ? fboH : totalHeight;
float cursor = topY;
for (auto& bubble : phoneChatVisibleBubbles_) {
auto node = uiManager.findNode(bubble.nodeName);
auto node = chatUiManager.findNode(bubble.nodeName);
if (!node) continue;
node->localY = cursor - bubble.height;
cursor -= bubble.height + CHAT_SPACING;
}
uiManager.updateAllLayouts();
chatUiManager.updateAllLayouts();
}
void MenuManager::rebuildChatBubblesFromHistory(int chatIndex) {
uiManager.clearChatBubbles("chatMessagesContainer");
void MenuManager::recomputePhoneChatPositionsPortrait() {
float totalHeight = 0.0f;
for (size_t i = 0; i < phoneChatVisibleBubbles_.size(); ++i) {
totalHeight += phoneChatVisibleBubbles_[i].height;
if (i > 0) totalHeight += CHAT_SPACING;
}
const float fboH = CHAT_FBO_OFFSET_UP_P + CHAT_FBO_OFFSET_DOWN_P;
const float topY = (totalHeight <= fboH) ? fboH : totalHeight;
float cursor = topY;
for (auto& bubble : phoneChatVisibleBubbles_) {
auto node = chatUiManager.findNode(bubble.nodeName);
if (!node) continue;
node->localY = cursor - bubble.height;
cursor -= bubble.height + CHAT_SPACING;
}
chatUiManager.updateAllLayouts();
}
void MenuManager::rebuildChatBubblesFromHistory(int chatIndex, bool isPortrait) {
chatUiManager.clearChatBubbles("chatMessagesContainer");
phoneChatVisibleBubbles_.clear();
if (chatIndex < 0 || chatIndex > 2) return;
for (const auto& msg : gameState_.chatHistory[chatIndex]) {
const bool inc = msg.incoming;
const std::string nodeName = uiManager.addChatBubble(
const std::string nodeName = chatUiManager.addChatBubble(
"chatMessagesContainer", msg.text, inc,
inc ? texBubbleInCenter_ : texBubbleOutCenter_,
inc ? texBubbleInLT_ : texBubbleOutLT_,
inc ? texBubbleInLB_ : texBubbleOutLB_,
inc ? texBubbleInRT_ : texBubbleOutRT_,
inc ? texBubbleInRB_ : texBubbleOutRB_,
renderer, "resources/fonts/DroidSans.ttf", 20, CONST_ZIP_FILE);
renderer, "resources/fonts/DroidSans.ttf", isPortrait ? 32 : 20, isPortrait, CONST_ZIP_FILE);
if (!nodeName.empty()) {
auto n = uiManager.findNode(nodeName);
auto n = chatUiManager.findNode(nodeName);
phoneChatVisibleBubbles_.push_back({ nodeName, n ? n->height : 60.0f });
}
}
recomputePhoneChatPositions();
if (isPortrait)
{
recomputePhoneChatPositionsPortrait();
}
else
{
recomputePhoneChatPositions();
}
}
void MenuManager::onChatBubbleReady(const std::string& text, bool incoming) {
@@ -1310,25 +1371,34 @@ namespace ZL {
if (uiState_ != GameUiState::PhoneScreen) return;
const std::string nodeName = uiManager.addChatBubble(
bool isPortrait = isPortraitMode();
const std::string nodeName = chatUiManager.addChatBubble(
"chatMessagesContainer", text, incoming,
incoming ? texBubbleInCenter_ : texBubbleOutCenter_,
incoming ? texBubbleInLT_ : texBubbleOutLT_,
incoming ? texBubbleInLB_ : texBubbleOutLB_,
incoming ? texBubbleInRT_ : texBubbleOutRT_,
incoming ? texBubbleInRB_ : texBubbleOutRB_,
renderer, "resources/fonts/DroidSans.ttf", 20, CONST_ZIP_FILE);
renderer, "resources/fonts/DroidSans.ttf", isPortrait ? 32 : 20, isPortrait, CONST_ZIP_FILE);
if (nodeName.empty()) return;
auto node = uiManager.findNode(nodeName);
auto node = chatUiManager.findNode(nodeName);
if (node) {
node->scaleX = 0.0f;
node->scaleY = 0.0f;
phoneChatVisibleBubbles_.push_back({ nodeName, node->height });
}
recomputePhoneChatPositions();
uiManager.startPopIn(nodeName, 300.0f);
if (isPortrait)
{
recomputePhoneChatPositionsPortrait();
}
else
{
recomputePhoneChatPositions();
}
chatUiManager.startPopIn(nodeName, 300.0f);
}
void MenuManager::setDarklandsMode(bool enabled)
@@ -1537,6 +1607,57 @@ namespace ZL {
void MenuManager::update(float deltaMs) {
updateToasts(deltaMs);
chatUiManager.update(deltaMs);
}
void MenuManager::drawChatFbo(Renderer& renderer) {
if (!chatFrameBuffer_) return;
const bool portrait = isPortraitMode();
const float fboW = portrait ? CHAT_FBO_WIDTH_P : CHAT_FBO_WIDTH_L;
const float offsetUp = portrait ? CHAT_FBO_OFFSET_UP_P : CHAT_FBO_OFFSET_UP_L;
const float offsetDown= portrait ? CHAT_FBO_OFFSET_DOWN_P : CHAT_FBO_OFFSET_DOWN_L;
const float fboH = offsetUp + offsetDown;
const float projW = Environment::projectionWidth;
const float projH = Environment::projectionHeight;
// Pass 1: render chatUiManager into FBO
chatFrameBuffer_->Bind();
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
chatUiManager.draw(renderer, fboW, fboH);
chatFrameBuffer_->Unbind();
// Pass 2: blit FBO texture onto screen, centered with up/down offsets from screen center
if (chatFboQuadLastProjW_ != projW || chatFboQuadLastPortrait_ != portrait ||
chatFboQuadLastProjH_ != projH) {
const float x0 = (projW - fboW) * 0.5f;
const float y0 = projH * 0.5f - offsetDown;
const float x1 = x0 + fboW;
const float y1 = y0 + fboH;
VertexDataStruct vd;
vd.PositionData = { {x0,y0,0},{x0,y1,0},{x1,y1,0},
{x0,y0,0},{x1,y1,0},{x1,y0,0} };
vd.TexCoordData = { {0,0},{0,1},{1,1},
{0,0},{1,1},{1,0} };
chatFboQuad_.AssignFrom(vd);
chatFboQuadLastProjW_ = projW;
chatFboQuadLastProjH_ = projH;
chatFboQuadLastPortrait_ = portrait;
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
renderer.PushProjectionMatrix(projW, projH, -1, 1);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.RenderUniform1f("uAlpha", 1.0f);
glBindTexture(GL_TEXTURE_2D, chatFrameBuffer_->getTextureID());
renderer.DrawVertexRenderStruct(chatFboQuad_);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
}
void MenuManager::saveSettings() {
@@ -1609,10 +1730,19 @@ namespace ZL {
currentPhoneSubScreen_ = PhoneSubScreen::MapUni;
break;
case PhoneSubScreen::ChatList:
case PhoneSubScreen::Chat:
openPhoneScreen();
openPhoneMessenger();
break;
case PhoneSubScreen::Chat: {
openPhoneScreen();
openPhoneMessenger();
std::shared_ptr<UiNode> chatRoot;
if (savedChatIndex == 0) chatRoot = isPortraitMode() ? verticalPhoneChat1Root : phoneChat1Root;
else if (savedChatIndex == 1) chatRoot = isPortraitMode() ? verticalPhoneChat2Root : phoneChat2Root;
else if (savedChatIndex == 2) chatRoot = isPortraitMode() ? verticalPhoneChat3Root : phoneChat3Root;
if (chatRoot) openPhoneChatFromList(savedChatIndex, chatRoot);
break;
}
default:
openPhoneScreen();
break;