og-edopro/gframe/game.cpp
2026-08-20 16:36:19 +02:00

4022 lines
199 KiB
C++

#include <sstream>
#include <nlohmann/json.hpp>
#include <irrlicht.h>
#include "client_updater.h"
#include "game_config.h"
#include "repo_manager.h"
#include "image_downloader.h"
#include "config.h"
#include "game.h"
#include "server_lobby.h"
#include "sound_manager.h"
#include "image_manager.h"
#include "data_manager.h"
#include "deck_manager.h"
#include "dllinterface.h"
#include "replay.h"
#include "materials.h"
#include "duelclient.h"
#include "netserver.h"
#include "replay_mode.h"
#include "single_mode.h"
#include "CGUICustomCheckBox/CGUICustomCheckBox.h"
#include "CGUICustomTable/CGUICustomTable.h"
#include "CGUICustomTabControl/CGUICustomTabControl.h"
#include "CGUISkinSystem/CGUISkinSystem.h"
#include "CGUICustomContextMenu/CGUICustomContextMenu.h"
#include "CGUICustomContextMenu/CGUICustomMenu.h"
#include "CGUICustomText/CGUICustomText.h"
#include "CGUIFileSelectListBox/CGUIFileSelectListBox.h"
#include "CProgressBar/CProgressBar.h"
#include "ResizeablePanel/ResizeablePanel.h"
#include "CGUITTFont/CGUITTFont.h"
#include "CGUIImageButton/CGUIImageButton.h"
#include "logging.h"
#include "utils_gui.h"
#include "custom_skin_enum.h"
#include "joystick_wrapper.h"
#include "CGUIWindowedTabControl/CGUIWindowedTabControl.h"
#include "file_stream.h"
#include "porting.h"
#include "fmt.h"
#if EDOPRO_ANDROID || EDOPRO_IOS
#include "CGUICustomComboBox/CGUICustomComboBox.h"
#define DispatchQueue() porting::dispatchQueuedMessages()
#else
#define DispatchQueue() ((void)0)
#endif
#if IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR==9
#define ClearZBuffer(driver) do {driver->clearBuffers(irr::video::ECBF_DEPTH);} while(0)
#else
#define ClearZBuffer(driver) do {driver->clearZBuffer();} while(0)
#endif
constexpr int CARD_IMG_WRAPPER_H_PADDING = 10;
constexpr int CARD_IMG_WRAPPER_V_PADDING = 9;
constexpr int CARD_IMG_WRAPPER_WIDTH = CARD_IMG_WIDTH + CARD_IMG_WRAPPER_H_PADDING * 2;
constexpr int CARD_IMG_WRAPPER_HEIGHT = CARD_IMG_HEIGHT + CARD_IMG_WRAPPER_V_PADDING * 2;
constexpr float CARD_IMG_WRAPPER_ASPECT_RATIO = ((float)CARD_IMG_WRAPPER_WIDTH) / ((float)CARD_IMG_WRAPPER_HEIGHT);
uint16_t PRO_VERSION = 0x1354;
namespace {
template<typename T>
inline T AlignElementWithParent(T elem) {
elem->setAlignment(irr::gui::EGUIA_SCALE, irr::gui::EGUIA_SCALE, irr::gui::EGUIA_SCALE, irr::gui::EGUIA_SCALE);
return elem;
}
}
namespace ygo {
#if EDOPRO_ANDROID || EDOPRO_IOS
#define AddComboBox(env, ...) irr::gui::CGUICustomComboBox::addCustomComboBox(env, __VA_ARGS__)
#else
#define AddComboBox(env, ...) env->addComboBox(__VA_ARGS__)
#endif
static inline epro::path_string NoSkinLabel() {
return Utils::ToPathString(gDataManager->GetSysString(2065));
}
Game::~Game() {
if(guiFont)
guiFont->drop();
if(textFont)
textFont->drop();
if(numFont)
numFont->drop();
if(adFont)
adFont->drop();
if(lpcFont)
lpcFont->drop();
if(filesystem)
filesystem->drop();
if(skinSystem)
delete skinSystem;
}
void Game::Initialize() {
dpi_scale = gGameConfig->dpi_scale;
duel_param = gGameConfig->lastDuelParam;
if(!device)
device = GUIUtils::CreateDevice(gGameConfig);
#if !EDOPRO_ANDROID && !EDOPRO_IOS
device->enableDragDrop(true, [](irr::core::vector2di pos, bool isFile) ->bool {
if(isFile) {
if(mainGame->dInfo.isInDuel || mainGame->dInfo.isInLobby || mainGame->is_siding
|| mainGame->wRoomListPlaceholder->isVisible() || mainGame->wLanWindow->isVisible()
|| mainGame->wCreateHost->isVisible() || mainGame->wHostPrepare->isVisible())
return false;
else
return true;
} else {
auto elem = mainGame->env->getRootGUIElement()->getElementFromPoint(pos);
if(elem && elem != mainGame->env->getRootGUIElement()) {
if(elem->hasType(irr::gui::EGUIET_EDIT_BOX) && elem->isEnabled())
return true;
return false;
}
irr::core::vector2di convpos = mainGame->Resize(pos.X, pos.Y, true);
auto x = convpos.X;
auto y = convpos.Y;
if(mainGame->is_building && !mainGame->is_siding) {
if(x >= 314 && x <= 794) {
if((y >= 164 && y <= 435) || (y >= 466 && y <= 530) || (y >= 564 && y <= 628))
return true;
}
}
}
return false;
});
#endif
#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR == 9
device->toggleTouchEventMouseTranslation(true, 15 * dpi_scale);
#endif
filesystem = device->getFileSystem();
filesystem->grab();
skinSystem = new CGUISkinSystem(epro::format(EPRO_TEXT("{}/skin"), Utils::GetWorkingDirectory()).data(), device.get());
if(!skinSystem)
throw std::runtime_error("Couldn't create skin system");
linePatternGL = 0x0f0f;
menuHandler.prev_sel = -1;
driver = device->getVideoDriver();
imageManager.SetDevice(device.get());
imageManager.Initial();
RefreshAiDecks();
if(!discord.Initialize())
gGameConfig->discordIntegration = false;
if(gGameConfig->discordIntegration)
discord.UpdatePresence(DiscordWrapper::INITIALIZE);
PopulateResourcesDirectories();
env = device->getGUIEnvironment();
env->getRootGUIElement()->setRelativePosition({ {}, {(irr::s32)(1024 * dpi_scale), (irr::s32)(640 * dpi_scale) } });
auto textfont = gGameConfig->textfont;
textfont.size = Scale(textfont.size);
auto fallbackFonts = gGameConfig->fallbackFonts;
for(auto& font : fallbackFonts)
font.size = Scale(font.size);
guiFont = irr::gui::CGUITTFont::createTTFont(env, textfont, fallbackFonts);
if(!guiFont)
throw std::runtime_error("Failed to load text font");
textFont = guiFont;
textFont->grab();
GameConfig::TextFont numfont{ gGameConfig->numfont, (uint8_t)Scale(16) };
numFont = irr::gui::CGUITTFont::createTTFont(env, numfont, fallbackFonts);
numfont.size = Scale(12);
adFont = irr::gui::CGUITTFont::createTTFont(env, numfont, fallbackFonts);
numfont.size = Scale(48);
lpcFont = irr::gui::CGUITTFont::createTTFont(env, numfont, fallbackFonts);
if(!numFont || !adFont || !lpcFont)
throw std::runtime_error("Failed to load numbers font");
if(!ApplySkin(gGameConfig->skin, false, true)) {
gGameConfig->skin = NoSkinLabel();
ApplySkin(gGameConfig->skin, false, true);
}
smgr = device->getSceneManager();
wCommitsLog = env->addWindow(Scale(0, 0, 500 + 10, 400 + 35 + 35), false, gDataManager->GetSysString(1209).data());
defaultStrings.emplace_back(wCommitsLog, 1209);
wCommitsLog->setVisible(false);
wCommitsLog->getCloseButton()->setEnabled(false);
wCommitsLog->getCloseButton()->setVisible(false);
stCommitLog = irr::gui::CGUICustomText::addCustomText(L"", false, env, wCommitsLog, -1, Scale(5, 30, 505, 430));
stCommitLog->setWordWrap(true);
((irr::gui::CGUICustomText*)stCommitLog)->enableScrollBar();
btnCommitLogExit = env->addButton(Scale(215, 435, 285, 460), wCommitsLog, BUTTON_REPO_CHANGELOG_EXIT, gDataManager->GetSysString(1211).data());
defaultStrings.emplace_back(btnCommitLogExit, 1211);
chkCommitLogExpand = env->addCheckBox(false, Scale(295, 435, 500, 460), wCommitsLog, BUTTON_REPO_CHANGELOG_EXPAND, gDataManager->GetSysString(1447).data());
defaultStrings.emplace_back(chkCommitLogExpand, 1447);
mTopMenu = irr::gui::CGUICustomMenu::addCustomMenu(env);
mRepositoriesInfo = mTopMenu->getSubMenu(mTopMenu->addItem(gDataManager->GetSysString(2045).data(), 1, true, true));
mAbout = mTopMenu->getSubMenu(mTopMenu->addItem(gDataManager->GetSysString(1970).data(), 2, true, true));
wAbout = env->addWindow(Scale(0, 0, 450, 700), false, L"", mAbout);
wAbout->getCloseButton()->setVisible(false);
wAbout->setDraggable(false);
wAbout->setDrawTitlebar(false);
wAbout->setDrawBackground(false);
stAbout = irr::gui::CGUICustomText::addCustomText(L"Project Ignis: EDOPro\n"
L"The bleeding-edge automatic duel simulator\n"
L"\n"
L"Copyright (C) 2020-2026 Edoardo Lolletti (edo9300) and others\n"
L"Card scripts and supporting resources by Project Ignis.\n"
L"https://github.com/edo9300/edopro\n"
L"https://github.com/edo9300/ygopro-core\n"
L"https://github.com/ProjectIgnis/CardScripts\n"
L"https://github.com/ProjectIgnis/BabelCDB\n"
L"https://github.com/ProjectIgnis/windbot\n"
L"Software components licensed under the GNU AGPLv3 or later. See LICENSE for more details.\n"
L"Supporting resources and app icon are distributed under separate licenses in their subfolders.\n"
L"\n"
L"Project Ignis:\n"
L"ahtelel, Cybercatman, Dragon3989, DyXel, edo9300, EerieCode,"
L"Finn, Gideon, Hatter, Icematoro, Larry126, Naim, pyrQ, Sanct,"
L"senpaizuri, Steeldarkeagel, TheRazgriz, WolfOfWolves, Yamato, YoshiDuels\n"
L"\n"
L"Default background and icon: LogicalNonsense\n"
L"Default fields: Icematoro\n"
L"\n"
L"Forked from Fluorohydride's YGOPro, maintainer mercury233.\n"
L"Yu-Gi-Oh! is a trademark of Shueisha and Konami.\n"
L"This project is not affiliated with or endorsed by Shueisha or Konami.", false, env, wAbout, -1, Scale(10, 10, 440, 690));
((irr::gui::CGUICustomText*)stAbout)->enableScrollBar();
((irr::gui::CGUICustomText*)stAbout)->setWordWrap(true);
((irr::gui::CGUICustomContextMenu*)mAbout)->addItem(wAbout, -1);
wAbout->setRelativePosition(irr::core::recti(0, 0, std::min(Scale(450), stAbout->getTextWidth() + Scale(20)), std::min(stAbout->getTextHeight() + Scale(20), Scale(700))));
mVersion = mTopMenu->getSubMenu(mTopMenu->addItem(gDataManager->GetSysString(2040).data(), 3, true, true));
wVersion = env->addWindow(Scale(0, 0, 300, 135), false, L"", mVersion);
wVersion->getCloseButton()->setVisible(false);
wVersion->setDraggable(false);
wVersion->setDrawTitlebar(false);
wVersion->setDrawBackground(false);
stVersion = env->addStaticText(EDOPRO_VERSION_STRING, Scale(10, 10, 290, 35), false, false, wVersion);
int titleWidth = stVersion->getTextWidth();
stVersion->setRelativePosition(irr::core::recti(Scale(10), Scale(10), titleWidth + Scale(10), Scale(35)));
stCoreVersion = env->addStaticText(L"", Scale(10, 40, 500, 65), false, false, wVersion);
stExpectedCoreVersion = env->addStaticText(
GetLocalizedExpectedCore().data(),
Scale(10, 70, 290, 95), false, true, wVersion);
stCompatVersion = env->addStaticText(
GetLocalizedCompatVersion().data(),
Scale(10, 100, 290, 125), false, true, wVersion);
((irr::gui::CGUICustomContextMenu*)mVersion)->addItem(wVersion, -1);
//main menu
int mainMenuWidth = std::max(280, static_cast<int>(titleWidth / dpi_scale + 15));
mainMenuLeftX = 510 - mainMenuWidth / 2;
mainMenuRightX = 510 + mainMenuWidth / 2;
wMainMenu = env->addWindow(Scale(mainMenuLeftX, 200, mainMenuRightX, 450), false, EDOPRO_VERSION_STRING);
wMainMenu->getCloseButton()->setVisible(false);
//wMainMenu->setVisible(!is_from_discord);
#define OFFSET(x1, y1, x2, y2) Scale(10, 30 + offset, mainMenuWidth - 10, 60 + offset)
int offset = 0;
btnOnlineMode = env->addButton(OFFSET(10, 30, 270, 60), wMainMenu, BUTTON_ONLINE_MULTIPLAYER, gDataManager->GetSysString(2042).data());
defaultStrings.emplace_back(btnOnlineMode, 2042);
offset += 35;
btnLanMode = env->addButton(OFFSET(10, 30, 270, 60), wMainMenu, BUTTON_LAN_MODE, gDataManager->GetSysString(1200).data());
defaultStrings.emplace_back(btnLanMode, 1200);
offset += 35;
btnSingleMode = env->addButton(OFFSET(10, 65, 270, 95), wMainMenu, BUTTON_SINGLE_MODE, gDataManager->GetSysString(1201).data());
defaultStrings.emplace_back(btnSingleMode, 1201);
offset += 35;
btnReplayMode = env->addButton(OFFSET(10, 100, 270, 130), wMainMenu, BUTTON_REPLAY_MODE, gDataManager->GetSysString(1202).data());
defaultStrings.emplace_back(btnReplayMode, 1202);
offset += 35;
btnDeckEdit = env->addButton(OFFSET(10, 135, 270, 165), wMainMenu, BUTTON_DECK_EDIT, gDataManager->GetSysString(1204).data());
defaultStrings.emplace_back(btnDeckEdit, 1204);
offset += 35;
btnModeExit = env->addButton(OFFSET(10, 170, 270, 200), wMainMenu, BUTTON_MODE_EXIT, gDataManager->GetSysString(1210).data());
defaultStrings.emplace_back(btnModeExit, 1210);
offset += 35;
#undef OFFSET
//lan mode
wLanWindow = env->addWindow(Scale(220, 100, 800, 520), false, gDataManager->GetSysString(1200).data());
defaultStrings.emplace_back(wLanWindow, 1200);
wLanWindow->getCloseButton()->setVisible(false);
wLanWindow->setVisible(false);
irr::gui::IGUIElement* tmpptr = env->addStaticText(gDataManager->GetSysString(1220).data(), Scale(10, 30, 220, 50), false, false, wLanWindow);
defaultStrings.emplace_back(tmpptr, 1220);
ebNickName = env->addEditBox(gGameConfig->nickname.data(), Scale(110, 25, 450, 50), true, wLanWindow, EDITBOX_NICKNAME);
ebNickName->setTextAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_CENTER);
lstHostList = env->addListBox(Scale(10, 60, 570, 320), wLanWindow, LISTBOX_LAN_HOST, true);
lstHostList->setItemHeight(Scale(18));
btnLanRefresh = env->addButton(Scale(240, 325, 340, 350), wLanWindow, BUTTON_LAN_REFRESH, gDataManager->GetSysString(1217).data());
defaultStrings.emplace_back(btnLanRefresh, 1217);
tmpptr = env->addStaticText(gDataManager->GetSysString(1221).data(), Scale(10, 360, 220, 380), false, false, wLanWindow);
defaultStrings.emplace_back(tmpptr, 1221);
ebJoinHost = env->addEditBox(gGameConfig->lasthost.data(), Scale(110, 355, 350, 380), true, wLanWindow);
ebJoinHost->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
ebJoinPort = env->addEditBox(gGameConfig->lastport.data(), Scale(360, 355, 420, 380), true, wLanWindow, EDITBOX_PORT_BOX);
ebJoinPort->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
tmpptr = env->addStaticText(gDataManager->GetSysString(1222).data(), Scale(10, 390, 220, 410), false, false, wLanWindow);
defaultStrings.emplace_back(tmpptr, 1222);
ebJoinPass = env->addEditBox(gGameConfig->roompass.data(), Scale(110, 385, 420, 410), true, wLanWindow);
ebJoinPass->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
btnJoinHost = env->addButton(Scale(460, 355, 570, 380), wLanWindow, BUTTON_JOIN_HOST, gDataManager->GetSysString(1223).data());
defaultStrings.emplace_back(btnJoinHost, 1223);
btnJoinCancel = env->addButton(Scale(460, 385, 570, 410), wLanWindow, BUTTON_JOIN_CANCEL, gDataManager->GetSysString(1212).data());
defaultStrings.emplace_back(btnJoinCancel, 1212);
btnCreateHost = env->addButton(Scale(460, 25, 570, 50), wLanWindow, BUTTON_CREATE_HOST, gDataManager->GetSysString(1224).data());
defaultStrings.emplace_back(btnCreateHost, 1224);
PopulateGameHostWindows();
PopulateAIBotWindow();
//img
wCardImg = env->addStaticText(L"", Scale(1, 1, 1 + CARD_IMG_WRAPPER_WIDTH, 1 + CARD_IMG_WRAPPER_HEIGHT), true, false, 0, -1, true);
wCardImg->setBackgroundColor(skin::CARDINFO_IMAGE_BACKGROUND_VAL);
wCardImg->setVisible(false);
imgCard = AlignElementWithParent(env->addImage(Scale(10, 9, 10 + CARD_IMG_WIDTH, 9 + CARD_IMG_HEIGHT), wCardImg));
imgCard->setImage(imageManager.tCover[0]);
imgCard->setScaleImage(true);
imgCard->setUseAlphaChannel(true);
//phase
wPhase = env->addStaticText(L"", Scale(480, 310, 855, 330));
wPhase->setVisible(false);
btnDP = AlignElementWithParent(env->addButton(Scale(0, 0, 50, 20), wPhase, -1, L"\xff24\xff30"));
btnDP->setEnabled(false);
btnDP->setPressed(true);
btnDP->setVisible(false);
btnSP = AlignElementWithParent(env->addButton(Scale(0, 0, 50, 20), wPhase, -1, L"\xff33\xff30"));
btnSP->setEnabled(false);
btnSP->setPressed(true);
btnSP->setVisible(false);
btnM1 = AlignElementWithParent(env->addButton(Scale(160, 0, 210, 20), wPhase, -1, L"\xff2d\xff11"));
btnM1->setEnabled(false);
btnM1->setPressed(true);
btnM1->setVisible(false);
btnBP = AlignElementWithParent(env->addButton(Scale(160, 0, 210, 20), wPhase, BUTTON_BP, L"\xff22\xff30"));
btnBP->setVisible(false);
btnM2 = AlignElementWithParent(env->addButton(Scale(160, 0, 210, 20), wPhase, BUTTON_M2, L"\xff2d\xff12"));
btnM2->setVisible(false);
btnEP = AlignElementWithParent(env->addButton(Scale(320, 0, 370, 20), wPhase, BUTTON_EP, L"\xff25\xff30"));
btnEP->setVisible(false);
PopulateTabSettingsWindow();
PopulateSettingsWindow();
wBtnSettings = env->addWindow(Scale(0, 610, 30, 640));
wBtnSettings->getCloseButton()->setVisible(false);
wBtnSettings->setDraggable(false);
wBtnSettings->setDrawTitlebar(false);
wBtnSettings->setDrawBackground(false);
auto dimBtnSettings = Scale(0, 0, 30, 30);
btnSettings = irr::gui::CGUIImageButton::addImageButton(env, dimBtnSettings, wBtnSettings, BUTTON_SHOW_SETTINGS);
btnSettings->setDrawBorder(false);
btnSettings->setImageSize(dimBtnSettings.getSize());
btnSettings->setImage(imageManager.tSettings);
//
wHand = env->addWindow(Scale(500, 450, 825, 605), false, L"");
wHand->getCloseButton()->setVisible(false);
wHand->setDraggable(false);
wHand->setDrawTitlebar(false);
wHand->setVisible(false);
for(int i = 0; i < 3; ++i) {
auto dim = Scale(10 + 105 * i, 10, 105 + 105 * i, 144);
btnHand[i] = irr::gui::CGUIImageButton::addImageButton(env, dim, wHand, BUTTON_HAND1 + i);
btnHand[i]->setImageSize(dim.getSize());
btnHand[i]->setImage(imageManager.tHand[i]);
}
//
wFTSelect = env->addWindow(Scale(550, 240, 780, 340), false, L"");
wFTSelect->getCloseButton()->setVisible(false);
wFTSelect->setVisible(false);
btnFirst = env->addButton(Scale(10, 30, 220, 55), wFTSelect, BUTTON_FIRST, gDataManager->GetSysString(100).data());
defaultStrings.emplace_back(btnFirst, 100);
btnSecond = env->addButton(Scale(10, 60, 220, 85), wFTSelect, BUTTON_SECOND, gDataManager->GetSysString(101).data());
defaultStrings.emplace_back(btnSecond, 101);
//message (310)
wMessage = env->addWindow(Scale(490, 200, 840, 340), false, gDataManager->GetSysString(1216).data());
defaultStrings.emplace_back(wMessage, 1216);
wMessage->getCloseButton()->setVisible(false);
wMessage->setVisible(false);
stMessage = irr::gui::CGUICustomText::addCustomText(L"", false, env, wMessage, -1, Scale(10, 20, 350, 100));
stMessage->setWordWrap(true);
stMessage->setTextAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_CENTER);
btnMsgOK = env->addButton(Scale(130, 105, 220, 130), wMessage, BUTTON_MSG_OK, gDataManager->GetSysString(1211).data());
defaultStrings.emplace_back(btnMsgOK, 1211);
//auto fade message (310)
wACMessage = env->addWindow(Scale(490, 240, 840, 300), false, L"");
wACMessage->getCloseButton()->setVisible(false);
wACMessage->setVisible(false);
wACMessage->setDrawBackground(false);
wACMessage->setDraggable(false);
stACMessage = irr::gui::CGUICustomText::addCustomText(L"", true, env, wACMessage, -1, Scale(0, 0, 350, 60), true);
stACMessage->setWordWrap(true);
stACMessage->setBackgroundColor(skin::DUELFIELD_ANNOUNCE_TEXT_BACKGROUND_COLOR_VAL);
auto tmp_color = skin::DUELFIELD_ANNOUNCE_TEXT_COLOR_VAL;
if(tmp_color != 0)
stACMessage->setOverrideColor(tmp_color);
stACMessage->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
//yes/no (310)
wQuery = env->addWindow(Scale(490, 200, 840, 340), false, gDataManager->GetSysString(560).data());
defaultStrings.emplace_back(wQuery, 560);
wQuery->getCloseButton()->setVisible(false);
wQuery->setVisible(false);
stQMessage = irr::gui::CGUICustomText::addCustomText(L"", false, env, wQuery, -1, Scale(10, 20, 350, 100));
stQMessage->setWordWrap(true);
stQMessage->setTextAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_CENTER);
btnYes = env->addButton(Scale(100, 105, 150, 130), wQuery, BUTTON_YES, gDataManager->GetSysString(1213).data());
defaultStrings.emplace_back(btnYes, 1213);
btnNo = env->addButton(Scale(200, 105, 250, 130), wQuery, BUTTON_NO, gDataManager->GetSysString(1214).data());
defaultStrings.emplace_back(btnNo, 1214);
//options (310)
wOptions = env->addWindow(Scale(490, 200, 840, 340), false, L"");
wOptions->getCloseButton()->setVisible(false);
wOptions->setVisible(false);
stOptions = irr::gui::CGUICustomText::addCustomText(L"", false, env, wOptions, -1, Scale(20, 20, 350, 100));
stOptions->setWordWrap(true);
stOptions->setTextAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_CENTER);
btnOptionOK = env->addButton(Scale(130, 105, 220, 130), wOptions, BUTTON_OPTION_OK, gDataManager->GetSysString(1211).data());
defaultStrings.emplace_back(btnOptionOK, 1211);
btnOptionp = env->addButton(Scale(20, 105, 60, 130), wOptions, BUTTON_OPTION_PREV, gDataManager->GetSysString(1432).data());
defaultStrings.emplace_back(btnOptionp, 1432);
btnOptionn = env->addButton(Scale(290, 105, 330, 130), wOptions, BUTTON_OPTION_NEXT, gDataManager->GetSysString(1433).data());
defaultStrings.emplace_back(btnOptionn, 1433);
for(int i = 0; i < 5; ++i) {
btnOption[i] = env->addButton(Scale(10, 30 + 40 * i, 340, 60 + 40 * i), wOptions, BUTTON_OPTION_0 + i, L"");
}
scrOption = env->addScrollBar(false, Scale(350, 30, 365, 220), wOptions, SCROLL_OPTION_SELECT);
scrOption->setLargeStep(1);
scrOption->setSmallStep(1);
scrOption->setMin(0);
//pos select
wPosSelect = env->addWindow(Scale(340, 200, 935, 410), false, gDataManager->GetSysString(561).data());
defaultStrings.emplace_back(wPosSelect, 561);
wPosSelect->getCloseButton()->setVisible(false);
wPosSelect->setVisible(false);
irr::core::dimension2di imgsize = { Scale<irr::s32>(CARD_IMG_WIDTH * 0.5f), Scale<irr::s32>(CARD_IMG_HEIGHT * 0.5f) };
btnPSAU = irr::gui::CGUIImageButton::addImageButton(env, Scale(10, 45, 150, 185), wPosSelect, BUTTON_POS_AU);
btnPSAU->setImageSize(imgsize);
btnPSAD = irr::gui::CGUIImageButton::addImageButton(env, Scale(155, 45, 295, 185), wPosSelect, BUTTON_POS_AD);
btnPSAD->setImageSize(imgsize);
btnPSAD->setImage(imageManager.tCover[0]);
btnPSDU = irr::gui::CGUIImageButton::addImageButton(env, Scale(300, 45, 440, 185), wPosSelect, BUTTON_POS_DU);
btnPSDU->setImageSize(imgsize);
btnPSDU->setImageRotation(270);
btnPSDD = irr::gui::CGUIImageButton::addImageButton(env, Scale(445, 45, 585, 185), wPosSelect, BUTTON_POS_DD);
btnPSDD->setImageSize(imgsize);
btnPSDD->setImageRotation(270);
btnPSDD->setImage(imageManager.tCover[0]);
//card select
imgsize = { Scale<irr::s32>(CARD_IMG_WIDTH * 0.6f), Scale<irr::s32>(CARD_IMG_HEIGHT * 0.6f) };
wCardSelect = env->addWindow(Scale(320, 100, 1000, 400), false, L"");
wCardSelect->getCloseButton()->setVisible(false);
wCardSelect->setVisible(false);
for(int i = 0; i < 5; ++i) {
stCardPos[i] = env->addStaticText(L"", Scale(30 + 125 * i, 30, 150 + 125 * i, 50), true, false, wCardSelect, -1, true);
stCardPos[i]->setBackgroundColor(0xffffffff);
stCardPos[i]->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
btnCardSelect[i] = irr::gui::CGUIImageButton::addImageButton(env, Scale(30 + 125 * i, 55, 150 + 125 * i, 225), wCardSelect, BUTTON_CARD_0 + i);
btnCardSelect[i]->setImageSize(imgsize);
}
scrCardList = env->addScrollBar(true, Scale(30, 235, 650, 255), wCardSelect, SCROLL_CARD_SELECT);
scrCardList->setLargeStep(scrCardList->getMax());
btnSelectOK = env->addButton(Scale(300, 265, 380, 290), wCardSelect, BUTTON_CARD_SEL_OK, gDataManager->GetSysString(1211).data());
defaultStrings.emplace_back(btnSelectOK, 1211);
//card display
wCardDisplay = env->addWindow(Scale(320, 100, 1000, 400), false, L"");
wCardDisplay->getCloseButton()->setVisible(false);
wCardDisplay->setVisible(false);
for(int i = 0; i < 5; ++i) {
stDisplayPos[i] = env->addStaticText(L"", Scale(30 + 125 * i, 30, 150 + 125 * i, 50), true, false, wCardDisplay, -1, true);
stDisplayPos[i]->setBackgroundColor(0xffffffff);
stDisplayPos[i]->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
btnCardDisplay[i] = irr::gui::CGUIImageButton::addImageButton(env, Scale(30 + 125 * i, 55, 150 + 125 * i, 225), wCardDisplay, BUTTON_DISPLAY_0 + i);
btnCardDisplay[i]->setImageSize(imgsize);
}
scrDisplayList = env->addScrollBar(true, Scale(30, 235, 650, 255), wCardDisplay, SCROLL_CARD_DISPLAY);
btnDisplayOK = env->addButton(Scale(300, 265, 380, 290), wCardDisplay, BUTTON_CARD_DISP_OK, gDataManager->GetSysString(1211).data());
defaultStrings.emplace_back(btnDisplayOK, 1211);
//announce number
wANNumber = env->addWindow(Scale(550, 200, 780, 295), false, L"");
wANNumber->getCloseButton()->setVisible(false);
wANNumber->setVisible(false);
cbANNumber = AddComboBox(env, Scale(40, 30, 190, 50), wANNumber, -1);
cbANNumber->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
btnANNumberOK = env->addButton(Scale(80, 60, 150, 85), wANNumber, BUTTON_ANNUMBER_OK, gDataManager->GetSysString(1211).data());
defaultStrings.emplace_back(btnANNumberOK, 1211);
//announce card
wANCard = env->addWindow(Scale(430, 170, 840, 370), false, L"");
wANCard->getCloseButton()->setVisible(false);
wANCard->setVisible(false);
ebANCard = env->addEditBox(L"", Scale(20, 25, 390, 45), true, wANCard, EDITBOX_ANCARD);
ebANCard->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
lstANCard = env->addListBox(Scale(20, 50, 390, 160), wANCard, LISTBOX_ANCARD, true);
btnANCardOK = env->addButton(Scale(60, 165, 350, 190), wANCard, BUTTON_ANCARD_OK, gDataManager->GetSysString(1211).data());
defaultStrings.emplace_back(btnANCardOK, 1211);
//announce attribute
wANAttribute = env->addWindow(Scale(500, 200, 830, 285), false, gDataManager->GetSysString(562).data());
defaultStrings.emplace_back(wANAttribute, 562);
wANAttribute->getCloseButton()->setVisible(false);
wANAttribute->setVisible(false);
for(int i = 0; i < 7; ++i) {
chkAttribute[i] = env->addCheckBox(false, Scale(10 + (i % 4) * 80, 25 + (i / 4) * 25, 90 + (i % 4) * 80, 50 + (i / 4) * 25),
wANAttribute, CHECK_ATTRIBUTE, gDataManager->GetSysString(1010 + i).data());
defaultStrings.emplace_back(chkAttribute[i], 1010 + i);
}
//announce race
wANRace = env->addWindow(Scale(480, 200, 850, 410), false, gDataManager->GetSysString(563).data());
defaultStrings.emplace_back(wANRace, 563);
wANRace->getCloseButton()->setVisible(false);
wANRace->setVisible(false);
{
auto tmpPanel = irr::gui::Panel::addPanel(env, wANRace, -1, wANRace->getClientRect(), true, false);
auto crPanel = tmpPanel->getSubpanel();
for(int i = 0; i < static_cast<int>(sizeofarr(chkRace)); ++i) {
auto string = gDataManager->GetRaceStringIndex(i);
chkRace[i] = env->addCheckBox(false, Scale(10 + (i % 3) * 120, (i / 3) * 25, 150 + (i % 3) * 120, 25 + (i / 3) * 25),
crPanel, CHECK_RACE, gDataManager->GetSysString(string).data());
defaultStrings.emplace_back(chkRace[i], string);
}
}
//selection hint
stHintMsg = env->addStaticText(L"", Scale(500, 60, 820, 90), true, false, 0, -1, false);
stHintMsg->setBackgroundColor(skin::DUELFIELD_TOOLTIP_TEXT_BACKGROUND_COLOR_VAL);
tmp_color = skin::DUELFIELD_TOOLTIP_TEXT_COLOR_VAL;
if(tmp_color != 0)
stHintMsg->setOverrideColor(tmp_color);
stHintMsg->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
stHintMsg->setVisible(false);
//cmd menu
wCmdMenu = env->addWindow(Scale(10, 10, 110, 179), false, L"");
wCmdMenu->setDrawTitlebar(false);
wCmdMenu->setVisible(false);
wCmdMenu->getCloseButton()->setVisible(false);
btnActivate = env->addButton(Scale(1, 1, 99, 21), wCmdMenu, BUTTON_CMD_ACTIVATE, gDataManager->GetSysString(1150).data());
defaultStrings.emplace_back(btnActivate, 1150);
btnSummon = env->addButton(Scale(1, 22, 99, 42), wCmdMenu, BUTTON_CMD_SUMMON, gDataManager->GetSysString(1151).data());
defaultStrings.emplace_back(btnSummon, 1151);
btnSPSummon = env->addButton(Scale(1, 43, 99, 63), wCmdMenu, BUTTON_CMD_SPSUMMON, gDataManager->GetSysString(1152).data());
defaultStrings.emplace_back(btnSPSummon, 1152);
btnMSet = env->addButton(Scale(1, 64, 99, 84), wCmdMenu, BUTTON_CMD_MSET, gDataManager->GetSysString(1153).data());
defaultStrings.emplace_back(btnMSet, 1153);
btnSSet = env->addButton(Scale(1, 85, 99, 105), wCmdMenu, BUTTON_CMD_SSET, gDataManager->GetSysString(1153).data());
defaultStrings.emplace_back(btnSSet, 1153);
btnRepos = env->addButton(Scale(1, 106, 99, 126), wCmdMenu, BUTTON_CMD_REPOS, gDataManager->GetSysString(1154).data());
defaultStrings.emplace_back(btnRepos, 1154);
btnAttack = env->addButton(Scale(1, 127, 99, 147), wCmdMenu, BUTTON_CMD_ATTACK, gDataManager->GetSysString(1157).data());
defaultStrings.emplace_back(btnAttack, 1157);
btnShowList = env->addButton(Scale(1, 148, 99, 168), wCmdMenu, BUTTON_CMD_SHOWLIST, gDataManager->GetSysString(1158).data());
defaultStrings.emplace_back(btnShowList, 1158);
btnOperation = env->addButton(Scale(1, 169, 99, 189), wCmdMenu, BUTTON_CMD_ACTIVATE, gDataManager->GetSysString(1161).data());
defaultStrings.emplace_back(btnOperation, 1161);
btnReset = env->addButton(Scale(1, 190, 99, 210), wCmdMenu, BUTTON_CMD_RESET, gDataManager->GetSysString(1162).data());
defaultStrings.emplace_back(btnReset, 1162);
//deck edit
wDeckEdit = AlignElementWithParent(env->addStaticText(L"", Scale(309, 8, 605, 130), true, false, 0, -1, true));
wDeckEdit->setVisible(false);
stBanlist = env->addStaticText(gDataManager->GetSysString(1300).data(), Scale(10, 9, 100, 29), false, false, wDeckEdit);
defaultStrings.emplace_back(stBanlist, 1300);
cbDBLFList = AlignElementWithParent(AddComboBox(env, Scale(80, 5, 220, 30), wDeckEdit, COMBOBOX_DBLFLIST));
cbDBLFList->setMaxSelectionRows(10);
stDeck = env->addStaticText(gDataManager->GetSysString(1301).data(), Scale(10, 39, 100, 59), false, false, wDeckEdit);
defaultStrings.emplace_back(stDeck, 1301);
cbDBDecks = AlignElementWithParent(AddComboBox(env, Scale(80, 35, 220, 60), wDeckEdit, COMBOBOX_DBDECKS));
cbDBDecks->setMaxSelectionRows(15);
btnSaveDeck = AlignElementWithParent(env->addButton(Scale(225, 35, 290, 60), wDeckEdit, BUTTON_SAVE_DECK, gDataManager->GetSysString(1302).data()));
defaultStrings.emplace_back(btnSaveDeck, 1302);
btnRenameDeck = AlignElementWithParent(env->addButton(Scale(5, 65, 75, 90), wDeckEdit, BUTTON_RENAME_DECK, gDataManager->GetSysString(1362).data()));
defaultStrings.emplace_back(btnRenameDeck, 1362);
ebDeckname = AlignElementWithParent(env->addEditBox(L"", Scale(80, 65, 220, 90), true, wDeckEdit, EDITBOX_DECK_NAME));
ebDeckname->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
btnSaveDeckAs = AlignElementWithParent(env->addButton(Scale(225, 65, 290, 90), wDeckEdit, BUTTON_SAVE_DECK_AS, gDataManager->GetSysString(1303).data()));
defaultStrings.emplace_back(btnSaveDeckAs, 1303);
btnShuffleDeck = AlignElementWithParent(env->addButton(Scale(5, 95, 75, 120), wDeckEdit, BUTTON_SHUFFLE_DECK, gDataManager->GetSysString(1307).data()));
defaultStrings.emplace_back(btnShuffleDeck, 1307);
btnSortDeck = AlignElementWithParent(env->addButton(Scale(80, 95, 145, 120), wDeckEdit, BUTTON_SORT_DECK, gDataManager->GetSysString(1305).data()));
defaultStrings.emplace_back(btnSortDeck, 1305);
btnClearDeck = AlignElementWithParent(env->addButton(Scale(155, 95, 220, 120), wDeckEdit, BUTTON_CLEAR_DECK, gDataManager->GetSysString(1304).data()));
defaultStrings.emplace_back(btnClearDeck, 1304);
btnDeleteDeck = AlignElementWithParent(env->addButton(Scale(225, 95, 290, 120), wDeckEdit, BUTTON_DELETE_DECK, gDataManager->GetSysString(1308).data()));
defaultStrings.emplace_back(btnDeleteDeck, 1308);
btnSideOK = AlignElementWithParent(env->addButton(Scale(510, 40, 820, 80), nullptr, BUTTON_SIDE_OK, gDataManager->GetSysString(1334).data()));
defaultStrings.emplace_back(btnSideOK, 1334);
btnSideOK->setVisible(false);
btnSideShuffle = AlignElementWithParent(env->addButton(Scale(310, 100, 370, 130), nullptr, BUTTON_SHUFFLE_DECK, gDataManager->GetSysString(1307).data()));
defaultStrings.emplace_back(btnSideShuffle, 1307);
btnSideShuffle->setVisible(false);
btnSideSort = AlignElementWithParent(env->addButton(Scale(375, 100, 435, 130), nullptr, BUTTON_SORT_DECK, gDataManager->GetSysString(1305).data()));
defaultStrings.emplace_back(btnSideSort, 1305);
btnSideSort->setVisible(false);
btnSideReload = AlignElementWithParent(env->addButton(Scale(440, 100, 500, 130), nullptr, BUTTON_SIDE_RELOAD, gDataManager->GetSysString(1309).data()));
defaultStrings.emplace_back(btnSideReload, 1309);
btnSideReload->setVisible(false);
btnHandTest = AlignElementWithParent(env->addButton(Scale(205, 90, 295, 130), nullptr, BUTTON_HAND_TEST, gDataManager->GetSysString(1297).data()));
defaultStrings.emplace_back(btnHandTest, 1297);
btnHandTest->setVisible(false);
btnHandTestSettings = AlignElementWithParent(env->addButton(Scale(205, 140, 295, 180), 0, BUTTON_HAND_TEST_SETTINGS, L""));
btnHandTestSettings->setVisible(false);
stHandTestSettings = AlignElementWithParent(irr::gui::CGUICustomText::addCustomText(gDataManager->GetSysString(1375).data(), false, env, btnHandTestSettings, -1, Scale(0, 0, 90, 40)));
stHandTestSettings->setWordWrap(true);
stHandTestSettings->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
defaultStrings.emplace_back(stHandTestSettings, 1375);
wHandTest = env->addWindow(Scale(mainMenuLeftX, 200, mainMenuRightX, 450), false, gDataManager->GetSysString(1297).data());
wHandTest->getCloseButton()->setVisible(false);
wHandTest->setVisible(false);
defaultStrings.emplace_back(wHandTest, 1297);
offset = 0;
auto nextHandTestRow = [&offset,this](int leftRail, int rightRail, bool increment = true) {
if(increment) offset += 35;
return Scale(leftRail, offset, rightRail, offset + 25);
};
chkHandTestNoOpponent = env->addCheckBox(false, nextHandTestRow(10, mainMenuWidth - 10), wHandTest, -1, gDataManager->GetSysString(2081).data());
defaultStrings.emplace_back(chkHandTestNoOpponent, 2081);
chkHandTestNoShuffle = env->addCheckBox(false, nextHandTestRow(10, mainMenuWidth - 10), wHandTest, -1, gDataManager->GetSysString(1230).data());
defaultStrings.emplace_back(chkHandTestNoShuffle, 1230);
tmpptr = env->addStaticText(gDataManager->GetSysString(1232).data(), nextHandTestRow(10, 90), false, false, wHandTest);
defaultStrings.emplace_back(tmpptr, 1232);
ebHandTestStartHand = env->addEditBox(L"5", nextHandTestRow(95, 175, false), true, wHandTest, EDITBOX_NUMERIC);
ebHandTestStartHand->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
tmpptr = env->addStaticText(gDataManager->GetSysString(1236).data(), nextHandTestRow(10, 90), false, false, wHandTest);
defaultStrings.emplace_back(tmpptr, 1236);
cbHandTestDuelRule = AddComboBox(env, nextHandTestRow(95, mainMenuWidth - 10, false), wHandTest);
ReloadCBDuelRule(cbHandTestDuelRule);
cbHandTestDuelRule->setSelected(4);
chkHandTestSaveReplay = env->addCheckBox(gGameConfig->saveHandTest, nextHandTestRow(10, mainMenuWidth - 10), wHandTest, CHECKBOX_SAVE_HAND_TEST_REPLAY, gDataManager->GetSysString(2077).data());
defaultStrings.emplace_back(chkHandTestSaveReplay, 2077);
tmpptr = env->addButton(nextHandTestRow(10, mainMenuWidth / 2 - 5), wHandTest, BUTTON_HAND_TEST_CANCEL, gDataManager->GetSysString(1210).data()); // cancel
defaultStrings.emplace_back(tmpptr, 1210);
tmpptr = env->addButton(nextHandTestRow(mainMenuWidth / 2 + 5, mainMenuWidth - 10, false), wHandTest, BUTTON_HAND_TEST_START, gDataManager->GetSysString(1215).data()); // start
defaultStrings.emplace_back(tmpptr, 1215);
//
btnYdkeManage = AlignElementWithParent(env->addButton(Scale(205, 190, 295, 230), 0, BUTTON_DECK_YDKE_MANAGE, gDataManager->GetSysString(2083).data()));
defaultStrings.emplace_back(btnYdkeManage, 2083);
btnYdkeManage->setVisible(false);
btnYdkeManage->setEnabled(true);
wYdkeManage = env->addWindow(Scale(mainMenuLeftX, 200, mainMenuRightX, 450), false, gDataManager->GetSysString(2084).data());
defaultStrings.emplace_back(wYdkeManage, 2084);
wYdkeManage->getCloseButton()->setVisible(false);
wYdkeManage->setVisible(false);
offset = 30;
auto nextYdkeManageRow = [&offset, &mainMenuWidth, this](bool increment = true) {
if(increment) offset += 55;
return Scale(10, offset, mainMenuWidth - 10, offset + 40);
};
tmpptr = env->addButton(nextYdkeManageRow(false), wYdkeManage, BUTTON_IMPORT_YDKE, gDataManager->GetSysString(2085).data());
defaultStrings.emplace_back(tmpptr, 2085);
tmpptr = env->addButton(nextYdkeManageRow(), wYdkeManage, BUTTON_EXPORT_YDKE, gDataManager->GetSysString(2086).data());
defaultStrings.emplace_back(tmpptr, 2086);
tmpptr = env->addButton(nextYdkeManageRow(), wYdkeManage, BUTTON_EXPORT_DECK_PLAINTEXT, gDataManager->GetSysString(2087).data());
defaultStrings.emplace_back(tmpptr, 2087);
tmpptr = env->addButton(nextYdkeManageRow(), wYdkeManage, BUTTON_CLOSE_YDKE_WINDOW, gDataManager->GetSysString(1210).data());
defaultStrings.emplace_back(tmpptr, 1210);
//
scrFilter = AlignElementWithParent(env->addScrollBar(false, Scale(999, 161, 1019, 629), 0, SCROLL_FILTER));
scrFilter->setLargeStep(DECK_SEARCH_SCROLL_STEP);
scrFilter->setSmallStep(DECK_SEARCH_SCROLL_STEP);
scrFilter->setVisible(false);
//sort type
wSort = AlignElementWithParent(env->addStaticText(L"", Scale(930, 132, 1020, 156), true, false, 0, -1, true));
cbSortType = AlignElementWithParent(AddComboBox(env, Scale(10, 2, 85, 22), wSort, COMBOBOX_SORTTYPE));
cbSortType->setMaxSelectionRows(10);
ReloadCBSortType();
wSort->setVisible(false);
//filters
wFilter = AlignElementWithParent(env->addStaticText(L"", Scale(610, 8, 1020, 130), true, false, 0, -1, true));
wFilter->setVisible(false);
stCategory = env->addStaticText(gDataManager->GetSysString(1311).data(), Scale(10, 5, 70, 25), false, false, wFilter);
defaultStrings.emplace_back(stCategory, 1311);
cbCardType = AlignElementWithParent(AddComboBox(env, Scale(60, 3, 120, 23), wFilter, COMBOBOX_MAINTYPE));
ReloadCBCardType();
cbCardType2 = AlignElementWithParent(AddComboBox(env, Scale(130, 3, 190, 23), wFilter, COMBOBOX_SECONDTYPE));
cbCardType2->setMaxSelectionRows(20);
cbCardType2->addItem(gDataManager->GetSysString(1310).data(), 0);
chkAnime = AlignElementWithParent(env->addCheckBox(gGameConfig->chkAnime, Scale(10, 96, 150, 118), wFilter, CHECKBOX_SHOW_ANIME, gDataManager->GetSysString(1999).data()));
defaultStrings.emplace_back(chkAnime, 1999);
stLimit = env->addStaticText(gDataManager->GetSysString(1315).data(), Scale(205, 5, 280, 25), false, false, wFilter);
defaultStrings.emplace_back(stLimit, 1315);
cbLimit = AlignElementWithParent(AddComboBox(env, Scale(260, 3, 390, 23), wFilter, COMBOBOX_OTHER_FILT));
cbLimit->setMaxSelectionRows(10);
ReloadCBLimit();
stAttribute = env->addStaticText(gDataManager->GetSysString(1319).data(), Scale(10, 28, 70, 48), false, false, wFilter);
defaultStrings.emplace_back(stAttribute, 1319);
cbAttribute = AlignElementWithParent(AddComboBox(env, Scale(60, 26, 190, 46), wFilter, COMBOBOX_OTHER_FILT));
cbAttribute->setMaxSelectionRows(10);
ReloadCBAttribute();
stRace = env->addStaticText(gDataManager->GetSysString(1321).data(), Scale(10, 51, 70, 71), false, false, wFilter);
defaultStrings.emplace_back(stRace, 1321);
cbRace = AlignElementWithParent(AddComboBox(env, Scale(60, 49, 190, 69), wFilter, COMBOBOX_OTHER_FILT));
cbRace->setMaxSelectionRows(10);
ReloadCBRace();
stAttack = env->addStaticText(gDataManager->GetSysString(1322).data(), Scale(205, 28, 280, 48), false, false, wFilter);
defaultStrings.emplace_back(stAttack, 1322);
ebAttack = AlignElementWithParent(env->addEditBox(L"", Scale(260, 26, 340, 46), true, wFilter, EDITBOX_ATTACK));
ebAttack->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
stDefense = env->addStaticText(gDataManager->GetSysString(1323).data(), Scale(205, 51, 280, 71), false, false, wFilter);
defaultStrings.emplace_back(stDefense, 1323);
ebDefense = AlignElementWithParent(env->addEditBox(L"", Scale(260, 49, 340, 69), true, wFilter, EDITBOX_DEFENSE));
ebDefense->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
stStar = env->addStaticText(gDataManager->GetSysString(1324).data(), Scale(10, 74, 80, 94), false, false, wFilter);
defaultStrings.emplace_back(stStar, 1324);
ebStar = AlignElementWithParent(env->addEditBox(L"", Scale(60, 72, 100, 92), true, wFilter, EDITBOX_STAR));
ebStar->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
stScale = env->addStaticText(gDataManager->GetSysString(1336).data(), Scale(110, 74, 150, 94), false, false, wFilter);
defaultStrings.emplace_back(stScale, 1336);
ebScale = AlignElementWithParent(env->addEditBox(L"", Scale(150, 72, 190, 92), true, wFilter, EDITBOX_SCALE));
ebScale->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
stSearch = env->addStaticText(gDataManager->GetSysString(1325).data(), Scale(205, 74, 280, 94), false, false, wFilter);
defaultStrings.emplace_back(stSearch, 1325);
ebCardName = AlignElementWithParent(env->addEditBox(L"", Scale(260, 72, 390, 92), true, wFilter, EDITBOX_KEYWORD));
ebCardName->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
btnEffectFilter = AlignElementWithParent(env->addButton(Scale(345, 28, 390, 69), wFilter, BUTTON_EFFECT_FILTER, gDataManager->GetSysString(1326).data()));
defaultStrings.emplace_back(btnEffectFilter, 1326);
btnStartFilter = AlignElementWithParent(env->addButton(Scale(327, 96, 390, 118), wFilter, BUTTON_START_FILTER, gDataManager->GetSysString(1327).data()));
defaultStrings.emplace_back(btnStartFilter, 1327);
btnClearFilter = AlignElementWithParent(env->addButton(Scale(260, 96, 322, 118), wFilter, BUTTON_CLEAR_FILTER, gDataManager->GetSysString(1304).data()));
defaultStrings.emplace_back(btnClearFilter, 1304);
wCategories = env->addWindow(Scale(450, 60, 1000, 270), false, L"");
wCategories->getCloseButton()->setVisible(false);
wCategories->setDrawTitlebar(false);
wCategories->setDraggable(false);
wCategories->setVisible(false);
btnCategoryOK = env->addButton(Scale(200, 175, 300, 200), wCategories, BUTTON_CATEGORY_OK, gDataManager->GetSysString(1211).data());
defaultStrings.emplace_back(btnCategoryOK, 1211);
for(int i = 0; i < 32; ++i) {
chkCategory[i] = env->addCheckBox(false, Scale(10 + (i % 4) * 130, 10 + (i / 4) * 20, 140 + (i % 4) * 130, 30 + (i / 4) * 20), wCategories, -1, gDataManager->GetSysString(1100 + i).data());
defaultStrings.emplace_back(chkCategory[i], 1100 + i);
}
btnMarksFilter = AlignElementWithParent(env->addButton(Scale(155, 96, 240, 118), wFilter, BUTTON_MARKS_FILTER, gDataManager->GetSysString(1374).data()));
defaultStrings.emplace_back(btnMarksFilter, 1374);
wLinkMarks = env->addWindow(Scale(700, 30, 820, 150), false, L"");
wLinkMarks->getCloseButton()->setVisible(false);
wLinkMarks->setDrawTitlebar(false);
wLinkMarks->setDraggable(false);
wLinkMarks->setVisible(false);
btnMarksOK = env->addButton(Scale(45, 45, 75, 75), wLinkMarks, BUTTON_MARKERS_OK, gDataManager->GetSysString(1211).data());
defaultStrings.emplace_back(btnMarksOK, 1211);
btnMark[0] = env->addButton(Scale(10, 10, 40, 40), wLinkMarks, -1, L"\u2196");
btnMark[1] = env->addButton(Scale(45, 10, 75, 40), wLinkMarks, -1, L"\u2191");
btnMark[2] = env->addButton(Scale(80, 10, 110, 40), wLinkMarks, -1, L"\u2197");
btnMark[3] = env->addButton(Scale(10, 45, 40, 75), wLinkMarks, -1, L"\u2190");
btnMark[4] = env->addButton(Scale(80, 45, 110, 75), wLinkMarks, -1, L"\u2192");
btnMark[5] = env->addButton(Scale(10, 80, 40, 110), wLinkMarks, -1, L"\u2199");
btnMark[6] = env->addButton(Scale(45, 80, 75, 110), wLinkMarks, -1, L"\u2193");
btnMark[7] = env->addButton(Scale(80, 80, 110, 110), wLinkMarks, -1, L"\u2198");
for(int i=0;i<8;i++)
btnMark[i]->setIsPushButton(true);
//replay window
wReplay = env->addWindow(Scale(220, 100, 800, 520), false, gDataManager->GetSysString(1202).data());
defaultStrings.emplace_back(wReplay, 1202);
wReplay->getCloseButton()->setVisible(false);
wReplay->setVisible(false);
lstReplayList = irr::gui::CGUIFileSelectListBox::addFileSelectListBox(env, wReplay, LISTBOX_REPLAY_LIST, Scale(10, 30, 350, 400), true, true, false);
lstReplayList->setWorkingPath(Utils::ToUnicodeIfNeeded(Replay::GetReplayFolder()), true);
lstReplayList->addFilteredExtensions({L"yrp", L"yrpx"});
lstReplayList->setItemHeight(Scale(18));
btnLoadReplay = env->addButton(Scale(470, 355, 570, 380), wReplay, BUTTON_LOAD_REPLAY, gDataManager->GetSysString(1348).data());
defaultStrings.emplace_back(btnLoadReplay, 1348);
btnLoadReplay->setEnabled(false);
btnDeleteReplay = env->addButton(Scale(360, 355, 460, 380), wReplay, BUTTON_DELETE_REPLAY, gDataManager->GetSysString(1361).data());
defaultStrings.emplace_back(btnDeleteReplay, 1361);
btnDeleteReplay->setEnabled(false);
btnRenameReplay = env->addButton(Scale(360, 385, 460, 410), wReplay, BUTTON_RENAME_REPLAY, gDataManager->GetSysString(1362).data());
defaultStrings.emplace_back(btnRenameReplay, 1362);
btnRenameReplay->setEnabled(false);
btnReplayCancel = env->addButton(Scale(470, 385, 570, 410), wReplay, BUTTON_CANCEL_REPLAY, gDataManager->GetSysString(1347).data());
defaultStrings.emplace_back(btnReplayCancel, 1347);
tmpptr = env->addStaticText(gDataManager->GetSysString(1349).data(), Scale(360, 30, 570, 50), false, true, wReplay);
defaultStrings.emplace_back(tmpptr, 1349);
stReplayInfo = irr::gui::CGUICustomText::addCustomText(L"", false, env, wReplay, -1, Scale(360, 60, 570, 350));
stReplayInfo->setWordWrap(true);
btnExportDeck = env->addButton(Scale(470, 325, 570, 350), wReplay, BUTTON_EXPORT_DECK, gDataManager->GetSysString(1358).data());
defaultStrings.emplace_back(btnExportDeck, 1358);
btnExportDeck->setEnabled(false);
btnShareReplay = env->addButton(Scale(360, 325, 460, 350), wReplay, BUTTON_SHARE_REPLAY, gDataManager->GetSysString(1378).data());
defaultStrings.emplace_back(btnShareReplay, 1378);
btnShareReplay->setEnabled(false);
#if !EDOPRO_ANDROID
btnShareReplay->setVisible(false);
#endif
chkYrp = env->addCheckBox(false, Scale(360, 250, 560, 270), wReplay, -1, gDataManager->GetSysString(1356).data());
defaultStrings.emplace_back(chkYrp, 1356);
tmpptr = env->addStaticText(gDataManager->GetSysString(1353).data(), Scale(360, 275, 570, 295), false, true, wReplay);
defaultStrings.emplace_back(tmpptr, 1353);
ebRepStartTurn = env->addEditBox(L"", Scale(360, 300, 460, 320), true, wReplay, -1);
ebRepStartTurn->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
//single play window
wSinglePlay = env->addWindow(Scale(220, 100, 800, 520), false, gDataManager->GetSysString(1201).data());
defaultStrings.emplace_back(wSinglePlay, 1201);
wSinglePlay->getCloseButton()->setVisible(false);
wSinglePlay->setVisible(false);
lstSinglePlayList = irr::gui::CGUIFileSelectListBox::addFileSelectListBox(env, wSinglePlay, LISTBOX_SINGLEPLAY_LIST, Scale(10, 30, 350, 400), true, true, false);
lstSinglePlayList->setItemHeight(Scale(18));
lstSinglePlayList->setWorkingPath(L"./puzzles", true);
lstSinglePlayList->addFilteredExtensions({L"lua"});
btnLoadSinglePlay = env->addButton(Scale(470, 355, 570, 380), wSinglePlay, BUTTON_LOAD_SINGLEPLAY, gDataManager->GetSysString(1357).data());
defaultStrings.emplace_back(btnLoadSinglePlay, 1357);
btnLoadSinglePlay->setEnabled(false);
btnOpenSinglePlay = env->addButton(Scale(470, 325, 570, 350), wSinglePlay, BUTTON_OPEN_SINGLEPLAY, gDataManager->GetSysString(1377).data());
defaultStrings.emplace_back(btnOpenSinglePlay, 1377);
btnOpenSinglePlay->setEnabled(false);
btnShareSinglePlay = env->addButton(Scale(360, 325, 460, 350), wSinglePlay, BUTTON_SHARE_SINGLEPLAY, gDataManager->GetSysString(1378).data());
defaultStrings.emplace_back(btnShareSinglePlay, 1378);
btnShareSinglePlay->setEnabled(false);
#if !EDOPRO_ANDROID
btnShareSinglePlay->setVisible(false);
#endif
btnDeleteSinglePlay = env->addButton(Scale(360, 355, 460, 380), wSinglePlay, BUTTON_DELETE_SINGLEPLAY, gDataManager->GetSysString(1361).data());
defaultStrings.emplace_back(btnDeleteSinglePlay, 1361);
btnDeleteSinglePlay->setEnabled(false);
btnRenameSinglePlay = env->addButton(Scale(360, 385, 460, 410), wSinglePlay, BUTTON_RENAME_SINGLEPLAY, gDataManager->GetSysString(1362).data());
defaultStrings.emplace_back(btnRenameSinglePlay, 1362);
btnRenameSinglePlay->setEnabled(false);
btnSinglePlayCancel = env->addButton(Scale(470, 385, 570, 410), wSinglePlay, BUTTON_CANCEL_SINGLEPLAY, gDataManager->GetSysString(1210).data());
defaultStrings.emplace_back(btnSinglePlayCancel, 1210);
tmpptr = env->addStaticText(gDataManager->GetSysString(1352).data(), Scale(360, 30, 570, 50), false, true, wSinglePlay);
defaultStrings.emplace_back(tmpptr, 1352);
stSinglePlayInfo = irr::gui::CGUICustomText::addCustomText(L"", false, env, wSinglePlay, -1, Scale(350, 60, 570, 320));
((irr::gui::CGUICustomText*)stSinglePlayInfo)->enableScrollBar();
stSinglePlayInfo->setWordWrap(true);
//replay save
wFileSave = env->addWindow(Scale(510, 200, 820, 320), false, gDataManager->GetSysString(1340).data());
defaultStrings.emplace_back(wFileSave, 1340);
wFileSave->getCloseButton()->setVisible(false);
wFileSave->setVisible(false);
stFileSaveHint = env->addStaticText(gDataManager->GetSysString(1342).data(), Scale(20, 25, 290, 45), false, false, wFileSave);
ebFileSaveName = env->addEditBox(L"", Scale(20, 50, 290, 70), true, wFileSave, EDITBOX_FILE_NAME);
ebFileSaveName->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
btnFileSaveYes = env->addButton(Scale(70, 80, 140, 105), wFileSave, BUTTON_FILE_SAVE, gDataManager->GetSysString(1341).data());
defaultStrings.emplace_back(btnFileSaveYes, 1341);
btnFileSaveNo = env->addButton(Scale(170, 80, 240, 105), wFileSave, BUTTON_FILE_CANCEL, gDataManager->GetSysString(1212).data());
defaultStrings.emplace_back(btnFileSaveNo, 1212);
//replay control
wReplayControl = AlignElementWithParent(env->addStaticText(L"", Scale(205, 143, 295, 273), true, false, 0, -1, true));
wReplayControl->setVisible(false);
btnReplayStart = AlignElementWithParent(env->addButton(Scale(5, 5, 85, 25), wReplayControl, BUTTON_REPLAY_START, gDataManager->GetSysString(1343).data()));
defaultStrings.emplace_back(btnReplayStart, 1343);
btnReplayPause = AlignElementWithParent(env->addButton(Scale(5, 5, 85, 25), wReplayControl, BUTTON_REPLAY_PAUSE, gDataManager->GetSysString(1344).data()));
defaultStrings.emplace_back(btnReplayPause, 1344);
btnReplayStep = AlignElementWithParent(env->addButton(Scale(5, 55, 85, 75), wReplayControl, BUTTON_REPLAY_STEP, gDataManager->GetSysString(1345).data()));
defaultStrings.emplace_back(btnReplayStep, 1345);
btnReplayUndo = AlignElementWithParent(env->addButton(Scale(5, 80, 85, 100), wReplayControl, BUTTON_REPLAY_UNDO, gDataManager->GetSysString(1360).data()));
defaultStrings.emplace_back(btnReplayUndo, 1360);
btnReplaySwap = AlignElementWithParent(env->addButton(Scale(5, 30, 85, 50), wReplayControl, BUTTON_REPLAY_SWAP, gDataManager->GetSysString(1346).data()));
defaultStrings.emplace_back(btnReplaySwap, 1346);
btnReplayExit = AlignElementWithParent(env->addButton(Scale(5, 105, 85, 125), wReplayControl, BUTTON_REPLAY_EXIT, gDataManager->GetSysString(1347).data()));
defaultStrings.emplace_back(btnReplayExit, 1347);
//chat
wChat = AlignElementWithParent(env->addWindow(Scale(305, 615, 1020, 640), false, L""));
wChat->getCloseButton()->setVisible(false);
wChat->setDraggable(false);
wChat->setDrawTitlebar(false);
wChat->setVisible(false);
ebChatInput = env->addEditBox(L"", Scale(3, 2, 710, 22), true, wChat, EDITBOX_CHAT);
ebChatInput->setAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
//swap
btnSpectatorSwap = AlignElementWithParent(env->addButton(Scale(205, 100, 295, 135), 0, BUTTON_REPLAY_SWAP, gDataManager->GetSysString(1346).data()));
defaultStrings.emplace_back(btnSpectatorSwap, 1346);
btnSpectatorSwap->setVisible(false);
//chain buttons
btnChainIgnore = AlignElementWithParent(env->addButton(Scale(205, 100, 295, 135), 0, BUTTON_CHAIN_IGNORE, gDataManager->GetSysString(1292).data()));
defaultStrings.emplace_back(btnChainIgnore, 1292);
btnChainAlways = AlignElementWithParent(env->addButton(Scale(205, 140, 295, 175), 0, BUTTON_CHAIN_ALWAYS, gDataManager->GetSysString(1293).data()));
defaultStrings.emplace_back(btnChainAlways, 1293);
btnChainWhenAvail = AlignElementWithParent(env->addButton(Scale(205, 180, 295, 215), 0, BUTTON_CHAIN_WHENAVAIL, gDataManager->GetSysString(1294).data()));
defaultStrings.emplace_back(btnChainWhenAvail, 1294);
btnChainIgnore->setIsPushButton(true);
btnChainAlways->setIsPushButton(true);
btnChainWhenAvail->setIsPushButton(true);
btnChainIgnore->setVisible(false);
btnChainAlways->setVisible(false);
btnChainWhenAvail->setVisible(false);
//shuffle
btnShuffle = AlignElementWithParent(env->addButton(Scale(0, 0, 50, 20), wPhase, BUTTON_CMD_SHUFFLE, gDataManager->GetSysString(1307).data()));
defaultStrings.emplace_back(btnShuffle, 1307);
btnShuffle->setVisible(false);
//cancel or finish
btnCancelOrFinish = AlignElementWithParent(env->addButton(Scale(205, 230, 295, 265), 0, BUTTON_CANCEL_OR_FINISH, gDataManager->GetSysString(1295).data()));
defaultStrings.emplace_back(btnCancelOrFinish, 1295);
btnCancelOrFinish->setVisible(false);
//leave/surrender/exit
btnLeaveGame = AlignElementWithParent(env->addButton(Scale(205, 5, 295, 80), 0, BUTTON_LEAVE_GAME, L""));
btnLeaveGame->setVisible(false);
//restart single
btnRestartSingle = AlignElementWithParent(env->addButton(Scale(205, 50, 295, 90), 0, BUTTON_RESTART_SINGLE, gDataManager->GetSysString(1366).data()));
defaultStrings.emplace_back(btnRestartSingle, 1366);
btnRestartSingle->setVisible(false);
//tip
stTip = env->addStaticText(L"", Scale(0, 0, 150, 150), false, true, 0, -1, true);
stTip->setBackgroundColor(skin::DUELFIELD_TOOLTIP_TEXT_BACKGROUND_COLOR_VAL);
tmp_color = skin::DUELFIELD_TOOLTIP_TEXT_COLOR_VAL;
if(tmp_color != 0)
stTip->setOverrideColor(tmp_color);
stTip->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
stTip->setVisible(false);
//tip for cards in select / display list
stCardListTip = env->addStaticText(L"", Scale(0, 0, 150, 150), false, true, wCardSelect, TEXT_CARD_LIST_TIP, true);
stCardListTip->setBackgroundColor(skin::DUELFIELD_TOOLTIP_TEXT_BACKGROUND_COLOR_VAL);
if(tmp_color != 0)
stCardListTip->setOverrideColor(tmp_color);
stCardListTip->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
stCardListTip->setVisible(false);
device->setEventReceiver(&menuHandler);
if(!gSoundManager->IsUsable()) {
tabSettings.DisableAudio();
gSettings.DisableAudio();
}
//server lobby
wRoomListPlaceholder = env->addStaticText(L"", Scale(1, 1, 1024 - 1, 640), false, false, 0, -1, false);
//wRoomListPlaceholder->setAlignment(EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
wRoomListPlaceholder->setVisible(false);
auto roomlistcolor = skin::ROOMLIST_TEXTS_COLOR_VAL;
//server choice dropdownlist
irr::gui::IGUIStaticText* statictext = env->addStaticText(gDataManager->GetSysString(2041).data(), Scale(10, 30, 110, 50), false, false, wRoomListPlaceholder, -1, false); // 2041 = Server:
defaultStrings.emplace_back(statictext, 2041);
statictext->setOverrideColor(roomlistcolor);
serverChoice = AddComboBox(env, Scale(90, 25, 385, 50), wRoomListPlaceholder, SERVER_CHOICE);
//online nickname
statictext = env->addStaticText(gDataManager->GetSysString(1220).data(), Scale(10, 60, 110, 80), false, false, wRoomListPlaceholder, -1, false); // 1220 = Nickname:
defaultStrings.emplace_back(statictext, 1220);
statictext->setOverrideColor(roomlistcolor);
ebNickNameOnline = env->addEditBox(gGameConfig->nickname.data(), Scale(90, 55, 275, 80), true, wRoomListPlaceholder, EDITBOX_NICKNAME);
//top right host online game button
btnCreateHost2 = env->addButton(Scale(904, 25, 1014, 50), wRoomListPlaceholder, BUTTON_CREATE_HOST2, gDataManager->GetSysString(1224).data());
defaultStrings.emplace_back(btnCreateHost2, 1224);
btnCreateHost2->setAlignment(irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
//filter dropdowns
cbFilterRule = AddComboBox(env, Scale(392, 25, 532, 50), wRoomListPlaceholder, CB_FILTER_ALLOWED_CARDS);
//cbFilterMatchMode = AddComboBox(env, Scale(392, 55, 532, 80), wRoomListPlaceholder, CB_FILTER_MATCH_MODE);
cbFilterBanlist = AddComboBox(env, Scale(392, 85, 532, 110), wRoomListPlaceholder, CB_FILTER_BANLIST);
cbFilterRule->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
//cbFilterMatchMode->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
cbFilterBanlist->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
RefreshLFLists();
ReloadCBFilterRule();
/*cbFilterMatchMode->addItem(epro::format(L"[{}]", gDataManager->GetSysString(1227)).data());
cbFilterMatchMode->addItem(gDataManager->GetSysString(1244).data());
cbFilterMatchMode->addItem(gDataManager->GetSysString(1245).data());
cbFilterMatchMode->addItem(gDataManager->GetSysString(1246).data());*/
//Scale(392, 55, 532, 80)
ebOnlineTeam1 = env->addEditBox(L"0", Scale(140 + (392 - 140), 55, 170 + (392 - 140), 80), true, wRoomListPlaceholder, EDITBOX_TEAM_COUNT);
ebOnlineTeam1->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
ebOnlineTeam1->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
stVersus = env->addStaticText(gDataManager->GetSysString(1380).data(), Scale(175 + (392 - 140), 55, 195 + (392 - 140), 80), true, false, wRoomListPlaceholder);
defaultStrings.emplace_back(stVersus, 1380);
stVersus->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
stVersus->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
stVersus->setOverrideColor(roomlistcolor);
ebOnlineTeam2 = env->addEditBox(L"0", Scale(200 + (392 - 140), 55, 230 + (392 - 140), 80), true, wRoomListPlaceholder, EDITBOX_TEAM_COUNT);
ebOnlineTeam2->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
ebOnlineTeam2->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
stBestof = env->addStaticText(gDataManager->GetSysString(1381).data(), Scale(235 + (392 - 140), 55, 280 + (392 - 140), 80), true, false, wRoomListPlaceholder);
defaultStrings.emplace_back(stBestof, 1381);
stBestof->setTextAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_CENTER);
stBestof->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
stBestof->setOverrideColor(roomlistcolor);
ebOnlineBestOf = env->addEditBox(L"0", Scale(285 + (392 - 140), 55, 315 + (392 - 140), 80), true, wRoomListPlaceholder, EDITBOX_NUMERIC);
ebOnlineBestOf->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
ebOnlineBestOf->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
btnFilterRelayMode = env->addButton(Scale(325 + (392 - 140), 55, 370 + (392 - 140), 80), wRoomListPlaceholder, BUTTON_FILTER_RELAY, gDataManager->GetSysString(1247).data());
defaultStrings.emplace_back(btnFilterRelayMode, 1247);
btnFilterRelayMode->setIsPushButton(true);
btnFilterRelayMode->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
//filter rooms textbox
ebRoomNameText = env->addStaticText(gDataManager->GetSysString(2021).data(), Scale(572, 30, 682, 50), false, false, wRoomListPlaceholder); //2021 = Filter:
defaultStrings.emplace_back(ebRoomNameText, 2021);
ebRoomNameText->setOverrideColor(roomlistcolor);
ebRoomName = env->addEditBox(L"", Scale(642, 25, 782, 50), true, wRoomListPlaceholder, EDIT_ONLINE_ROOM_NAME); //filter textbox
ebRoomName->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
ebRoomNameText->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
ebRoomName->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
//show locked rooms checkbox
chkShowPassword = irr::gui::CGUICustomCheckBox::addCustomCheckBox(false, env, Scale(642, 55, 1024, 80), wRoomListPlaceholder, CHECK_SHOW_LOCKED_ROOMS, gDataManager->GetSysString(1994).data());
defaultStrings.emplace_back(chkShowPassword, 1994);
((irr::gui::CGUICustomCheckBox*)chkShowPassword)->setColor(roomlistcolor);
chkShowPassword->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
//show active rooms checkbox
chkShowActiveRooms = irr::gui::CGUICustomCheckBox::addCustomCheckBox(false, env, Scale(642, 85, 1024, 110), wRoomListPlaceholder, CHECK_SHOW_ACTIVE_ROOMS, gDataManager->GetSysString(1985).data());
defaultStrings.emplace_back(chkShowActiveRooms, 1985);
((irr::gui::CGUICustomCheckBox*)chkShowActiveRooms)->setColor(roomlistcolor);
chkShowActiveRooms->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT);
//show all rooms in a table
roomListTable = irr::gui::CGUICustomTable::addCustomTable(env, Resize(1, 118, 1022, 550), wRoomListPlaceholder, TABLE_ROOMLIST, true);
roomListTable->setResizableColumns(true);
//roomListTable->setAlignment(EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
roomListTable->addColumn(L" ");//lock
roomListTable->addColumn(gDataManager->GetSysString(1225).data());//Allowed Cards:
roomListTable->addColumn(gDataManager->GetSysString(1227).data());//Duel Mode:
roomListTable->addColumn(gDataManager->GetSysString(1236).data());//master rule
roomListTable->addColumn(gDataManager->GetSysString(1226).data());//Forbidden List:
roomListTable->addColumn(gDataManager->GetSysString(2030).data());//Players:
roomListTable->addColumn(gDataManager->GetSysString(2024).data());//Notes:
roomListTable->addColumn(gDataManager->GetSysString(1988).data());//Status
roomListTable->setColumnWidth(0, Scale(30)); // lock
roomListTable->setColumnWidth(1, Scale(110)); // Allowed Cards:
roomListTable->setColumnWidth(2, Scale(150)); // Duel Mode:
roomListTable->setColumnWidth(3, Scale(50)); // Master Rule
roomListTable->setColumnWidth(4, Scale(130)); // Forbidden List:
roomListTable->setColumnWidth(5, Scale(115)); // Players:
roomListTable->setColumnWidth(6, Scale(355)); // Notes:
roomListTable->setColumnWidth(7, Scale(60)); // Status
roomListTable->setColumnOrdering(0, irr::gui::EGCO_FLIP_ASCENDING_DESCENDING);
roomListTable->setColumnOrdering(1, irr::gui::EGCO_FLIP_ASCENDING_DESCENDING);
roomListTable->setColumnOrdering(2, irr::gui::EGCO_FLIP_ASCENDING_DESCENDING);
roomListTable->setColumnOrdering(3, irr::gui::EGCO_FLIP_ASCENDING_DESCENDING);
roomListTable->setColumnOrdering(4, irr::gui::EGCO_FLIP_ASCENDING_DESCENDING);
roomListTable->setColumnOrdering(5, irr::gui::EGCO_FLIP_ASCENDING_DESCENDING);
roomListTable->setColumnOrdering(6, irr::gui::EGCO_FLIP_ASCENDING_DESCENDING);
roomListTable->setColumnOrdering(7, irr::gui::EGCO_FLIP_ASCENDING_DESCENDING);
//refresh button center bottom
btnLanRefresh2 = env->addButton(Scale(462, 640 - 10 - 25 - 25 - 5, 562, 640 - 10 - 25 - 5), wRoomListPlaceholder, BUTTON_LAN_REFRESH2, gDataManager->GetSysString(1217).data());
defaultStrings.emplace_back(btnLanRefresh2, 1217);
btnLanRefresh2->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT);
//server room password
wRoomPassword = env->addWindow(Scale(357, 200, 667, 320), false, L"");
wRoomPassword->getCloseButton()->setVisible(false);
wRoomPassword->setVisible(false);
wRoomPassword->setAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
tmpptr = env->addStaticText(gDataManager->GetSysString(2038).data(), Scale(20, 25, 290, 45), false, false, wRoomPassword);
defaultStrings.emplace_back(tmpptr, 2038);
ebRPName = env->addEditBox(L"", Scale(20, 50, 290, 70), true, wRoomPassword, -1);
ebRPName->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
btnRPYes = env->addButton(Scale(70, 80, 140, 105), wRoomPassword, BUTTON_ROOMPASSWORD_OK, gDataManager->GetSysString(1211).data());
defaultStrings.emplace_back(btnRPYes, 1211);
btnRPNo = env->addButton(Scale(170, 80, 240, 105), wRoomPassword, BUTTON_ROOMPASSWORD_CANCEL, gDataManager->GetSysString(1212).data());
defaultStrings.emplace_back(btnRPNo, 1212);
//join cancel buttons
btnJoinHost2 = env->addButton(Scale(1024 - 10 - 110, 640 - 20 - 25 - 25 - 5, 1024 - 10, 640 - 20 - 25 - 5), wRoomListPlaceholder, BUTTON_JOIN_HOST2, gDataManager->GetSysString(1223).data());
defaultStrings.emplace_back(btnJoinHost2, 1223);
btnJoinCancel2 = env->addButton(Scale(1024 - 10 - 110, 640 - 20 - 25, 1024 - 10, 640 - 20), wRoomListPlaceholder, BUTTON_JOIN_CANCEL2, gDataManager->GetSysString(1212).data());
defaultStrings.emplace_back(btnJoinCancel2, 1212);
btnJoinHost2->setAlignment(irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT);
btnJoinCancel2->setAlignment(irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT);
//load server(s)
LoadServers();
fpsCounter = env->addStaticText(L"", Scale(950, 620, 1024, 640), false, false);
fpsCounter->setOverrideColor(skin::FPS_TEXT_COLOR_VAL);
fpsCounter->setVisible(gGameConfig->showFPS);
fpsCounter->setTextRestrainedInside(false);
#if EDOPRO_ANDROID || EDOPRO_IOS
fpsCounter->setTextAlignment(irr::gui::EGUIA_LOWERRIGHT, irr::gui::EGUIA_LOWERRIGHT);
#else
fpsCounter->setTextAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
#endif
//update window
updateWindow = env->addWindow(Scale(490, 200, 840, 340), true, L"");
updateWindow->getCloseButton()->setVisible(false);
updateWindow->setVisible(false);
updateWindow->setDrawTitlebar(false);
updateProgressText = env->addStaticText(L"", Scale(5, 5, 345, 90), false, true, updateWindow);
updateProgressTop = new IProgressBar(env, Scale(5, 60, 335, 85), -1, updateWindow);
updateProgressTop->addBorder(1);
updateProgressTop->setProgress(0);
updateProgressTop->setVisible(false);
updateProgressTop->drop();
updateSubprogressText = env->addStaticText(L"", Scale(5, 90, 345, 110), false, true, updateWindow);
updateProgressBottom = new IProgressBar(env, Scale(5, 115, 335, 130), -1, updateWindow);
updateProgressBottom->addBorder(1);
updateProgressBottom->setProgress(0);
updateProgressBottom->drop();
Utils::CreateResourceFolders();
LoadGithubRepositories();
LoadCore();
RefreshUICoreVersion();
ApplySkin(EPRO_TEXT(""), true);
auto selectedLocale = gSettings.cbCurrentLocale->getSelected();
if(selectedLocale != 0)
ApplyLocale(selectedLocale, true);
env->getRootGUIElement()->bringToFront(wBtnSettings);
env->getRootGUIElement()->bringToFront(mTopMenu);
env->setFocus(wMainMenu);
}
void Game::LoadCore() {
ocgcore = Core::LoadBundled();
#ifdef YGOPRO_BUILD_DLL
corename = L"Bundled";
if(auto newcore = Core::Load(Utils::GetWorkingDirectory(), true); newcore) {
ocgcore = std::move(newcore);
corename = L"./";
} else {
const auto path = epro::format(EPRO_TEXT("{}/expansions/"), Utils::GetWorkingDirectory());
if(newcore = Core::Load(path, true); newcore) {
ocgcore = std::move(newcore);
corename = L"./expansions/";
}
}
if(gRepoManager->IsReadOnly())
LoadCoreFromRepos();
#endif
}
#ifdef YGOPRO_BUILD_DLL
void Game::LoadCoreFromRepos() {
if(cores_to_load.empty() || gRepoManager->GetUpdatingReposNumber() > 0)
return;
for(auto& path : cores_to_load) {
if(auto ncore = Core::Load(Utils::GetWorkingDirectory() + path, true); ncore) {
ocgcore = std::move(ncore);
corename = Utils::ToUnicodeIfNeeded(path);
RefreshUICoreVersion();
break;
}
}
cores_to_load.clear();
}
#endif
static constexpr std::pair<epro::wstringview, irr::video::E_DRIVER_TYPE> supported_graphic_drivers[]{
{ L"Default"sv, irr::video::EDT_COUNT},
#if !EDOPRO_ANDROID && !EDOPRO_IOS
{ L"OpenGL"sv, irr::video::EDT_OPENGL },
#endif
#if EDOPRO_WINDOWS
{ L"Direct3D 9"sv, irr::video::EDT_DIRECT3D9},
#if (IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR==9)
{ L"Direct3D 9on12"sv, irr::video::EDT_DIRECT3D9_ON_12},
#endif
#endif
#if !EDOPRO_MACOS && (IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR==9)
{ L"OpenGL ES 1"sv, irr::video::EDT_OGLES1 },
{ L"OpenGL ES 2"sv, irr::video::EDT_OGLES2 },
#endif
};
void Game::PopulateGameHostWindows() {
//create host
wtcCreateHost = irr::gui::CGUIWindowedTabControl::addCGUIWindowedTabControl(env, Scale(320, 100, 700, 530), gDataManager->GetSysString(1224).data(), TAB_CONTROL_CREATE_HOST);
wCreateHost = wtcCreateHost->getWindow();
defaultStrings.emplace_back(wCreateHost, 1224);
wCreateHost->getCloseButton()->setVisible(false);
wCreateHost->setVisible(false);
{
auto tDuelSettings = wtcCreateHost->addTab(gDataManager->GetSysString(2089).data());
defaultStrings.emplace_back(tDuelSettings, 2089);
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1226).data(), Scale(20, 10, 220, 30), false, false, tDuelSettings), 1226);
cbHostLFList = AddComboBox(env, Scale(140, 5, 300, 30), tDuelSettings, COMBOBOX_HOST_LFLIST);
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1225).data(), Scale(20, 40, 220, 60), false, false, tDuelSettings), 1225);
cbRule = AddComboBox(env, Scale(140, 35, 300, 60), tDuelSettings);
ReloadCBRule();
cbRule->setSelected(gGameConfig->lastallowedcards);
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1227).data(), Scale(20, 70, 220, 90), false, false, tDuelSettings), 1227);
#define WStr(i) epro::to_wstring<int>(i).data()
ebTeam1 = env->addEditBox(WStr(gGameConfig->team1count), Scale(140, 65, 170, 90), true, tDuelSettings, EDITBOX_TEAM_COUNT);
ebTeam1->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
auto vsstring = env->addStaticText(gDataManager->GetSysString(1380).data(), Scale(175, 65, 195, 90), false, false, tDuelSettings);
defaultStrings.emplace_back(vsstring, 1380);
vsstring->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
ebTeam2 = env->addEditBox(WStr(gGameConfig->team2count), Scale(200, 65, 230, 90), true, tDuelSettings, EDITBOX_TEAM_COUNT);
ebTeam2->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
vsstring = env->addStaticText(gDataManager->GetSysString(1381).data(), Scale(235, 65, 280, 90), false, false, tDuelSettings);
defaultStrings.emplace_back(vsstring, 1381);
vsstring->setTextAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_CENTER);
ebBestOf = env->addEditBox(WStr(gGameConfig->bestOf), Scale(285, 65, 315, 90), true, tDuelSettings, EDITBOX_NUMERIC);
ebBestOf->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
btnRelayMode = env->addButton(Scale(325, 65, 370, 90), tDuelSettings, -1, gDataManager->GetSysString(1247).data());
defaultStrings.emplace_back(btnRelayMode, 1247);
btnRelayMode->setIsPushButton(true);
btnRelayMode->setPressed(gGameConfig->relayDuel);
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1237).data(), Scale(20, 100, 320, 120), false, false, tDuelSettings), 1237);
ebTimeLimit = env->addEditBox(WStr(gGameConfig->timeLimit), Scale(140, 95, 220, 120), true, tDuelSettings, EDITBOX_NUMERIC);
ebTimeLimit->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1236).data(), Scale(20, 130, 220, 150), false, false, tDuelSettings), 1236);
cbDuelRule = AddComboBox(env, Scale(140, 125, 300, 150), tDuelSettings, COMBOBOX_DUEL_RULE);
chkNoShuffleDeck = env->addCheckBox(gGameConfig->noShuffleDeck, Scale(20, 160, 170, 180), tDuelSettings, DONT_SHUFFLE_DECK, gDataManager->GetSysString(1230).data());
defaultStrings.emplace_back(chkNoShuffleDeck, 1230);
menuHandler.MakeElementSynchronized(chkNoShuffleDeck);
chkTcgRulings = env->addCheckBox(duel_param & DUEL_TCG_SEGOC_NONPUBLIC, Scale(180, 160, 360, 180), tDuelSettings, TCG_SEGOC_NONPUBLIC, gDataManager->GetSysString(1239).data());
defaultStrings.emplace_back(chkTcgRulings, 1239);
chkNoCheckDeckContent = env->addCheckBox(gGameConfig->noCheckDeckContent, Scale(20, 190, 360, 210), tDuelSettings, DONT_CHECK_DECK_CONTENT, gDataManager->GetSysString(1229).data());
defaultStrings.emplace_back(chkNoCheckDeckContent, 1229);
menuHandler.MakeElementSynchronized(chkNoCheckDeckContent);
chkNoCheckDeckSize = env->addCheckBox(gGameConfig->noCheckDeckSize, Scale(20, 220, 360, 240), tDuelSettings, DONT_CHECK_DECK_SIZE, gDataManager->GetSysString(12113).data());
defaultStrings.emplace_back(chkNoCheckDeckSize, 12113);
menuHandler.MakeElementSynchronized(chkNoCheckDeckSize);
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1231).data(), Scale(20, 250, 320, 270), false, false, tDuelSettings), 1231);
ebStartLP = env->addEditBox(WStr(gGameConfig->startLP), Scale(140, 245, 220, 270), true, tDuelSettings, EDITBOX_NUMERIC);
ebStartLP->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1232).data(), Scale(20, 280, 320, 300), false, false, tDuelSettings), 1232);
ebStartHand = env->addEditBox(WStr(gGameConfig->startHand), Scale(140, 275, 220, 300), true, tDuelSettings, EDITBOX_NUMERIC);
ebStartHand->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1233).data(), Scale(20, 310, 320, 330), false, false, tDuelSettings), 1233);
ebDrawCount = env->addEditBox(WStr(gGameConfig->drawCount), Scale(140, 305, 220, 330), true, tDuelSettings, EDITBOX_NUMERIC);
ebDrawCount->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1234).data(), Scale(10, 340, 220, 360), false, false, tDuelSettings), 1234);
ebServerName = env->addEditBox(gGameConfig->gamename.data(), Scale(110, 335, 250, 360), true, tDuelSettings);
ebServerName->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
btnRuleCards = env->addButton(Scale(260, 335, 370, 360), tDuelSettings, BUTTON_RULE_CARDS, gDataManager->GetSysString(1625).data());
defaultStrings.emplace_back(btnRuleCards, 1625);
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1235).data(), Scale(10, 370, 220, 390), false, false, tDuelSettings), 1235);
ebServerPass = env->addEditBox(L"", Scale(110, 365, 250, 390), true, tDuelSettings);
ebServerPass->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
btnHostConfirm = env->addButton(Scale(260, 365, 370, 390), tDuelSettings, BUTTON_HOST_CONFIRM, gDataManager->GetSysString(1211).data());
defaultStrings.emplace_back(btnHostConfirm, 1211);
btnHostCancel = env->addButton(Scale(260, 395, 370, 420), tDuelSettings, BUTTON_HOST_CANCEL, gDataManager->GetSysString(1212).data());
defaultStrings.emplace_back(btnHostCancel, 1212);
stHostPort = env->addStaticText(gDataManager->GetSysString(1238).data(), Scale(10, 400, 220, 420), false, false, tDuelSettings);
defaultStrings.emplace_back(stHostPort, 1238);
ebHostPort = env->addEditBox(gGameConfig->serverport.data(), Scale(110, 395, 250, 420), true, tDuelSettings, EDITBOX_PORT_BOX);
ebHostPort->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
stHostNotes = env->addStaticText(gDataManager->GetSysString(2024).data(), Scale(10, 400, 220, 420), false, false, tDuelSettings);
defaultStrings.emplace_back(stHostNotes, 2024);
stHostNotes->setVisible(false);
ebHostNotes = env->addEditBox(L"", Scale(110, 395, 250, 420), true, tDuelSettings);
ebHostNotes->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
ebHostNotes->setVisible(false);
wRules = env->addWindow(Scale(630, 100, 1000, 310), false, L"");
wRules->getCloseButton()->setVisible(false);
wRules->setDrawTitlebar(false);
wRules->setDraggable(true);
wRules->setVisible(false);
btnRulesOK = env->addButton(Scale(135, 175, 235, 200), wRules, BUTTON_RULE_OK, gDataManager->GetSysString(1211).data());
defaultStrings.emplace_back(btnRulesOK, 1211);
for(int i = 0, str = 1132; i < static_cast<int>(sizeofarr(chkRules)); ++str) {
chkRules[i] = env->addCheckBox(false, Scale(10 + (i % 2) * 150, 10 + (i / 2) * 20, 200 + (i % 2) * 120, 30 + (i / 2) * 20), wRules, CHECKBOX_EXTRA_RULE, gDataManager->GetSysString(str).data());
defaultStrings.emplace_back(chkRules[i], str);
++i;
}
extra_rules = gGameConfig->lastExtraRules;
UpdateExtraRules(true);
}
{
irr::s32 cur_y = 15;
constexpr auto y_incr = 30;
auto GetNextRect = [&cur_y, y_incr, this] {
auto cur = cur_y;
cur_y += y_incr;
return Scale<irr::s32>(20, cur, 360, cur + 25);
};
auto GetCurrentRectWithXOffset = [&cur_y, this](irr::s32 x1, irr::s32 x2) {
return Scale<irr::s32>(x1, cur_y, x2, cur_y + 25);
};
auto tDeckSettings = wtcCreateHost->addTab(gDataManager->GetSysString(12105).data());
defaultStrings.emplace_back(tDeckSettings, 12105);
chkNoShuffleDeckSecondary = env->addCheckBox(gGameConfig->noShuffleDeck, GetNextRect(), tDeckSettings, DONT_SHUFFLE_DECK, gDataManager->GetSysString(1230).data());
defaultStrings.emplace_back(chkNoShuffleDeckSecondary, 1230);
menuHandler.MakeElementSynchronized(chkNoShuffleDeckSecondary);
chkNoCheckDeckContentSecondary = env->addCheckBox(gGameConfig->noCheckDeckContent, GetNextRect(), tDeckSettings, DONT_CHECK_DECK_CONTENT, gDataManager->GetSysString(1229).data());
defaultStrings.emplace_back(chkNoCheckDeckContentSecondary, 1229);
menuHandler.MakeElementSynchronized(chkNoCheckDeckContentSecondary);
chkNoCheckDeckSizeSecondary = env->addCheckBox(gGameConfig->noCheckDeckSize, GetNextRect(), tDeckSettings, DONT_CHECK_DECK_SIZE, gDataManager->GetSysString(12113).data());
defaultStrings.emplace_back(chkNoCheckDeckSizeSecondary, 12113);
menuHandler.MakeElementSynchronized(chkNoCheckDeckSizeSecondary);
#define ADD_DECK_SIZE_CHECKBOXES(deck) do { \
eb##deck##Min = env->addEditBox(WStr(gGameConfig->min##deck##DeckSize), GetCurrentRectWithXOffset(310, 360), true, tDeckSettings, EDITBOX_NUMERIC); \
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(12106 + idx).data(), GetCurrentRectWithXOffset(20, 300), false, false, tDeckSettings), 12106 + idx); \
eb##deck##Min->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER); \
cur_y += y_incr; \
++idx; \
eb##deck##Max = env->addEditBox(WStr(gGameConfig->max##deck##DeckSize), GetCurrentRectWithXOffset(310, 360), true, tDeckSettings, EDITBOX_NUMERIC); \
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(12106 + idx).data(), GetCurrentRectWithXOffset(20, 300), false, false, tDeckSettings), 12106 + idx); \
eb##deck##Max->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER); \
cur_y += y_incr; \
++idx; \
} while(0)
{
int idx = 0;
ADD_DECK_SIZE_CHECKBOXES(Main);
ADD_DECK_SIZE_CHECKBOXES(Extra);
ADD_DECK_SIZE_CHECKBOXES(Side);
#undef ADD_DECK_SIZE_CHECKBOXES
}
}
{
forbiddentypes = gGameConfig->lastDuelForbidden;
auto tCustomRules = wtcCreateHost->addTab(gDataManager->GetSysString(1630).data());
defaultStrings.emplace_back(tCustomRules, 1630);
//crRect.LowerRightCorner.Y -= 45; //ok button
auto tmpPanel = irr::gui::Panel::addPanel(env, tCustomRules, -1, { {}, tCustomRules->getAbsolutePosition().getSize() }, true, false);
auto crPanel = tmpPanel->getSubpanel();
int spacingL = 0;
auto rectsize = [&spacingL, this]() {
auto ret = Scale(10, spacingL * 20, 680, 20 + spacingL * 20);
spacingL++;
return ret;
};
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1629).data(), rectsize(), false, false, crPanel), 1629);
for(auto i = 0u; i < sizeofarr(chkCustomRules); ++i) {
bool set = false;
if(i == 19)
set = duel_param & DUEL_USE_TRAPS_IN_NEW_CHAIN;
else if(i == 20)
set = duel_param & DUEL_6_STEP_BATLLE_STEP;
else if(i == 21)
set = duel_param & DUEL_TRIGGER_WHEN_PRIVATE_KNOWLEDGE;
else if(i > 21)
set = duel_param & 0x100ULL << (i - 3);
else
set = duel_param & 0x100ULL << i;
chkCustomRules[i] = env->addCheckBox(set, rectsize(), crPanel, CHECKBOX_OBSOLETE + i, gDataManager->GetSysString(1631 + i).data());
defaultStrings.emplace_back(chkCustomRules[i], 1631 + i);
}
defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1628).data(), rectsize(), false, false, crPanel), 1628);
static constexpr uint32_t limits[]{ TYPE_FUSION, TYPE_SYNCHRO, TYPE_XYZ, TYPE_PENDULUM, TYPE_LINK };
#define TYPECHK(id,stringid)\
chkTypeLimit[id] = env->addCheckBox(forbiddentypes & limits[id], rectsize(), crPanel, -1, epro::sprintf(gDataManager->GetSysString(1627), gDataManager->GetSysString(stringid)).data());
TYPECHK(0, 1056);
TYPECHK(1, 1063);
TYPECHK(2, 1073);
TYPECHK(3, 1074);
TYPECHK(4, 1076);
#undef TYPECHK
UpdateDuelParam();
}
//host(single)
wHostPrepare = env->addWindow(Scale(270, 120, 750, 440), false, gDataManager->GetSysString(1250).data());
defaultStrings.emplace_back(wHostPrepare, 1250);
wHostPrepare->getCloseButton()->setVisible(false);
wHostPrepare->setVisible(false);
wHostPrepareR = env->addWindow(Scale(750, 120, 950, 440), false, gDataManager->GetSysString(1625).data());
defaultStrings.emplace_back(wHostPrepareR, 1625);
wHostPrepareR->getCloseButton()->setVisible(false);
wHostPrepareR->setVisible(false);
wHostPrepareL = env->addWindow(Scale(70, 120, 270, 440), false, gDataManager->GetSysString(1628).data());
defaultStrings.emplace_back(wHostPrepareL, 1628);
wHostPrepareL->getCloseButton()->setVisible(false);
wHostPrepareL->setVisible(false);
auto wHostPrepareRrect = wHostPrepareR->getClientRect();
wHostPrepareRrect.UpperLeftCorner.X += Scale(10);
wHostPrepareRrect.LowerRightCorner.X -= Scale(10);
stHostPrepRuleR = irr::gui::CGUICustomText::addCustomText(L"", false, env, wHostPrepareR, -1, wHostPrepareRrect);
((irr::gui::CGUICustomText*)stHostPrepRuleR)->enableScrollBar();
stHostPrepRuleR->setWordWrap(true);
stHostPrepRuleL = irr::gui::CGUICustomText::addCustomText(L"", false, env, wHostPrepareL, -1, wHostPrepareRrect);
((irr::gui::CGUICustomText*)stHostPrepRuleL)->enableScrollBar();
stHostPrepRuleL->setWordWrap(true);
btnHostPrepDuelist = env->addButton(Scale(10, 30, 110, 55), wHostPrepare, BUTTON_HP_DUELIST, gDataManager->GetSysString(1251).data());
defaultStrings.emplace_back(btnHostPrepDuelist, 1251);
btnHostPrepWindBot = env->addButton(Scale(170, 30, 270, 55), wHostPrepare, BUTTON_HP_AI_TOGGLE, gDataManager->GetSysString(2050).data());
defaultStrings.emplace_back(btnHostPrepWindBot, 2050);
for(int i = 0; i < 6; ++i) {
btnHostPrepKick[i] = env->addButton(Scale(10, 65 + i * 25, 30, 85 + i * 25), wHostPrepare, BUTTON_HP_KICK, L"X");
stHostPrepDuelist[i] = env->addStaticText(L"", Scale(40, 65 + i * 25, 240, 85 + i * 25), true, false, wHostPrepare);
chkHostPrepReady[i] = env->addCheckBox(false, Scale(250, 65 + i * 25, 270, 85 + i * 25), wHostPrepare, CHECKBOX_HP_READY, L"");
chkHostPrepReady[i]->setEnabled(false);
}
btnHostPrepOB = env->addButton(Scale(10, 180, 110, 205), wHostPrepare, BUTTON_HP_OBSERVER, gDataManager->GetSysString(1252).data());
defaultStrings.emplace_back(btnHostPrepOB, 1252);
stHostPrepOB = env->addStaticText(epro::format(L"{} 0", gDataManager->GetSysString(1253)).data(), Scale(10, 210, 270, 230), false, false, wHostPrepare);
defaultStrings.emplace_back(stHostPrepOB, 1253);
stHostPrepRule = irr::gui::CGUICustomText::addCustomText(L"", false, env, wHostPrepare, -1, Scale(280, 30, 460, 270));
stHostPrepRule->setWordWrap(true);
stDeckSelect = env->addStaticText(gDataManager->GetSysString(1254).data(), Scale(10, 235, 110, 255), false, false, wHostPrepare);
defaultStrings.emplace_back(stDeckSelect, 1254);
cbDeckSelect = AddComboBox(env, Scale(120, 230, 270, 255), wHostPrepare);
cbDeckSelect->setMaxSelectionRows(10);
btnHostPrepReady = env->addButton(Scale(170, 180, 270, 205), wHostPrepare, BUTTON_HP_READY, gDataManager->GetSysString(1218).data());
defaultStrings.emplace_back(btnHostPrepReady, 1218);
btnHostPrepNotReady = env->addButton(Scale(170, 180, 270, 205), wHostPrepare, BUTTON_HP_NOTREADY, gDataManager->GetSysString(1219).data());
defaultStrings.emplace_back(btnHostPrepNotReady, 1219);
btnHostPrepNotReady->setVisible(false);
btnHostPrepStart = env->addButton(Scale(230, 280, 340, 305), wHostPrepare, BUTTON_HP_START, gDataManager->GetSysString(1215).data());
defaultStrings.emplace_back(btnHostPrepStart, 1215);
btnHostPrepCancel = env->addButton(Scale(350, 280, 460, 305), wHostPrepare, BUTTON_HP_CANCEL, gDataManager->GetSysString(1210).data());
defaultStrings.emplace_back(btnHostPrepCancel, 1210);
}
void Game::PopulateAIBotWindow() {
#if !EDOPRO_ANDROID && !EDOPRO_IOS
static constexpr bool showWindbotArgs = true;
#else
static constexpr bool showWindbotArgs = false;
#endif
gBot.window = env->addWindow(Scale(750, 120, 960, showWindbotArgs ? 455 : 420), false, gDataManager->GetSysString(2051).data());
defaultStrings.emplace_back(gBot.window, 2051);
gBot.window->getCloseButton()->setVisible(false);
gBot.window->setVisible(false);
gBot.deckProperties = env->addStaticText(L"", Scale(10, 25, 200, 100), true, true, gBot.window);
gBot.chkThrowRock = env->addCheckBox(gGameConfig->botThrowRock, Scale(10, 105, 200, 130), gBot.window, -1, gDataManager->GetSysString(2052).data());
defaultStrings.emplace_back(gBot.chkThrowRock, 2052);
gBot.chkMute = env->addCheckBox(gGameConfig->botMute, Scale(10, 135, 200, 160), gBot.window, -1, gDataManager->GetSysString(2053).data());
defaultStrings.emplace_back(gBot.chkMute, 2053);
gBot.cbBotDeck = AddComboBox(env, Scale(10, 165, 200, 190), gBot.window, COMBOBOX_BOT_DECK);
gBot.stBotEngine = env->addStaticText(gDataManager->GetSysString(2082).data(), Scale(10, 195, 200, 220), false, false, gBot.window);
defaultStrings.emplace_back(gBot.stBotEngine, 2082);
gBot.cbBotEngine = AddComboBox(env, Scale(10, 225, 200, 250), gBot.window, COMBOBOX_BOT_ENGINE);
gBot.btnAdd = env->addButton(Scale(10, 260, 200, 285), gBot.window, BUTTON_BOT_ADD, gDataManager->GetSysString(2054).data());
defaultStrings.emplace_back(gBot.btnAdd, 2054);
if(showWindbotArgs) {
gBot.btnCommand = env->addButton(Scale(10, 295, 200, 320), gBot.window, BUTTON_BOT_COPY_COMMAND, gDataManager->GetSysString(12120).data());
defaultStrings.emplace_back(gBot.btnCommand, 12120);
}
}
void Game::PopulateTabSettingsWindow() {
//tab
infosExpanded = 0;
wInfos = AlignElementWithParent(irr::gui::CGUICustomTabControl::addCustomTabControl(env, Scale(1, 275, 301, 639), 0, true));
wInfos->setVisible(false);
//info
{
irr::gui::IGUITab* tabInfo = wInfos->addTab(gDataManager->GetSysString(1270).data());
defaultStrings.emplace_back(tabInfo, 1270);
{
auto name = irr::gui::CGUICustomText::addCustomText(L"", true, env, tabInfo, -1, Scale(10, 10, 287, 32));
name->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
name->setTextAutoScrolling(irr::gui::CGUICustomText::LEFT_TO_RIGHT_BOUNCING, 0, 1.0f, 0, 120, 300);
stName = name;
}
stInfo = irr::gui::CGUICustomText::addCustomText(L"", false, env, tabInfo, -1, Scale(15, 37, 287, 60));
stInfo->setWordWrap(true);
stInfo->setOverrideColor(skin::CARDINFO_TYPES_COLOR_VAL);
stDataInfo = irr::gui::CGUICustomText::addCustomText(L"", false, env, tabInfo, -1, Scale(15, 60, 287, 83));
stDataInfo->setWordWrap(true);
stDataInfo->setOverrideColor(skin::CARDINFO_STATS_COLOR_VAL);
stSetName = irr::gui::CGUICustomText::addCustomText(L"", false, env, tabInfo, -1, Scale(15, 83, 287, 106));
stSetName->setWordWrap(true);
stSetName->setOverrideColor(skin::CARDINFO_ARCHETYPE_TEXT_COLOR_VAL);
stSetName->setVisible(!gGameConfig->chkHideSetname);
stPasscodeScope = irr::gui::CGUICustomText::addCustomText(L"", false, env, tabInfo, -1, Scale(15, 106, 287, 129));
stPasscodeScope->setWordWrap(true);
stPasscodeScope->setOverrideColor(skin::CARDINFO_PASSCODE_SCOPE_TEXT_COLOR_VAL);
stPasscodeScope->setVisible(!gGameConfig->hidePasscodeScope);
{
auto text = irr::gui::CGUICustomText::addCustomText(L"", false, env, tabInfo, -1, Scale(15, 129, 287, 324));
text->enableScrollBar();
stText = text;
}
stText->setWordWrap(true);
}
//log
{
tabLog = wInfos->addTab(gDataManager->GetSysString(1271).data());
defaultStrings.emplace_back(tabLog, 1271);
lstLog = env->addListBox(Scale(10, 10, 290, 290), tabLog, LISTBOX_LOG, false);
lstLog->setItemHeight(Scale(18));
btnClearLog = env->addButton(Scale(160, 300, 260, 325), tabLog, BUTTON_CLEAR_LOG, gDataManager->GetSysString(1272).data());
defaultStrings.emplace_back(btnClearLog, 1272);
btnExpandLog = env->addButton(Scale(40, 300, 140, 325), tabLog, BUTTON_EXPAND_INFOBOX, gDataManager->GetSysString(2043).data());
defaultStrings.emplace_back(btnExpandLog, 2043);
}
//chat
{
tabChat = wInfos->addTab(gDataManager->GetSysString(1279).data());
defaultStrings.emplace_back(tabChat, 1279);
lstChat = env->addListBox(Scale(10, 10, 290, 290), tabChat, -1, false);
lstChat->setItemHeight(Scale(18));
btnClearChat = env->addButton(Scale(160, 300, 260, 325), tabChat, BUTTON_CLEAR_CHAT, gDataManager->GetSysString(1282).data());
defaultStrings.emplace_back(btnClearChat, 1282);
btnExpandChat = env->addButton(Scale(40, 300, 140, 325), tabChat, BUTTON_EXPAND_INFOBOX, gDataManager->GetSysString(2043).data());
defaultStrings.emplace_back(btnExpandChat, 2043);
}
//system
{
irr::s32 cur_y = 20;
constexpr auto y_incr = 30;
auto GetNextRect = [&cur_y, y_incr, this] {
auto cur = cur_y;
cur_y += y_incr;
return Scale<irr::s32>(20, cur, 280, cur + 25);
};
auto GetCurrentRectWithXOffset = [&cur_y, this](irr::s32 x1, irr::s32 x2, bool is_scrollbar = false) {
return Scale<irr::s32>(x1, cur_y + (is_scrollbar * 5), x2, cur_y + 25 - (is_scrollbar * 5));
};
irr::gui::IGUITab* _tabSystem = wInfos->addTab(gDataManager->GetSysString(1273).data());
defaultStrings.emplace_back(_tabSystem, 1273);
tabSystem = irr::gui::Panel::addPanel(env, _tabSystem, -1, { {}, _tabSystem->getAbsolutePosition().getSize() }, true, false);
auto tabPanel = tabSystem->getSubpanel();
tabSettings.chkIgnoreOpponents = env->addCheckBox(gGameConfig->chkIgnore1, GetNextRect(), tabPanel, CHECKBOX_IGNORE_OPPONENTS, gDataManager->GetSysString(1290).data());
menuHandler.MakeElementSynchronized(tabSettings.chkIgnoreOpponents);
defaultStrings.emplace_back(tabSettings.chkIgnoreOpponents, 1290);
tabSettings.chkIgnoreSpectators = env->addCheckBox(gGameConfig->chkIgnore2, GetNextRect(), tabPanel, CHECKBOX_IGNORE_SPECTATORS, gDataManager->GetSysString(1291).data());
menuHandler.MakeElementSynchronized(tabSettings.chkIgnoreSpectators);
defaultStrings.emplace_back(tabSettings.chkIgnoreSpectators, 1291);
tabSettings.chkQuickAnimation = env->addCheckBox(gGameConfig->quick_animation, GetNextRect(), tabPanel, CHECKBOX_QUICK_ANIMATION, gDataManager->GetSysString(1299).data());
menuHandler.MakeElementSynchronized(tabSettings.chkQuickAnimation);
defaultStrings.emplace_back(tabSettings.chkQuickAnimation, 1299);
tabSettings.chkTopdown = env->addCheckBox(gGameConfig->topdown_view, GetNextRect(), tabPanel, CHECKBOX_TOPDOWN, gDataManager->GetSysString(2093).data());
menuHandler.MakeElementSynchronized(tabSettings.chkTopdown);
defaultStrings.emplace_back(tabSettings.chkTopdown, 2093);
tabSettings.chkKeepFieldRatio = env->addCheckBox(gGameConfig->keep_aspect_ratio, GetNextRect(), tabPanel, CHECKBOX_KEEP_FIELD_ASPECT_RATIO, gDataManager->GetSysString(2094).data());
menuHandler.MakeElementSynchronized(tabSettings.chkKeepFieldRatio);
defaultStrings.emplace_back(tabSettings.chkKeepFieldRatio, 2094);
tabSettings.chkAlternativePhaseLayout = env->addCheckBox(gGameConfig->alternative_phase_layout, GetNextRect(), tabPanel, CHECKBOX_ALTERNATIVE_PHASE_LAYOUT, gDataManager->GetSysString(1298).data());
menuHandler.MakeElementSynchronized(tabSettings.chkAlternativePhaseLayout);
defaultStrings.emplace_back(tabSettings.chkAlternativePhaseLayout, 1298);
tabSettings.chkHideChainButtons = env->addCheckBox(gGameConfig->chkHideHintButton, GetNextRect(), tabPanel, CHECKBOX_CHAIN_BUTTONS, gDataManager->GetSysString(1355).data());
menuHandler.MakeElementSynchronized(tabSettings.chkHideChainButtons);
defaultStrings.emplace_back(tabSettings.chkHideChainButtons, 1355);
tabSettings.chkAutoChainOrder = env->addCheckBox(gGameConfig->chkAutoChain, GetNextRect(), tabPanel, CHECKBOX_AUTO_CHAIN_ORDER, gDataManager->GetSysString(1276).data());
menuHandler.MakeElementSynchronized(tabSettings.chkAutoChainOrder);
defaultStrings.emplace_back(tabSettings.chkAutoChainOrder, 1276);
// audio
{
const auto next_rect = GetNextRect();
tabSettings.stNoAudioBackend = env->addStaticText(gDataManager->GetSysString(2058).data(), next_rect, false, true, tabPanel);
defaultStrings.emplace_back(tabSettings.stNoAudioBackend, 2058);
tabSettings.stNoAudioBackend->setVisible(false);
tabSettings.chkEnableSound = env->addCheckBox(gGameConfig->enablesound, next_rect, tabPanel, CHECKBOX_ENABLE_SOUND, gDataManager->GetSysString(2047).data());
menuHandler.MakeElementSynchronized(tabSettings.chkEnableSound);
defaultStrings.emplace_back(tabSettings.chkEnableSound, 2047);
}
{
tabSettings.stSoundVolume = env->addStaticText(gDataManager->GetSysString(2049).data(), GetCurrentRectWithXOffset(20, 80), false, true, tabPanel);
defaultStrings.emplace_back(tabSettings.stSoundVolume, 2049);
tabSettings.scrSoundVolume = env->addScrollBar(true, GetCurrentRectWithXOffset(85, 280, true), tabPanel, SCROLL_SOUND_VOLUME);
menuHandler.MakeElementSynchronized(tabSettings.scrSoundVolume);
tabSettings.scrSoundVolume->setMax(100);
tabSettings.scrSoundVolume->setMin(0);
tabSettings.scrSoundVolume->setPos(gGameConfig->soundVolume);
tabSettings.scrSoundVolume->setLargeStep(1);
tabSettings.scrSoundVolume->setSmallStep(1);
cur_y += y_incr;
}
tabSettings.chkEnableMusic = env->addCheckBox(gGameConfig->enablemusic, GetNextRect(), tabPanel, CHECKBOX_ENABLE_MUSIC, gDataManager->GetSysString(2046).data());
menuHandler.MakeElementSynchronized(tabSettings.chkEnableMusic);
defaultStrings.emplace_back(tabSettings.chkEnableMusic, 2046);
{
tabSettings.stMusicVolume = env->addStaticText(gDataManager->GetSysString(2048).data(), GetCurrentRectWithXOffset(20, 80), false, true, tabPanel);
defaultStrings.emplace_back(tabSettings.stMusicVolume, 2048);
tabSettings.scrMusicVolume = env->addScrollBar(true, GetCurrentRectWithXOffset(85, 280, true), tabPanel, SCROLL_MUSIC_VOLUME);
menuHandler.MakeElementSynchronized(tabSettings.scrMusicVolume);
tabSettings.scrMusicVolume->setMax(100);
tabSettings.scrMusicVolume->setMin(0);
tabSettings.scrMusicVolume->setPos(gGameConfig->musicVolume);
tabSettings.scrMusicVolume->setLargeStep(1);
tabSettings.scrMusicVolume->setSmallStep(1);
cur_y += y_incr;
}
tabSettings.chkNoChainDelay = env->addCheckBox(gGameConfig->chkWaitChain, GetNextRect(), tabPanel, CHECKBOX_NO_CHAIN_DELAY, gDataManager->GetSysString(1277).data());
menuHandler.MakeElementSynchronized(tabSettings.chkNoChainDelay);
defaultStrings.emplace_back(tabSettings.chkNoChainDelay, 1277);
tabSettings.chkIgnoreDeckContents = env->addCheckBox(gGameConfig->ignoreDeckContents, GetNextRect(), tabPanel, CHECKBOX_IGNORE_DECK_CONTENTS, gDataManager->GetSysString(12119).data());
menuHandler.MakeElementSynchronized(tabSettings.chkIgnoreDeckContents);
defaultStrings.emplace_back(tabSettings.chkIgnoreDeckContents, 12119);
// Check OnResize for button placement information
cur_y += 5;
btnTabShowSettings = env->addButton(GetNextRect(), tabPanel, BUTTON_SHOW_SETTINGS, gDataManager->GetSysString(2059).data());
defaultStrings.emplace_back(btnTabShowSettings, 2059);
/* padding = */ env->addStaticText(L"", Scale(20, cur_y, 280, cur_y + 10), false, true, tabPanel, -1, false);
}
//repositories
{
tabRepositories = wInfos->addTab(gDataManager->GetSysString(2045).data());
defaultStrings.emplace_back(tabRepositories, 2045);
mTabRepositories = irr::gui::CGUICustomContextMenu::addCustomContextMenu(env, tabRepositories, -1, Scale(1, 275, 301, 639));
mTabRepositories->setCloseHandling(irr::gui::ECONTEXT_MENU_CLOSE::ECMC_HIDE);
}
}
void Game::PopulateSettingsWindow() {
gSettings.tabcontrolwindow = irr::gui::CGUIWindowedTabControl::addCGUIWindowedTabControl(env, Scale(180, 85, 840, 515), gDataManager->GetSysString(1273).data());
gSettings.window = gSettings.tabcontrolwindow->getWindow();
defaultStrings.emplace_back(gSettings.window, 1273);
gSettings.window->setVisible(false);
irr::s32 cur_y = 5;
irr::s32 cur_x = 15;
constexpr auto y_incr = 30;
constexpr auto x_incr = 325;
auto ResetXandY = [&cur_y, base_y = cur_y, &cur_x, base_x = cur_x] {
cur_y = base_y;
cur_x = base_x;
};
auto IncrementXorY = [&cur_y, y_incr, &cur_x, x_incr] {
if(cur_x > x_incr) {
cur_x -= x_incr;
cur_y += y_incr;
} else
cur_x += x_incr;
};
auto GetNextRect = [&cur_y, &cur_x, &IncrementXorY, this] {
auto cury = cur_y;
auto curx = cur_x;
IncrementXorY();
return Scale<irr::s32>(curx, cury, curx + 305, cury + 25);
};
auto GetCurrentRectWithXOffset = [&cur_y, &cur_x, this](irr::s32 x1, irr::s32 x2, bool is_scrollbar_text = false) {
const auto x_incr = cur_x - 15;
return Scale<irr::s32>(x1 + x_incr, cur_y, x2 + x_incr, cur_y + 25 - (is_scrollbar_text * 5));
};
{
gSettings.client.construct(env, gSettings.tabcontrolwindow, gDataManager->GetSysString(2088).data());
defaultStrings.emplace_back(gSettings.client.tab, 2088);
auto* sPanel = gSettings.client.panel->getSubpanel();
gSettings.chkShowScopeLabel = env->addCheckBox(gGameConfig->showScopeLabel, GetNextRect(), sPanel, CHECKBOX_SHOW_SCOPE_LABEL, gDataManager->GetSysString(2076).data());
defaultStrings.emplace_back(gSettings.chkShowScopeLabel, 2076);
gSettings.chkShowFPS = env->addCheckBox(gGameConfig->showFPS, GetNextRect(), sPanel, CHECKBOX_SHOW_FPS, gDataManager->GetSysString(1445).data());
defaultStrings.emplace_back(gSettings.chkShowFPS, 1445);
gSettings.chkHideSetname = env->addCheckBox(gGameConfig->chkHideSetname, GetNextRect(), sPanel, CHECKBOX_HIDE_ARCHETYPES, gDataManager->GetSysString(1354).data());
defaultStrings.emplace_back(gSettings.chkHideSetname, 1354);
gSettings.chkHidePasscodeScope = env->addCheckBox(gGameConfig->hidePasscodeScope, GetNextRect(), sPanel, CHECKBOX_HIDE_PASSCODE_SCOPE, gDataManager->GetSysString(2063).data());
defaultStrings.emplace_back(gSettings.chkHidePasscodeScope, 2063);
gSettings.chkFilterBot = env->addCheckBox(gGameConfig->filterBot, GetNextRect(), sPanel, CHECKBOX_FILTER_BOT, gDataManager->GetSysString(2069).data());
defaultStrings.emplace_back(gSettings.chkFilterBot, 2069);
{
gSettings.stCurrentSkin = env->addStaticText(gDataManager->GetSysString(2064).data(), GetCurrentRectWithXOffset(15, 90), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stCurrentSkin, 2064);
gSettings.cbCurrentSkin = AddComboBox(env, GetCurrentRectWithXOffset(95, 320), sPanel, COMBOBOX_CURRENT_SKIN);
ReloadCBCurrentSkin();
IncrementXorY();
}
{
gSettings.stCurrentLocale = env->addStaticText(gDataManager->GetSysString(2067).data(), GetCurrentRectWithXOffset(15, 90), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stCurrentLocale, 2067);
PopulateLocales();
gSettings.cbCurrentLocale = AddComboBox(env, GetCurrentRectWithXOffset(95, 320), sPanel, COMBOBOX_CURRENT_LOCALE);
auto selectedLocale = gSettings.cbCurrentLocale->addItem(L"English");
for(const auto& _locale : locales) {
auto& locale = _locale.first;
auto itemIndex = gSettings.cbCurrentLocale->addItem(Utils::ToUnicodeIfNeeded(locale).data());
if(gGameConfig->locale == locale) {
selectedLocale = itemIndex;
}
}
gSettings.cbCurrentLocale->setSelected(selectedLocale);
IncrementXorY();
}
gSettings.btnReloadSkin = env->addButton(GetNextRect(), sPanel, BUTTON_RELOAD_SKIN, gDataManager->GetSysString(2066).data());
defaultStrings.emplace_back(gSettings.btnReloadSkin, 2066);
{
gSettings.stDpiScale = env->addStaticText(gDataManager->GetSysString(2070).data(), GetCurrentRectWithXOffset(15, 90), false, false, sPanel);
defaultStrings.emplace_back(gSettings.stDpiScale, 2070);
gSettings.ebDpiScale = env->addEditBox(WStr(gGameConfig->dpi_scale * 100), GetCurrentRectWithXOffset(95, 150), true, sPanel, EDITBOX_NUMERIC);
env->addStaticText(L"%", GetCurrentRectWithXOffset(155, 170), false, false, sPanel);
gSettings.ebDpiScale->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
gSettings.btnRestart = env->addButton(GetCurrentRectWithXOffset(175, 320), sPanel, BUTTON_APPLY_RESTART, gDataManager->GetSysString(2071).data());
defaultStrings.emplace_back(gSettings.btnRestart, 2071);
IncrementXorY();
}
#ifdef UPDATE_URL
gSettings.chkUpdates = env->addCheckBox(gGameConfig->noClientUpdates, GetNextRect(), sPanel, -1, gDataManager->GetSysString(1466).data());
defaultStrings.emplace_back(gSettings.chkUpdates, 1466);
#endif
gSettings.chkHideHandsInReplays = env->addCheckBox(gGameConfig->hideHandsInReplays, GetNextRect(), sPanel, CHECKBOX_HIDE_HANDS_REPLAY, gDataManager->GetSysString(2080).data());
defaultStrings.emplace_back(gSettings.chkHideHandsInReplays, 2080);
gSettings.chkConfirmDeckClear = env->addCheckBox(gGameConfig->confirm_clear_deck, GetNextRect(), sPanel, CHECKBOX_CONFIRM_DECK_CLEAR, gDataManager->GetSysString(12104).data());
defaultStrings.emplace_back(gSettings.chkConfirmDeckClear, 12104);
gSettings.chkIgnoreDeckContents = env->addCheckBox(gGameConfig->ignoreDeckContents, GetNextRect(), sPanel, CHECKBOX_IGNORE_DECK_CONTENTS, gDataManager->GetSysString(12119).data());
menuHandler.MakeElementSynchronized(gSettings.chkIgnoreDeckContents);
defaultStrings.emplace_back(gSettings.chkIgnoreDeckContents, 12119);
gSettings.chkAddCardNamesInDeckList = env->addCheckBox(gGameConfig->addCardNamesToDeckList, GetNextRect(), sPanel, CHECKBOX_ADD_CARD_NAME_TO_DECK_LIST, gDataManager->GetSysString(12123).data());
defaultStrings.emplace_back(gSettings.chkAddCardNamesInDeckList, 12123);
}
{
gSettings.system.construct(env, gSettings.tabcontrolwindow, gDataManager->GetSysString(2089).data());
defaultStrings.emplace_back(gSettings.system.tab, 2089);
ResetXandY();
auto* sPanel = gSettings.system.panel->getSubpanel();
gSettings.chkIgnoreOpponents = env->addCheckBox(gGameConfig->chkIgnore1, GetNextRect(), sPanel, CHECKBOX_IGNORE_OPPONENTS, gDataManager->GetSysString(1290).data());
menuHandler.MakeElementSynchronized(gSettings.chkIgnoreOpponents);
defaultStrings.emplace_back(gSettings.chkIgnoreOpponents, 1290);
gSettings.chkIgnoreSpectators = env->addCheckBox(gGameConfig->chkIgnore2, GetNextRect(), sPanel, CHECKBOX_IGNORE_SPECTATORS, gDataManager->GetSysString(1291).data());
menuHandler.MakeElementSynchronized(gSettings.chkIgnoreSpectators);
defaultStrings.emplace_back(gSettings.chkIgnoreSpectators, 1291);
gSettings.chkQuickAnimation = env->addCheckBox(gGameConfig->quick_animation, GetNextRect(), sPanel, CHECKBOX_QUICK_ANIMATION, gDataManager->GetSysString(1299).data());
menuHandler.MakeElementSynchronized(gSettings.chkQuickAnimation);
defaultStrings.emplace_back(gSettings.chkQuickAnimation, 1299);
gSettings.chkTopdown = env->addCheckBox(gGameConfig->topdown_view, GetNextRect(), sPanel, CHECKBOX_TOPDOWN, gDataManager->GetSysString(2093).data());
menuHandler.MakeElementSynchronized(gSettings.chkTopdown);
defaultStrings.emplace_back(gSettings.chkTopdown, 2093);
gSettings.chkKeepFieldRatio = env->addCheckBox(gGameConfig->keep_aspect_ratio, GetNextRect(), sPanel, CHECKBOX_KEEP_FIELD_ASPECT_RATIO, gDataManager->GetSysString(2094).data());
menuHandler.MakeElementSynchronized(gSettings.chkKeepFieldRatio);
defaultStrings.emplace_back(gSettings.chkKeepFieldRatio, 2094);
gSettings.chkKeepCardRatio = env->addCheckBox(gGameConfig->keep_cardinfo_aspect_ratio, GetNextRect(), sPanel, CHECKBOX_KEEP_CARD_ASPECT_RATIO, gDataManager->GetSysString(2095).data());
defaultStrings.emplace_back(gSettings.chkKeepCardRatio, 2095);
gSettings.chkAlternativePhaseLayout = env->addCheckBox(gGameConfig->alternative_phase_layout, GetNextRect(), sPanel, CHECKBOX_ALTERNATIVE_PHASE_LAYOUT, gDataManager->GetSysString(1298).data());
menuHandler.MakeElementSynchronized(gSettings.chkAlternativePhaseLayout);
defaultStrings.emplace_back(gSettings.chkAlternativePhaseLayout, 1298);
gSettings.chkHideChainButtons = env->addCheckBox(gGameConfig->chkHideHintButton, GetNextRect(), sPanel, CHECKBOX_CHAIN_BUTTONS, gDataManager->GetSysString(1355).data());
menuHandler.MakeElementSynchronized(gSettings.chkHideChainButtons);
defaultStrings.emplace_back(gSettings.chkHideChainButtons, 1355);
gSettings.chkAutoChainOrder = env->addCheckBox(gGameConfig->chkAutoChain, GetNextRect(), sPanel, CHECKBOX_AUTO_CHAIN_ORDER, gDataManager->GetSysString(1276).data());
menuHandler.MakeElementSynchronized(gSettings.chkAutoChainOrder);
defaultStrings.emplace_back(gSettings.chkAutoChainOrder, 1276);
gSettings.chkDottedLines = env->addCheckBox(gGameConfig->dotted_lines, GetNextRect(), sPanel, CHECKBOX_DOTTED_LINES, gDataManager->GetSysString(1376).data());
defaultStrings.emplace_back(gSettings.chkDottedLines, 1376);
#if (IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR==9)
{
const auto type = driver->getDriverType();
if(type == irr::video::EDT_OGLES1 || type == irr::video::EDT_OGLES2) {
gSettings.chkDottedLines->setEnabled(false);
gSettings.chkDottedLines->setChecked(false);
gGameConfig->dotted_lines = false;
}
}
#endif
gSettings.chkMAutoPos = env->addCheckBox(gGameConfig->chkMAutoPos, GetNextRect(), sPanel, -1, gDataManager->GetSysString(1274).data());
defaultStrings.emplace_back(gSettings.chkMAutoPos, 1274);
gSettings.chkSTAutoPos = env->addCheckBox(gGameConfig->chkSTAutoPos, GetNextRect(), sPanel, -1, gDataManager->GetSysString(1278).data());
defaultStrings.emplace_back(gSettings.chkSTAutoPos, 1278);
{
gSettings.chkRandomPos = env->addCheckBox(gGameConfig->chkRandomPos, GetCurrentRectWithXOffset(35, 320), sPanel, -1, gDataManager->GetSysString(1275).data());
defaultStrings.emplace_back(gSettings.chkRandomPos, 1275);
IncrementXorY();
}
gSettings.chkNoChainDelay = env->addCheckBox(gGameConfig->chkWaitChain, GetNextRect(), sPanel, CHECKBOX_NO_CHAIN_DELAY, gDataManager->GetSysString(1277).data());
menuHandler.MakeElementSynchronized(gSettings.chkNoChainDelay);
defaultStrings.emplace_back(gSettings.chkNoChainDelay, 1277);
gSettings.chkAutoRPS = env->addCheckBox(gGameConfig->chkAutoRPS, GetNextRect(), sPanel, CHECKBOX_AUTO_RPS, gDataManager->GetSysString(12124).data());
defaultStrings.emplace_back(gSettings.chkAutoRPS, 12124);
}
{
gSettings.sound.construct(env, gSettings.tabcontrolwindow, gDataManager->GetSysString(2090).data());
defaultStrings.emplace_back(gSettings.sound.tab, 2090);
ResetXandY();
auto* sPanel = gSettings.sound.panel->getSubpanel();
if constexpr(SoundManager::HasMultipleBackends()) {
gSettings.stAudioBackend = env->addStaticText(gDataManager->GetSysString(12125).data(), GetCurrentRectWithXOffset(15, 115), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stAudioBackend, 12125);
gSettings.cbAudioBackend = AddComboBox(env, GetCurrentRectWithXOffset(120, 320), sPanel, -1);
int selected_backend = 0;
for(const auto& backend : SoundManager::GetSupportedBackends()) {
if(auto idx = gSettings.cbAudioBackend->addItem(epro::to_wstring(backend).data(), backend); backend == gGameConfig->sound_backend) {
selected_backend = idx;
}
}
gSettings.cbAudioBackend->setSelected(selected_backend);
cur_y += y_incr;
}
gSettings.chkEnableSound = env->addCheckBox(gGameConfig->enablesound, GetNextRect(), sPanel, CHECKBOX_ENABLE_SOUND, gDataManager->GetSysString(2047).data());
menuHandler.MakeElementSynchronized(gSettings.chkEnableSound);
defaultStrings.emplace_back(gSettings.chkEnableSound, 2047);
{
gSettings.stSoundVolume = env->addStaticText(gDataManager->GetSysString(2049).data(), GetCurrentRectWithXOffset(15, 75), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stSoundVolume, 2049);
gSettings.scrSoundVolume = env->addScrollBar(true, GetCurrentRectWithXOffset(80, 320, true), sPanel, SCROLL_SOUND_VOLUME);
menuHandler.MakeElementSynchronized(gSettings.scrSoundVolume);
gSettings.scrSoundVolume->setMax(100);
gSettings.scrSoundVolume->setMin(0);
gSettings.scrSoundVolume->setPos(gGameConfig->soundVolume);
gSettings.scrSoundVolume->setLargeStep(1);
gSettings.scrSoundVolume->setSmallStep(1);
IncrementXorY();
}
gSettings.chkEnableMusic = env->addCheckBox(gGameConfig->enablemusic, GetNextRect(), sPanel, CHECKBOX_ENABLE_MUSIC, gDataManager->GetSysString(2046).data());
menuHandler.MakeElementSynchronized(gSettings.chkEnableMusic);
defaultStrings.emplace_back(gSettings.chkEnableMusic, 2046);
{
gSettings.stMusicVolume = env->addStaticText(gDataManager->GetSysString(2048).data(), GetCurrentRectWithXOffset(15, 75), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stMusicVolume, 2048);
gSettings.scrMusicVolume = env->addScrollBar(true, GetCurrentRectWithXOffset(80, 320, true), sPanel, SCROLL_MUSIC_VOLUME);
menuHandler.MakeElementSynchronized(gSettings.scrMusicVolume);
gSettings.scrMusicVolume->setMax(100);
gSettings.scrMusicVolume->setMin(0);
gSettings.scrMusicVolume->setPos(gGameConfig->musicVolume);
gSettings.scrMusicVolume->setLargeStep(1);
gSettings.scrMusicVolume->setSmallStep(1);
IncrementXorY();
}
gSettings.chkLoopMusic = env->addCheckBox(gGameConfig->loopMusic, GetNextRect(), sPanel, CHECKBOX_LOOP_MUSIC, gDataManager->GetSysString(2079).data());
defaultStrings.emplace_back(gSettings.chkLoopMusic, 2079);
gSettings.stNoAudioBackend = env->addStaticText(gDataManager->GetSysString(2058).data(), GetCurrentRectWithXOffset(15, 320), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stNoAudioBackend, 2058);
gSettings.stNoAudioBackend->setVisible(false);
}
{
gSettings.graphics.construct(env, gSettings.tabcontrolwindow, gDataManager->GetSysString(2091).data());
defaultStrings.emplace_back(gSettings.graphics.tab, 2091);
ResetXandY();
auto* sPanel = gSettings.graphics.panel->getSubpanel();
gSettings.chkScaleBackground = env->addCheckBox(gGameConfig->scale_background, GetNextRect(), sPanel, CHECKBOX_SCALE_BACKGROUND, gDataManager->GetSysString(2061).data());
defaultStrings.emplace_back(gSettings.chkScaleBackground, 2061);
gSettings.chkAccurateBackgroundResize = env->addCheckBox(gGameConfig->accurate_bg_resize, GetNextRect(), sPanel, CHECKBOX_ACCURATE_BACKGROUND_RESIZE, gDataManager->GetSysString(2062).data());
defaultStrings.emplace_back(gSettings.chkAccurateBackgroundResize, 2062);
gSettings.chkDrawFieldSpells = env->addCheckBox(gGameConfig->draw_field_spell, GetNextRect(), sPanel, CHECKBOX_DRAW_FIELD_SPELLS, gDataManager->GetSysString(2068).data());
defaultStrings.emplace_back(gSettings.chkDrawFieldSpells, 2068);
{
gSettings.stAntiAlias = env->addStaticText(gDataManager->GetSysString(2075).data(), GetCurrentRectWithXOffset(15, 220), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stAntiAlias, 2075);
gSettings.ebAntiAlias = env->addEditBox(WStr(gGameConfig->antialias), GetCurrentRectWithXOffset(225, 320), true, sPanel, EDITBOX_NUMERIC);
IncrementXorY();
}
{
gSettings.stVSync = env->addStaticText(gDataManager->GetSysString(2073).data(), GetCurrentRectWithXOffset(15, 105), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stVSync, 2073);
gSettings.cbVSync = AddComboBox(env, GetCurrentRectWithXOffset(110, 320), sPanel, COMBOBOX_VSYNC);
ReloadCBVsync();
gSettings.cbVSync->setSelected(gGameConfig->vsync);
IncrementXorY();
}
{
gSettings.stFPSCap = env->addStaticText(gDataManager->GetSysString(2074).data(), GetCurrentRectWithXOffset(15, 220), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stFPSCap, 2074);
gSettings.ebFPSCap = env->addEditBox(WStr(gGameConfig->maxFPS), GetCurrentRectWithXOffset(225, 275), true, sPanel, EDITBOX_FPS_CAP);
gSettings.btnFPSCap = env->addButton(GetCurrentRectWithXOffset(280, 320), sPanel, BUTTON_FPS_CAP, gDataManager->GetSysString(1211).data());
defaultStrings.emplace_back(gSettings.btnFPSCap, 1211);
IncrementXorY();
}
{
gSettings.stVideoDriver = env->addStaticText(gDataManager->GetSysString(2096).data(), GetCurrentRectWithXOffset(15, 105), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stVideoDriver, 2096);
gSettings.cbVideoDriver = AddComboBox(env, GetCurrentRectWithXOffset(110, 320), sPanel, -1);
int selected_driver = 0;
for(const auto& cur_driver : supported_graphic_drivers) {
const auto idx = gSettings.cbVideoDriver->addItem(cur_driver.first.data(), cur_driver.second);
if(cur_driver.second == gGameConfig->driver_type)
selected_driver = idx;
}
gSettings.cbVideoDriver->setSelected(selected_driver);
IncrementXorY();
}
}
{
gSettings.system.construct(env, gSettings.tabcontrolwindow, gDataManager->GetSysString(2092).data());
defaultStrings.emplace_back(gSettings.system.tab, 2092);
ResetXandY();
auto* sPanel = gSettings.system.panel->getSubpanel();
gSettings.chkFullscreen = env->addCheckBox(gGameConfig->fullscreen, GetNextRect(), sPanel, CHECKBOX_FULLSCREEN, gDataManager->GetSysString(2060).data());
defaultStrings.emplace_back(gSettings.chkFullscreen, 2060);
#if EDOPRO_ANDROID || EDOPRO_IOS
gSettings.chkFullscreen->setChecked(true);
gSettings.chkFullscreen->setEnabled(false);
#elif EDOPRO_MACOS
gSettings.chkFullscreen->setEnabled(false);
#endif
gSettings.chkShowConsole = env->addCheckBox(gGameConfig->showConsole, GetNextRect(), sPanel, -1, gDataManager->GetSysString(2072).data());
defaultStrings.emplace_back(gSettings.chkShowConsole, 2072);
#if !EDOPRO_WINDOWS
gSettings.chkShowConsole->setChecked(false);
gSettings.chkShowConsole->setEnabled(false);
#endif
{
gSettings.stCoreLogOutput = env->addStaticText(gDataManager->GetSysString(1998).data(), GetCurrentRectWithXOffset(15, 105), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stCoreLogOutput, 1998);
gSettings.cbCoreLogOutput = AddComboBox(env, GetCurrentRectWithXOffset(110, 320), sPanel, COMBOBOX_CORE_LOG_OUTPUT);
ReloadCBCoreLogOutput();
IncrementXorY();
}
{
gSettings.stMaxImagesPerFrame = env->addStaticText(gDataManager->GetSysString(2097).data(), GetCurrentRectWithXOffset(15, 270), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stMaxImagesPerFrame, 2097);
gSettings.ebMaxImagesPerFrame = env->addEditBox(WStr(gGameConfig->maxImagesPerFrame), GetCurrentRectWithXOffset(275, 320), true, sPanel, EDITBOX_NUMERIC);
IncrementXorY();
}
{
gSettings.stImageLoadThreads = env->addStaticText(gDataManager->GetSysString(2098).data(), GetCurrentRectWithXOffset(15, 270), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stImageLoadThreads, 2098);
gSettings.ebImageLoadThreads = env->addEditBox(WStr(gGameConfig->imageLoadThreads), GetCurrentRectWithXOffset(275, 320), true, sPanel, EDITBOX_NUMERIC);
IncrementXorY();
}
{
gSettings.stImageDownloadThreads = env->addStaticText(gDataManager->GetSysString(2099).data(), GetCurrentRectWithXOffset(15, 270), false, true, sPanel);
defaultStrings.emplace_back(gSettings.stImageDownloadThreads, 2099);
gSettings.ebImageDownloadThreads = env->addEditBox(WStr(gGameConfig->imageDownloadThreads), GetCurrentRectWithXOffset(275, 320), true, sPanel, EDITBOX_NUMERIC);
IncrementXorY();
}
#ifdef DISCORD_APP_ID
gSettings.chkDiscordIntegration = env->addCheckBox(gGameConfig->discordIntegration, GetNextRect(), sPanel, CHECKBOX_DISCORD_INTEGRATION, gDataManager->GetSysString(2078).data());
defaultStrings.emplace_back(gSettings.chkDiscordIntegration, 2078);
gSettings.chkDiscordIntegration->setEnabled(discord.IsInitialized());
#endif
gSettings.chkLogDownloadErrors = env->addCheckBox(gGameConfig->logDownloadErrors, GetNextRect(), sPanel, CHECKBOX_LOG_DOWNLOAD_ERRORS, gDataManager->GetSysString(12100).data());
defaultStrings.emplace_back(gSettings.chkLogDownloadErrors, 12100);
#if EDOPRO_MACOS && (IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR==9)
gSettings.chkIntegratedGPU = env->addCheckBox(gGameConfig->useIntegratedGpu, GetNextRect(), sPanel, -1, gDataManager->GetSysString(12101).data());
defaultStrings.emplace_back(gSettings.chkIntegratedGPU, 12101);
#endif
#if EDOPRO_ANDROID
gSettings.chkNativeKeyboard = env->addCheckBox(gGameConfig->native_keyboard, GetNextRect(), sPanel, CHECKBOX_NATIVE_KEYBOARD, gDataManager->GetSysString(12102).data());
defaultStrings.emplace_back(gSettings.chkNativeKeyboard, 12102);
gSettings.chkNativeMouse = env->addCheckBox(gGameConfig->native_mouse, GetNextRect(), sPanel, CHECKBOX_NATIVE_MOUSE, gDataManager->GetSysString(12103).data());
defaultStrings.emplace_back(gSettings.chkNativeMouse, 12103);
#endif
}
}
#undef WStr
static inline irr::core::matrix4 BuildProjectionMatrix(irr::f32 left, irr::f32 right, irr::f32 ratio = 1.f) {
irr::core::matrix4 mProjection;
mProjection.buildProjectionMatrixPerspectiveLH((right - left) * ratio, CAMERA_TOP - CAMERA_BOTTOM, 1.0f, 100.0f);
mProjection[8] = (CAMERA_LEFT + CAMERA_RIGHT) / (CAMERA_LEFT - CAMERA_RIGHT);
mProjection[9] = (CAMERA_TOP + CAMERA_BOTTOM) / (CAMERA_BOTTOM - CAMERA_TOP);
return mProjection;
}
irr::core::vector3df getTarget() {
return { FIELD_X, 0.f, 0.f };
}
irr::core::vector3df getPosition() {
if(gGameConfig->topdown_view)
return { FIELD_X, 0.f, FIELD_Z * 1.4f };
return { FIELD_X, FIELD_Y, FIELD_Z };
}
irr::core::vector3df getUpVector() {
if(gGameConfig->topdown_view)
return { 0.f, -1.f, 0.f };
return { 0.f, 0.f, 1.f };
}
static const auto defaultProjection = BuildProjectionMatrix(CAMERA_LEFT, CAMERA_RIGHT);
bool Game::MainLoop() {
irr::core::matrix4 mProjection;
camera = smgr->addCameraSceneNode(0);
auto UpdateAspectRatio = [this]() {
if(!gGameConfig->keep_aspect_ratio) {
camera->setProjectionMatrix(defaultProjection);
return;
}
const float ratio = ((float)window_size.Width / (float)window_size.Height);
camera->setProjectionMatrix(BuildProjectionMatrix(CAMERA_BOTTOM, CAMERA_TOP, ratio));
};
auto UpdateCameraPosition = [this] {
camera->setPosition(getPosition());
camera->setUpVector(getUpVector());
if(dInfo.isInDuel)
dField.RefreshAllCards();
};
UpdateAspectRatio();
current_topdown = gGameConfig->topdown_view;
current_keep_aspect_ratio = gGameConfig->keep_aspect_ratio;
camera->setTarget(irr::core::vector3df(FIELD_X, 0, 0));
UpdateCameraPosition();
smgr->setAmbientLight(irr::video::SColorf(1.0f, 1.0f, 1.0f));
float atkframe = 0.1f;
#if EDOPRO_LINUX
bool last_resize = false;
irr::core::dimension2d<irr::u32> prev_window_size;
#endif
const irr::ITimer* timer = device->getTimer();
uint32_t cur_time = 0;
uint32_t prev_time = timer->getRealTime();
float frame_counter = 0.0f;
int fps = 0;
bool was_connected = false;
bool update_prompted = false;
bool update_checked = false;
#if (IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR==9)
if(auto driver_type = driver->getDriverType(); driver_type == irr::video::EDT_OGLES1 || driver_type == irr::video::EDT_OGLES2) {
auto SetClamp = [](irr::video::SMaterialLayer layer[irr::video::MATERIAL_MAX_TEXTURES]) {
layer[0].TextureWrapU = irr::video::ETC_CLAMP_TO_EDGE;
layer[0].TextureWrapV = irr::video::ETC_CLAMP_TO_EDGE;
};
SetClamp(matManager.mCard.TextureLayer);
SetClamp(matManager.mTexture.TextureLayer);
SetClamp(matManager.mBackLine.TextureLayer);
SetClamp(matManager.mSelField.TextureLayer);
SetClamp(matManager.mLinkedField.TextureLayer);
SetClamp(matManager.mMutualLinkedField.TextureLayer);
SetClamp(matManager.mOutLine.TextureLayer);
SetClamp(matManager.mTRTexture.TextureLayer);
SetClamp(matManager.mATK.TextureLayer);
SetClamp(matManager.mCard.TextureLayer);
}
#endif
if (gGameConfig->fullscreen) {
// Synchronize actual fullscreen state with config struct
bool currentlyFullscreen = false;
GUIUtils::ToggleFullscreen(device.get(), currentlyFullscreen);
}
while(!restart && device->run()) {
DispatchQueue();
if(should_reload_skin) {
should_reload_skin = false;
if(Utils::ToPathString(gSettings.cbCurrentSkin->getItem(gSettings.cbCurrentSkin->getSelected())) != gGameConfig->skin) {
gGameConfig->skin = Utils::ToPathString(gSettings.cbCurrentSkin->getItem(gSettings.cbCurrentSkin->getSelected()));
ApplySkin(gGameConfig->skin);
} else {
ApplySkin(EPRO_TEXT(""), true);
}
}
ParseGithubRepositories(gRepoManager->GetReadyRepos());
if(ServerLobby::HasRefreshedRooms())
ServerLobby::FillOnlineRooms();
#ifdef YGOPRO_BUILD_DLL
if(!dInfo.isStarted) {
LoadCoreFromRepos();
}
#endif //YGOPRO_BUILD_DLL
for(auto& repo : gRepoManager->GetRepoStatus()) {
repoInfoGui[repo.first].progress1->setProgress(repo.second);
repoInfoGui[repo.first].progress2->setProgress(repo.second);
}
gSoundManager->Tick();
fps++;
auto now = timer->getRealTime();
delta_time = now - prev_time;
prev_time = now;
cur_time += delta_time;
gJWrapper->ProcessEvents();
bool resized = false;
auto size = driver->getScreenSize();
#if EDOPRO_LINUX
prev_window_size = std::exchange(window_size, size);
if(prev_window_size != window_size && !last_resize && prev_window_size.Width != 0 && prev_window_size.Height != 0) {
last_resize = true;
} else if((prev_window_size == window_size && last_resize) || (prev_window_size.Width == 0 && prev_window_size.Height == 0)) {
last_resize = false;
#else
if(window_size != size) {
#endif
resized = true;
window_size = size;
window_scale.X = (window_size.Width / 1024.0) / gGameConfig->dpi_scale;
window_scale.Y = (window_size.Height / 640.0) / gGameConfig->dpi_scale;
cardimagetextureloading = false;
UpdateAspectRatio();
should_refresh_hands = true;
OnResize();
}
frame_counter += (float)delta_time * 60.0f/1000.0f;
float remainder;
frame_counter = std::modf(frame_counter, &remainder);
delta_frames = std::round(remainder);
for(uint32_t i = 0; i < delta_frames; i++){
linePatternD3D = (linePatternD3D + 1) % 30;
linePatternGL = (linePatternGL << 1) | (linePatternGL >> 15);
}
atkframe += 0.1f * (float)delta_time * 60.0f / 1000.0f;
atkdy = (float)sin(atkframe);
driver->beginScene(true, true, irr::video::SColor(0, 0, 0, 0));
gMutex.lock();
if(dInfo.isInDuel) {
if(dInfo.isReplay)
discord.UpdatePresence(DiscordWrapper::REPLAY);
else if(dInfo.isHandTest)
discord.UpdatePresence(DiscordWrapper::HAND_TEST);
else if(dInfo.isSingleMode)
discord.UpdatePresence(DiscordWrapper::PUZZLE);
else {
if(dInfo.isStarted)
discord.UpdatePresence(DiscordWrapper::DUEL_STARTED);
else
discord.UpdatePresence(DiscordWrapper::DUEL);
}
if (showcardcode == 1 || showcardcode == 3)
gSoundManager->PlayBGM(SoundManager::BGM::WIN, gGameConfig->loopMusic);
else if (showcardcode == 2)
gSoundManager->PlayBGM(SoundManager::BGM::LOSE, gGameConfig->loopMusic);
else if (dInfo.lp[0] > 0 && dInfo.lp[0] <= dInfo.lp[1] / 2)
gSoundManager->PlayBGM(SoundManager::BGM::DISADVANTAGE, gGameConfig->loopMusic);
else if (dInfo.lp[0] > 0 && dInfo.lp[0] >= dInfo.lp[1] * 2)
gSoundManager->PlayBGM(SoundManager::BGM::ADVANTAGE, gGameConfig->loopMusic);
else
gSoundManager->PlayBGM(SoundManager::BGM::DUEL, gGameConfig->loopMusic);
auto bg_texture = imageManager.tBackGround;
if(current_topdown && imageManager.tBackGround_duel_topdown)
bg_texture = imageManager.tBackGround_duel_topdown;
DrawBackImage(bg_texture, resized);
DrawBackGround();
DrawCards();
DrawMisc();
smgr->drawAll();
driver->setMaterial(irr::video::IdentityMaterial);
ClearZBuffer(driver);//Without this, "animations" are drawn behind everything
} else if(is_building) {
if(is_siding)
discord.UpdatePresence(DiscordWrapper::DECK_SIDING);
else
discord.UpdatePresence(DiscordWrapper::DECK);
gSoundManager->PlayBGM(SoundManager::BGM::DECK, gGameConfig->loopMusic);
DrawBackImage(imageManager.tBackGround_deck ? imageManager.tBackGround_deck : imageManager.tBackGround, resized);
DrawDeckBd();
} else {
if(dInfo.isInLobby)
discord.UpdatePresence(DiscordWrapper::IN_LOBBY);
else
discord.UpdatePresence(DiscordWrapper::MENU);
gSoundManager->PlayBGM(SoundManager::BGM::MENU, gGameConfig->loopMusic);
DrawBackImage(imageManager.tBackGround_menu ? imageManager.tBackGround_menu : imageManager.tBackGround, resized);
}
if(current_topdown != gGameConfig->topdown_view || current_keep_aspect_ratio != gGameConfig->keep_aspect_ratio) {
if(std::exchange(gGameConfig->topdown_view, current_topdown) != gGameConfig->topdown_view)
UpdateCameraPosition();
if(std::exchange(gGameConfig->keep_aspect_ratio, current_keep_aspect_ratio) != gGameConfig->keep_aspect_ratio) {
UpdateAspectRatio();
ResizePhaseButtons();
should_refresh_hands = true;
}
} else if(should_refresh_hands && dInfo.isInDuel) {
should_refresh_hands = false;
dField.RefreshHandHitboxes();
}
#if !EDOPRO_ANDROID
// text width is actual size, other pixels are relative to the assumed 1024x640
// so we recompensate for the scale factor and window resizing
int fpsCounterWidth = fpsCounter->getTextWidth() / (dpi_scale * window_scale.X);
#else
int fpsCounterWidth = fpsCounter->getTextWidth() / (dpi_scale * window_scale.X) + 20; // corner may be curved
#endif
if (is_building || is_siding) {
fpsCounter->setRelativePosition(Resize(205, CARD_IMG_HEIGHT + 1, 300, CARD_IMG_HEIGHT + 21));
} else if (wChat->isVisible()) { // Move it above the chat box
fpsCounter->setRelativePosition(Resize(1020 - fpsCounterWidth, 595, 1020, 615));
} else { // bottom right of window with a little padding
fpsCounter->setRelativePosition(Resize(1024 - fpsCounterWidth, 620, 1024, 640));
}
wBtnSettings->setVisible(!(is_building || is_siding || dInfo.isInDuel || open_file));
DrawGUI();
DrawSpec();
if(cardimagetextureloading) {
ShowCardInfo(showingcard);
}
if(signalFrame > 0) {
uint32_t movetime = std::min(delta_time, signalFrame);
signalFrame -= movetime;
if(!signalFrame)
frameSignal.Set();
}
if(waitFrame >= 0.0f) {
waitFrame += (float)delta_time * 60.0f / 1000.0f;
if((int)std::round(waitFrame) % 90 == 0) {
stHintMsg->setText(gDataManager->GetSysString(1390).data());
} else if((int)std::round(waitFrame) % 90 == 30) {
stHintMsg->setText(gDataManager->GetSysString(1391).data());
} else if((int)std::round(waitFrame) % 90 == 60) {
stHintMsg->setText(gDataManager->GetSysString(1392).data());
}
}
driver->endScene();
gMutex.unlock();
if(closeDuelWindow)
CloseDuelWindow();
if (DuelClient::try_needed) {
DuelClient::try_needed = false;
DuelClient::StartClient(DuelClient::temp_ip, DuelClient::temp_port, dInfo.secret.game_id, false);
}
{
std::lock_guard<epro::mutex> lk(popupCheck);
if(queued_msg.size()) {
env->addMessageBox(queued_caption.data(), queued_msg.data());
queued_msg.clear();
queued_caption.clear();
}
}
{
std::lock_guard<epro::mutex> lk(progressStatusLock);
if(progressStatus.newFile) {
updateProgressText->setText(progressStatus.progressText.data());
updateProgressTop->setVisible(!progressStatus.subProgressText.empty());
updateSubprogressText->setText(progressStatus.subProgressText.data());
}
updateProgressTop->setProgress(progressStatus.progressTop);
updateProgressBottom->setProgress(progressStatus.progressBottom);
}
discord.Check();
if(discord.IsConnected() && !was_connected) {
was_connected = true;
env->setFocus(stACMessage);
stACMessage->setText(gDataManager->GetSysString(1437).data());
PopupElement(wACMessage, 30);
} else if(!discord.IsConnected() && was_connected) {
was_connected = false;
env->setFocus(stACMessage);
stACMessage->setText(gDataManager->GetSysString(1438).data());
PopupElement(wACMessage, 30);
}
if(!wQuery->isVisible()) {
if(!update_prompted && gClientUpdater->HasUpdate() && !(dInfo.isInDuel || dInfo.isInLobby || is_siding
|| wRoomListPlaceholder->isVisible() || wLanWindow->isVisible()
|| wCreateHost->isVisible() || wHostPrepare->isVisible())) {
std::lock_guard<epro::mutex> lock(gMutex);
menuHandler.prev_operation = ACTION_UPDATE_PROMPT;
stQMessage->setText(epro::format(L"{}\n{}", gDataManager->GetSysString(1460), gDataManager->GetSysString(1461)).data());
SetCentered(wQuery);
PopupElement(wQuery);
update_prompted = true;
} else if (show_changelog) {
std::lock_guard<epro::mutex> lock(gMutex);
menuHandler.prev_operation = ACTION_SHOW_CHANGELOG;
stQMessage->setText(gDataManager->GetSysString(1451).data());
SetCentered(wQuery);
PopupElement(wQuery);
show_changelog = false;
}
else if(needs_to_acknowledge_discord_host) {
std::lock_guard<epro::mutex> lock(gMutex);
menuHandler.prev_operation = ACTION_ACKNOWLEDGE_HOST;
stQMessage->setText(epro::format(gDataManager->GetSysString(1468), dInfo.secret.host.address, dInfo.secret.host.port).data());
SetCentered(wQuery);
PopupElement(wQuery);
needs_to_acknowledge_discord_host = false;
}
#if EDOPRO_LINUX && (IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR==9)
else if(gGameConfig->useWayland == 2) {
std::lock_guard<epro::mutex> lock(gMutex);
menuHandler.prev_operation = ACTION_TRY_WAYLAND;
stQMessage->setText(L"Do you want to try the new native wayland backend?\nIf you're having issues after enabling it manually change the useWayland option in your system.conf file.");
SetCentered(wQuery);
PopupElement(wQuery);
show_changelog = false;
}
#endif
}
#if EDOPRO_MACOS && (IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR==9)
if(!wMessage->isVisible() && gGameConfig->useIntegratedGpu == 2) {
std::lock_guard<epro::mutex> lock(gMutex);
gGameConfig->useIntegratedGpu = 1;
SaveConfig();
stMessage->setText(L"The game is using the integrated gpu, if you want it to use the dedicated one change it from the settings.");
PopupElement(wMessage);
}
#endif
if(!update_checked && gClientUpdater->UpdateDownloaded()) {
if(gClientUpdater->UpdateFailed()) {
update_checked = true;
HideElement(updateWindow);
stMessage->setText(gDataManager->GetSysString(1467).data());
PopupElement(wMessage);
} else {
update_checked = true;
gClientUpdater->StartUnzipper(Game::UpdateUnzipBar, mainGame);
}
}
#if EDOPRO_MACOS
// Vsync is a lost cause on MacOS, emulate it by hadrcoding to 60 fps
int fpsLimit = gGameConfig->vsync ? 60 / gGameConfig->vsync : gGameConfig->maxFPS;
if(fpsLimit > 0) {
#else
int fpsLimit = gGameConfig->maxFPS;
if(gGameConfig->maxFPS > 0 && !gGameConfig->vsync) {
#endif
int64_t delta = std::round(fps * (1000.0f / fpsLimit) - cur_time);
if(delta > 0) {
int64_t t = timer->getRealTime();
while((timer->getRealTime() - t) < delta) {
epro::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
}
while(cur_time >= 1000) {
fpsCounter->setText(epro::format(gDataManager->GetSysString(1444), fps).data());
fps = 0;
cur_time -= 1000;
if(dInfo.time_player == 0 || dInfo.time_player == 1)
if(dInfo.time_left[dInfo.time_player])
dInfo.time_left[dInfo.time_player]--;
}
if(gGameConfig->maxFPS != -1)
epro::this_thread::sleep_for(std::chrono::milliseconds(1));
}
discord.UpdatePresence(DiscordWrapper::TERMINATE);
{
std::lock_guard<epro::mutex> lk(gMutex);
replaySignal.SetNoWait(true);
actionSignal.SetNoWait(true);
closeDoneSignal.SetNoWait(true);
frameSignal.SetNoWait(true);
}
DuelClient::StopClient(true);
//This is set again as waitable in the above call
frameSignal.SetNoWait(true);
SingleMode::StopPlay(true);
ReplayMode::StopReplay(true);
ClearTextures();
SaveConfig();
//device->drop();
return restart;
}
bool Game::ApplySkin(const epro::path_string& skinname, bool reload, bool firstrun) {
static epro::path_string prev_skin = EPRO_TEXT("");
bool applied = true;
auto reapply_colors = [&] () {
wCardImg->setBackgroundColor(skin::CARDINFO_IMAGE_BACKGROUND_VAL);
stInfo->setOverrideColor(skin::CARDINFO_TYPES_COLOR_VAL);
stDataInfo->setOverrideColor(skin::CARDINFO_STATS_COLOR_VAL);
stSetName->setOverrideColor(skin::CARDINFO_ARCHETYPE_TEXT_COLOR_VAL);
stPasscodeScope->setOverrideColor(skin::CARDINFO_PASSCODE_SCOPE_TEXT_COLOR_VAL);
stACMessage->setBackgroundColor(skin::DUELFIELD_ANNOUNCE_TEXT_BACKGROUND_COLOR_VAL);
auto tmp_color = skin::DUELFIELD_ANNOUNCE_TEXT_COLOR_VAL;
if(tmp_color != 0) {
stACMessage->setOverrideColor(tmp_color);
} else {
stACMessage->enableOverrideColor(false);
}
stHintMsg->setBackgroundColor(skin::DUELFIELD_TOOLTIP_TEXT_BACKGROUND_COLOR_VAL);
tmp_color = skin::DUELFIELD_TOOLTIP_TEXT_COLOR_VAL;
if(tmp_color != 0) {
stHintMsg->setOverrideColor(tmp_color);
} else {
stHintMsg->enableOverrideColor(false);
}
stTip->setBackgroundColor(skin::DUELFIELD_TOOLTIP_TEXT_BACKGROUND_COLOR_VAL);
tmp_color = skin::DUELFIELD_TOOLTIP_TEXT_COLOR_VAL;
if(tmp_color != 0)
stTip->setOverrideColor(tmp_color);
stCardListTip->setBackgroundColor(skin::DUELFIELD_TOOLTIP_TEXT_BACKGROUND_COLOR_VAL);
if(tmp_color != 0)
stCardListTip->setOverrideColor(tmp_color);
stCardListTip->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER);
auto roomlistcolor = skin::ROOMLIST_TEXTS_COLOR_VAL;
stVersus->setOverrideColor(roomlistcolor);
stBestof->setOverrideColor(roomlistcolor);
ebRoomNameText->setOverrideColor(roomlistcolor);
((irr::gui::CGUICustomCheckBox*)chkShowPassword)->setColor(roomlistcolor);
((irr::gui::CGUICustomCheckBox*)chkShowActiveRooms)->setColor(roomlistcolor);
fpsCounter->setOverrideColor(skin::FPS_TEXT_COLOR_VAL);
for(auto& repo : repoInfoGui) {
repo.second.progress1->setColors(skin::PROGRESSBAR_FILL_COLOR_VAL, skin::PROGRESSBAR_EMPTY_COLOR_VAL);
repo.second.progress2->setColors(skin::PROGRESSBAR_FILL_COLOR_VAL, skin::PROGRESSBAR_EMPTY_COLOR_VAL);
}
updateProgressTop->setColors(skin::PROGRESSBAR_FILL_COLOR_VAL, skin::PROGRESSBAR_EMPTY_COLOR_VAL);
updateProgressBottom->setColors(skin::PROGRESSBAR_FILL_COLOR_VAL, skin::PROGRESSBAR_EMPTY_COLOR_VAL);
btnPSAD->setImage(imageManager.tCover[0]);
btnPSDD->setImage(imageManager.tCover[0]);
btnSettings->setImage(imageManager.tSettings);
btnHand[0]->setImage(imageManager.tHand[0]);
btnHand[1]->setImage(imageManager.tHand[1]);
btnHand[2]->setImage(imageManager.tHand[2]);
};
if(!skinSystem || ((skinname == prev_skin || (reload && prev_skin == EPRO_TEXT(""))) && !firstrun))
return false;
if(!reload)
prev_skin = skinname;
if(prev_skin == NoSkinLabel()) {
auto skin = env->createSkin(irr::gui::EGST_WINDOWS_METALLIC);
env->setSkin(skin);
skin->drop();
skin::ResetDefaults();
imageManager.ResetTextures();
} else {
if(skinSystem->applySkin(prev_skin.data())) {
#define CLR(val1,val2,val3,val4) irr::video::SColor(val1,val2,val3,val4)
#define DECLR(what,val) skin::what##_VAL = skinSystem->getCustomColor(skin::what,val);
#include "custom_skin_enum.inl"
#undef DECLR
#undef CLR
imageManager.ChangeTextures(epro::format(EPRO_TEXT("./skin/{}/textures/"), prev_skin));
} else {
if(firstrun)
return false;
applied = false;
auto skin = env->createSkin(irr::gui::EGST_WINDOWS_METALLIC);
env->setSkin(skin);
skin->drop();
skin::ResetDefaults();
imageManager.ResetTextures();
}
}
auto skin = env->getSkin();
skin->setFont(guiFont);
#define SKIN_SCALE(elem)skin->setSize(elem, Scale(skin->getSize(elem)));
skin->setSize(irr::gui::EGDS_SCROLLBAR_SIZE, Scale(20));
SKIN_SCALE(irr::gui::EGDS_MENU_HEIGHT)
SKIN_SCALE(irr::gui::EGDS_WINDOW_BUTTON_WIDTH)
SKIN_SCALE(irr::gui::EGDS_CHECK_BOX_WIDTH)
SKIN_SCALE(irr::gui::EGDS_BUTTON_WIDTH)
SKIN_SCALE(irr::gui::EGDS_BUTTON_HEIGHT)
SKIN_SCALE(irr::gui::EGDS_TITLEBARTEXT_DISTANCE_X)
SKIN_SCALE(irr::gui::EGDS_TITLEBARTEXT_DISTANCE_Y)
SKIN_SCALE(irr::gui::EGDS_TEXT_DISTANCE_X)
SKIN_SCALE(irr::gui::EGDS_TEXT_DISTANCE_Y)
SKIN_SCALE(irr::gui::EGDS_MESSAGE_BOX_GAP_SPACE)
#undef SKIN_SCALE
if(wInfos) {
wInfos->setTabHeight(skin->getSize(irr::gui::EGDS_BUTTON_HEIGHT) + Scale(2));
wInfos->setTabVerticalAlignment(irr::gui::EGUIA_UPPERLEFT);
}
if(prev_skin == NoSkinLabel()){
for (int i = 0; i < irr::gui::EGDC_COUNT; ++i) {
irr::video::SColor col = skin->getColor((irr::gui::EGUI_DEFAULT_COLOR)i);
col.setAlpha(224);
skin->setColor((irr::gui::EGUI_DEFAULT_COLOR)i, col);
}
}
if(!firstrun)
reapply_colors();
if(wAbout)
wAbout->setRelativePosition(irr::core::recti(0, 0, std::min(Scale(450), stAbout->getTextWidth() + Scale(20)), std::min(stAbout->getTextHeight() + Scale(40), Scale(700))));
if(dpi_scale > 1.5f) {
auto* sprite_texture = imageManager.GetCheckboxScaledTexture(dpi_scale);
if(sprite_texture) {
auto* sprites = skin->getSpriteBank();
auto sprite_id = sprites->addTextureAsSprite(sprite_texture);
if(sprite_id != -1)
skin->setIcon(irr::gui::EGDI_CHECK_BOX_CHECKED, sprite_id);
}
}
return applied;
}
void Game::RefreshDeck(irr::gui::IGUIComboBox* cbDeck) {
cbDeck->clear();
for(auto& file : Utils::FindFiles(DeckManager::GetDeckFolder(), { EPRO_TEXT("ydk") })) {
file.erase(file.size() - 4);
cbDeck->addItem(Utils::ToUnicodeIfNeeded(file).data());
}
for(irr::u32 i = 0; i < cbDeck->getItemCount(); ++i) {
if(gGameConfig->lastdeck == cbDeck->getItem(i)) {
cbDeck->setSelected(static_cast<irr::s32>(i));
break;
}
}
}
void Game::RefreshLFLists() {
cbHostLFList->clear();
cbHostLFList->setSelected(0);
cbDBLFList->clear();
cbDBLFList->setSelected(0);
auto prevFilter = std::max(0, cbFilterBanlist->getSelected());
cbFilterBanlist->clear();
cbFilterBanlist->addItem(epro::format(L"[{}]", gDataManager->GetSysString(1226)).data());
for (auto &list : gdeckManager->_lfList) {
auto hostIndex = cbHostLFList->addItem(list.listName.data(), list.hash);
auto deckIndex = cbDBLFList->addItem(list.listName.data(), list.hash);
cbFilterBanlist->addItem(list.listName.data(), list.hash);
if (gGameConfig->lastlflist == list.hash) {
cbHostLFList->setSelected(hostIndex);
cbDBLFList->setSelected(deckIndex);
}
}
deckBuilder.filterList = &gdeckManager->_lfList[cbDBLFList->getSelected()];
cbFilterBanlist->setSelected(prevFilter);
}
void Game::RefreshAiDecks() {
gBot.bots.clear();
FileStream windbots{ EPRO_TEXT("./WindBot/bots.json"), FileStream::in };
if (windbots.good()) {
nlohmann::json j;
try {
windbots >> j;
}
catch(const std::exception& e) {
ErrorLog("Failed to load WindBot Ignite config json: {}", e.what());
}
if(j.is_array()) {
#if EDOPRO_LINUX || EDOPRO_MACOS
{
auto it = gGameConfig->user_configs.find("posixPathExtension");
if(it != gGameConfig->user_configs.end() && it->is_string()) {
WindBot::executablePath = it->get<epro::path_string>();
} else if((it = gGameConfig->configs.find("posixPathExtension")) != gGameConfig->configs.end()
&& it->is_string()) {
WindBot::executablePath = it->get<epro::path_string>();
}
}
#endif
WindBot generic_engine_bot;
for(auto& obj : j) {
try {
WindBot bot;
bot.name = BufferIO::DecodeUTF8(obj.at("name").get_ref<std::string&>());
bot.deck = BufferIO::DecodeUTF8(obj.at("deck").get_ref<std::string&>());
bot.deckfile = epro::format(L"AI_{}", bot.deck);
bot.difficulty = obj.at("difficulty").get<int>();
for(auto& masterRule : obj.at("masterRules")) {
if(masterRule.is_number()) {
bot.masterRules.insert(masterRule.get<int>());
}
}
bool is_generic_engine = bot.deck == L"Lucky";
if(is_generic_engine)
generic_engine_bot = bot;
else
gBot.bots.push_back(std::move(bot));
}
catch(const std::exception& e) {
ErrorLog("Failed to parse WindBot Ignite config json entry: {}", e.what());
}
}
if(generic_engine_bot.deck.size()) {
gBot.bots.push_back(std::move(generic_engine_bot));
gBot.genericEngine = &gBot.bots.back();
}
}
} else {
ErrorLog("Failed to open WindBot Ignite config json!");
}
}
void Game::RefreshReplay() {
lstReplayList->resetPath();
}
void Game::RefreshSingleplay() {
lstSinglePlayList->resetPath();
}
template<typename T>
inline void TrySaveInt(T& dest, const irr::gui::IGUIElement* src) {
try {
dest = static_cast<T>(std::stoul(src->getText()));
}
catch (...) {}
}
void Game::SaveConfig() {
gGameConfig->nickname = ebNickName->getText();
gGameConfig->lastallowedcards = cbRule->getSelected();
gGameConfig->lastDuelParam = duel_param;
gGameConfig->lastExtraRules = extra_rules;
gGameConfig->lastDuelForbidden = forbiddentypes;
TrySaveInt(gGameConfig->timeLimit, ebTimeLimit);
TrySaveInt(gGameConfig->team1count, ebTeam1);
TrySaveInt(gGameConfig->team2count, ebTeam2);
TrySaveInt(gGameConfig->bestOf, ebBestOf);
TrySaveInt(gGameConfig->startLP, ebStartLP);
TrySaveInt(gGameConfig->startHand, ebStartHand);
TrySaveInt(gGameConfig->drawCount, ebDrawCount);
TrySaveInt(gGameConfig->minMainDeckSize, ebMainMin);
TrySaveInt(gGameConfig->maxMainDeckSize, ebMainMax);
TrySaveInt(gGameConfig->minExtraDeckSize, ebExtraMin);
TrySaveInt(gGameConfig->maxExtraDeckSize, ebExtraMax);
TrySaveInt(gGameConfig->minSideDeckSize, ebSideMin);
TrySaveInt(gGameConfig->maxSideDeckSize, ebSideMax);
TrySaveInt(gGameConfig->antialias, gSettings.ebAntiAlias);
gGameConfig->showConsole = gSettings.chkShowConsole->isChecked();
gGameConfig->relayDuel = btnRelayMode->isPressed();
gGameConfig->noCheckDeckContent = chkNoCheckDeckContent->isChecked();
gGameConfig->noCheckDeckSize = chkNoCheckDeckSize->isChecked();
gGameConfig->noShuffleDeck = chkNoShuffleDeck->isChecked();
gGameConfig->botThrowRock = gBot.chkThrowRock->isChecked();
gGameConfig->botMute = gBot.chkMute->isChecked();
auto lastServerIndex = serverChoice->getSelected();
if(lastServerIndex >= 0)
gGameConfig->lastServer = serverChoice->getItem(lastServerIndex);
gGameConfig->chkMAutoPos = gSettings.chkMAutoPos->isChecked();
gGameConfig->chkSTAutoPos = gSettings.chkSTAutoPos->isChecked();
gGameConfig->chkRandomPos = gSettings.chkRandomPos->isChecked();
gGameConfig->chkAutoChain = gSettings.chkAutoChainOrder->isChecked();
gGameConfig->chkWaitChain = gSettings.chkNoChainDelay->isChecked();
gGameConfig->chkAutoRPS = gSettings.chkAutoRPS->isChecked();
gGameConfig->chkIgnore1 = gSettings.chkIgnoreOpponents->isChecked();
gGameConfig->chkIgnore2 = gSettings.chkIgnoreSpectators->isChecked();
gGameConfig->chkHideHintButton = gSettings.chkHideChainButtons->isChecked();
gGameConfig->chkAnime = chkAnime->isChecked();
#ifdef UPDATE_URL
gGameConfig->noClientUpdates = gSettings.chkUpdates->isChecked();
#endif
TrySaveInt(gGameConfig->maxImagesPerFrame, gSettings.ebMaxImagesPerFrame);
TrySaveInt(gGameConfig->imageLoadThreads, gSettings.ebImageLoadThreads);
TrySaveInt(gGameConfig->imageDownloadThreads, gSettings.ebImageDownloadThreads);
#if EDOPRO_MACOS && (IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR==9)
gGameConfig->useIntegratedGpu = gSettings.chkIntegratedGPU->isChecked();
#endif
gGameConfig->driver_type = static_cast<irr::video::E_DRIVER_TYPE>(gSettings.cbVideoDriver->getItemData(gSettings.cbVideoDriver->getSelected()));
if constexpr(SoundManager::HasMultipleBackends()) {
gGameConfig->sound_backend = static_cast<SoundManager::BACKEND>(gSettings.cbAudioBackend->getItemData(gSettings.cbAudioBackend->getSelected()));
}
#if EDOPRO_ANDROID
if(gGameConfig->Save(epro::format("{}/system.conf", porting::internal_storage))) {
Utils::FileCopy(epro::format("{}/system.conf", porting::internal_storage), EPRO_TEXT("./config/system.conf"));
return;
}
#endif
gGameConfig->Save(EPRO_TEXT("./config/system.conf"));
}
Game::RepoGui* Game::AddGithubRepositoryStatusWindow(const GitRepo* repo) {
std::wstring name = BufferIO::DecodeUTF8(repo->repo_name);
auto a = env->addWindow(Scale(0, 0, 470, 55), false, L"", mRepositoriesInfo);
a->getCloseButton()->setVisible(false);
a->setDraggable(false);
a->setDrawTitlebar(false);
a->setDrawBackground(false);
env->addStaticText(name.data(), Scale(5, 5, 90 + 295, 20 + 5), false, false, a);
auto& grepo = repoInfoGui[repo->repo_path];
grepo.progress1 = new IProgressBar(env, Scale(5, 20 + 15, 170 + 295, 20 + 30), -1, a);
grepo.progress1->addBorder(1);
grepo.progress1->setColors(skin::PROGRESSBAR_FILL_COLOR_VAL, skin::PROGRESSBAR_EMPTY_COLOR_VAL);
grepo.progress1->drop();
((irr::gui::CGUICustomContextMenu*)mRepositoriesInfo)->addItem(a, -1);
grepo.history_button1 = env->addButton(Scale(90 + 295, 0, 170 + 295, 20 + 5), a, BUTTON_REPO_CHANGELOG, gDataManager->GetSysString(1443).data());
defaultStrings.emplace_back(grepo.history_button1, 1443);
grepo.history_button1->setEnabled(repo->ready);
auto b = env->addWindow(Scale(0, 0, 10000, 55), false, L"", tabRepositories);
b->getCloseButton()->setVisible(false);
b->setDraggable(false);
b->setDrawTitlebar(false);
b->setDrawBackground(false);
env->addStaticText(name.data(), Scale(5, 5, 300, 20 + 5), false, false, b);
grepo.progress2 = new IProgressBar(env, Scale(5, 20 + 15, 300 - 5, 20 + 30), -1, b);
grepo.progress2->addBorder(1);
grepo.progress2->setColors(skin::PROGRESSBAR_FILL_COLOR_VAL, skin::PROGRESSBAR_EMPTY_COLOR_VAL);
grepo.progress2->drop();
((irr::gui::CGUICustomContextMenu*)mTabRepositories)->addItem(b, -1);
grepo.history_button2 = env->addButton(Scale(200, 5, 300 - 5, 20 + 10), b, BUTTON_REPO_CHANGELOG, gDataManager->GetSysString(1443).data());
defaultStrings.emplace_back(grepo.history_button2, 1443);
grepo.history_button2->setEnabled(repo->ready);
return &grepo;
}
void Game::LoadGithubRepositories() {
bool update_ready = true;
for(const auto& repo : gRepoManager->GetAllRepos()) {
auto grepo = AddGithubRepositoryStatusWindow(repo);
if(repo->ready && update_ready) {
UpdateRepoInfo(repo, grepo);
if(repo->is_language) {
auto lang = Utils::ToPathString(repo->language);
const auto find_pred = [&lang](const locale_entry_t& locale) {
return locale.first == lang;
};
const auto it = std::find_if(locales.begin(), locales.end(), find_pred);
if(it != locales.end())
it->second.push_back(Utils::ToPathString(repo->data_path));
}
} else {
update_ready = false;
}
}
if(gRepoManager->IsReadOnly())
ParseGithubRepositories(gRepoManager->GetReadyRepos());
}
void Game::ParseGithubRepositories(const std::vector<const GitRepo*>& repos) {
if(repos.empty())
return;
bool refresh_db = false;
for(auto& repo : repos) {
auto grepo = &repoInfoGui[repo->repo_path];
UpdateRepoInfo(repo, grepo);
auto data_path = Utils::ToPathString(repo->data_path);
auto files = Utils::FindFiles(data_path, { EPRO_TEXT("cdb") }, 0);
if(!repo->is_language) {
for(auto& file : files) {
const auto db_path = data_path + file;
if(gDataManager->LoadDB(db_path)) {
WindBot::AddDatabase(db_path);
refresh_db = true;
}
}
gDataManager->LoadStrings(data_path + EPRO_TEXT("strings.conf"));
refresh_db = gDataManager->LoadIdsMapping(data_path + EPRO_TEXT("mappings.json")) || refresh_db;
} else {
if(Utils::ToUTF8IfNeeded(gGameConfig->locale) == repo->language) {
for(auto& file : files)
refresh_db = gDataManager->LoadLocaleDB(data_path + file) || refresh_db;
gDataManager->LoadLocaleStrings(data_path + EPRO_TEXT("strings.conf"));
}
auto langpath = Utils::ToPathString(repo->language);
auto lang = Utils::ToUpperNoAccents(langpath);
auto it = std::find_if(locales.begin(), locales.end(),
[&lang](const auto& locale) {
return Utils::ToUpperNoAccents(locale.first) == lang;
});
if(it != locales.end()) {
it->second.push_back(std::move(data_path));
} else {
Utils::MakeDirectory(EPRO_TEXT("./config/languages/") + langpath);
locales.emplace_back(std::move(langpath), std::vector<epro::path_string>{ std::move(data_path) });
gSettings.cbCurrentLocale->addItem(BufferIO::DecodeUTF8(repo->language).data());
}
}
}
if(refresh_db && is_building) {
if(!is_siding)
deckBuilder.RefreshCurrentDeck();
if(deckBuilder.results.size())
deckBuilder.StartFilter(true);
}
if(gRepoManager->GetUpdatingReposNumber() == 0) {
gdeckManager->StopDummyLoading();
ReloadElementsStrings();
}
}
void Game::UpdateRepoInfo(const GitRepo* repo, RepoGui* grepo) {
if(repo->history.error.size()) {
ErrorLog("The repo {} couldn't be cloned", repo->url);
ErrorLog("Error: {}", repo->history.error);
grepo->history_button1->setText(gDataManager->GetSysString(1434).data());
defaultStrings.emplace_back(grepo->history_button1, 1434);
grepo->history_button1->setEnabled(true);
grepo->history_button2->setText(gDataManager->GetSysString(1434).data());
defaultStrings.emplace_back(grepo->history_button2, 1434);
grepo->history_button2->setEnabled(true);
auto error_string = repo->not_git_repo ? epro::format(gDataManager->GetSysString(1452), BufferIO::DecodeUTF8(repo->repo_path)) :
epro::format(gDataManager->GetSysString(1435), BufferIO::DecodeUTF8(repo->url));
grepo->commit_history_full = epro::format(L"{}\n{}",
error_string,
epro::format(gDataManager->GetSysString(1436), BufferIO::DecodeUTF8(repo->history.error))
);
grepo->commit_history_partial = grepo->commit_history_full;
return;
}
auto get_text = [](const std::vector<std::string>& history) {
std::string text;
std::for_each(history.begin(), history.end(), [&text](const std::string& n) { if(n.size()) { text.append(n).append(2, '\n'); }});
if(text.size())
text.erase(text.size() - 2, 2);
return BufferIO::DecodeUTF8(text);
};
grepo->commit_history_full = get_text(repo->history.full_history);
if(repo->history.partial_history.size()) {
if(repo->history.partial_history.front() == repo->history.full_history.front() && repo->history.partial_history.back() == repo->history.full_history.back()) {
grepo->commit_history_partial = grepo->commit_history_full;
} else {
grepo->commit_history_partial = get_text(repo->history.partial_history);
}
} else {
if(repo->history.warning.size()) {
grepo->history_button1->setText(gDataManager->GetSysString(1448).data());
defaultStrings.emplace_back(grepo->history_button1, 1448);
grepo->history_button2->setText(gDataManager->GetSysString(1448).data());
defaultStrings.emplace_back(grepo->history_button2, 1448);
grepo->commit_history_partial = epro::format(L"{}\n{}\n\n{}",
gDataManager->GetSysString(1449),
gDataManager->GetSysString(1450),
BufferIO::DecodeUTF8(repo->history.warning));
} else {
grepo->commit_history_partial.assign(gDataManager->GetSysString(1446).data(), gDataManager->GetSysString(1446).size());
}
}
grepo->history_button1->setEnabled(true);
grepo->history_button2->setEnabled(true);
if(!repo->is_language) {
script_dirs.insert(script_dirs.begin(), Utils::ToPathString(repo->script_path));
auto init_script = epro::format(EPRO_TEXT("{}{}"), script_dirs.front(), EPRO_TEXT("init.lua"));
if(Utils::FileExists(init_script))
init_scripts.push_back(std::move(init_script));
auto script_subdirs = Utils::FindSubfolders(Utils::ToPathString(repo->script_path), 2);
script_dirs.insert(script_dirs.begin(), std::make_move_iterator(script_subdirs.begin()), std::make_move_iterator(script_subdirs.end()));
pic_dirs.insert(pic_dirs.begin(), Utils::ToPathString(repo->pics_path));
if(repo->has_core)
cores_to_load.insert(cores_to_load.begin(), Utils::ToPathString(repo->core_path));
auto data_path = Utils::ToPathString(repo->data_path);
auto lflist_path = Utils::ToPathString(repo->lflist_path);
if(gdeckManager->LoadLFListSingle(data_path + EPRO_TEXT("lflist.conf")) || gdeckManager->LoadLFListFolder(lflist_path)) {
gdeckManager->RefreshLFList();
RefreshLFLists();
}
}
}
void Game::LoadServers() {
for(auto& _config : { &gGameConfig->user_configs, &gGameConfig->configs }) {
auto& config = *_config;
auto it = config.find("servers");
if(it != config.end() && it->is_array()) {
for(auto& obj : *it) {
try {
ServerInfo tmp_server{};
tmp_server.name = BufferIO::DecodeUTF8(obj.at("name").get_ref<std::string&>());
tmp_server.address = obj.at("address").get<std::string>();
tmp_server.roomaddress = obj.at("roomaddress").get<std::string>();
tmp_server.roomlistport = obj.at("roomlistport").get<uint16_t>();
tmp_server.duelport = obj.at("duelport").get<uint16_t>();
{
auto protocolIt = obj.find("roomlistprotocol");
if(protocolIt != obj.end() && protocolIt->is_string()) {
tmp_server.protocol = ServerInfo::GetProtocol(protocolIt->get_ref<std::string&>());
}
}
int i = serverChoice->addItem(tmp_server.name.data());
if(gGameConfig->lastServer == tmp_server.name)
serverChoice->setSelected(i);
ServerLobby::serversVector.push_back(std::move(tmp_server));
}
catch(const std::exception& e) {
ErrorLog("Exception occurred while parsing server entry: {}", e.what());
}
}
}
}
}
void Game::ShowCardInfo(uint32_t code, bool resize, imgType type) {
static auto prevtype = imgType::ART;
if(resize) {
//Update the text fields beforehand when resizing so that their horizontal size
//is correct when the text is set and is then broken into pieces
const auto widthRect = irr::core::recti(Scale(15), 0, Scale(287 * window_scale.X), 10);
stInfo->setRelativePosition(widthRect);
stDataInfo->setRelativePosition(widthRect);
stSetName->setRelativePosition(widthRect);
stPasscodeScope->setRelativePosition(widthRect);
stText->setRelativePosition(widthRect);
}
if(code == 0) {
ClearCardInfo(0);
return;
}
auto cd = gDataManager->GetCardData(code);
if(!cd)
ClearCardInfo(0);
bool only_texture = !cd;
if(showingcard == code) {
if(!resize && !cardimagetextureloading)
return;
only_texture = only_texture || !resize;
}
int shouldrefresh = -1;
auto img = imageManager.GetTextureCard(code, resize ? prevtype : type, false, true, &shouldrefresh);
cardimagetextureloading = false;
if(shouldrefresh == 2)
cardimagetextureloading = true;
imgCard->setImage(img);
showingcard = code;
if(only_texture)
return;
auto tmp_code = code;
if(cd->IsInArtworkOffsetRange())
tmp_code = cd->alias;
stName->setText(gDataManager->GetName(tmp_code).data());
stPasscodeScope->setText(epro::format(L"[{:08}] {}", tmp_code, gDataManager->FormatScope(cd->ot)).data());
stSetName->setText(L"");
auto setcodes = cd->setcodes;
if (cd->alias) {
auto data = gDataManager->GetCardData(cd->alias);
if(data)
setcodes = data->setcodes;
}
if (setcodes.size()) {
stSetName->setText(epro::format(L"{}{}", gDataManager->GetSysString(1329), gDataManager->FormatSetName(setcodes)).data());
}
if(cd->type & TYPE_MONSTER) {
stInfo->setText(epro::format(L"[{}] {} {}", gDataManager->FormatType(cd->type), gDataManager->FormatAttribute(cd->attribute), gDataManager->FormatRace(cd->race)).data());
std::wstring text;
if(cd->type & TYPE_LINK){
if(cd->attack < 0)
text.append(epro::format(L"?/LINK {} ", cd->level));
else
text.append(epro::format(L"{}/LINK {} ", cd->attack, cd->level));
text.append(gDataManager->FormatLinkMarker(cd->link_marker));
} else {
text.append(epro::format(L"[{}{}] ", (cd->type & TYPE_XYZ) ? L"\u2606" : L"\u2605", cd->level));
if (cd->attack < 0 && cd->defense < 0)
text.append(L"?/?");
else if (cd->attack < 0)
text.append(epro::format(L"?/{}", cd->defense));
else if (cd->defense < 0)
text.append(epro::format(L"{}/?", cd->attack));
else
text.append(epro::format(L"{}/{}", cd->attack, cd->defense));
}
if(cd->type & TYPE_PENDULUM) {
text.append(epro::format(L" {}/{}", cd->lscale, cd->rscale));
}
stDataInfo->setText(text.data());
} else {
if(cd->type & TYPE_SKILL) { // TYPE_SKILL created by hints
// Hack: Race encodes the character for now
stInfo->setText(epro::format(L"[{}|{}]", gDataManager->FormatRace(cd->race, true), gDataManager->FormatType(cd->type)).data());
} else {
stInfo->setText(epro::format(L"[{}]", gDataManager->FormatType(cd->type)).data());
}
if(cd->type & TYPE_LINK) {
stDataInfo->setText(epro::format(L"LINK {} {}", cd->level, gDataManager->FormatLinkMarker(cd->link_marker)).data());
} else
stDataInfo->setText(L"");
}
RefreshCardInfoTextPositions();
stText->setText(gDataManager->GetText(code).data());
}
void Game::RefreshCardInfoTextPositions() {
const int xLeft = Scale(15);
const int xRight = Scale(287 * window_scale.X);
int offset = Scale(37);
auto offsetIfVisibleWithContent = [&](irr::gui::IGUIStaticText* st) {
if (st->isVisible() && wcscmp(st->getText(), L"")) {
st->setRelativePosition(irr::core::recti(xLeft, offset, xRight, offset + st->getTextHeight()));
offset += st->getTextHeight();
}
};
offsetIfVisibleWithContent(stInfo);
offsetIfVisibleWithContent(stDataInfo);
offsetIfVisibleWithContent(stSetName);
offsetIfVisibleWithContent(stPasscodeScope);
stText->setRelativePosition(irr::core::recti(xLeft, offset, xRight, stText->getParent()->getAbsolutePosition().getHeight() - Scale(1)));
}
void Game::ClearCardInfo(int player) {
imgCard->setImage(imageManager.tCover[player]);
stName->setText(L"");
stInfo->setText(L"");
stDataInfo->setText(L"");
stSetName->setText(L"");
stPasscodeScope->setText(L"");
stText->setText(L"");
cardimagetextureloading = false;
showingcard = 0;
}
void Game::AddChatMsg(epro::wstringview msg, int player, int type) {
for(int i = 7; i > 0; --i) {
chatMsg[i].swap(chatMsg[i - 1]);
chatTiming[i] = chatTiming[i - 1];
chatType[i] = chatType[i - 1];
}
chatTiming[0] = 1200.0f;
chatType[0] = player;
epro::wstringview sender = L"";
if(type == 0) {
gSoundManager->PlaySoundEffect(SoundManager::SFX::CHAT);
sender = dInfo.selfnames[player];
} else if(type == 1) {
gSoundManager->PlaySoundEffect(SoundManager::SFX::CHAT);
sender = dInfo.opponames[player];
} else if(type == 2) {
switch(player) {
case 7: //local name
sender = ebNickName->getText();
break;
case 8: //system custom message, no prefix.
gSoundManager->PlaySoundEffect(SoundManager::SFX::CHAT);
sender = gDataManager->GetSysString(1439);
break;
case 9: //error message
sender = gDataManager->GetSysString(1440);
break;
default: //from watcher or unknown
if(player < 11 || player > 19)
sender = gDataManager->GetSysString(1441);
}
}
chatMsg[0] = epro::format(L"{}: {}", sender, msg);
lstChat->addItem(chatMsg[0].data());
}
void Game::AddChatMsg(epro::wstringview name, epro::wstringview msg, int type) {
for(int i = 7; i > 0; --i) {
chatMsg[i].swap(chatMsg[i - 1]);
chatTiming[i] = chatTiming[i - 1];
chatType[i] = chatType[i - 1];
}
chatTiming[0] = 1200.0f;
switch(type) {
case STOC_Chat2::PTYPE_DUELIST:
chatType[0] = 0;
break;
case STOC_Chat2::PTYPE_SYSTEM:
chatType[0] = 12;
break;
case STOC_Chat2::PTYPE_SYSTEM_SHOUT:
chatType[0] = 16;
break;
case STOC_Chat2::PTYPE_OBS:
case STOC_Chat2::PTYPE_SYSTEM_ERROR:
default:
chatType[0] = 11;
}
if(type == STOC_Chat2::PTYPE_DUELIST || type == STOC_Chat2::PTYPE_OBS)
chatMsg[0] = epro::format(L"{}: {}", name, msg);
else
chatMsg[0] = epro::format(L"System: {}", msg);
lstChat->addItem(chatMsg[0].data());
gSoundManager->PlaySoundEffect(SoundManager::SFX::CHAT);
}
void Game::AddLog(epro::wstringview msg, int param) {
logParam.push_back(param);
lstLog->addItem(msg.data());
if(!env->hasFocus(lstLog)) {
lstLog->setSelected(-1);
}
}
void Game::ClearChatMsg() {
for(int i = 7; i >= 0; --i) {
chatTiming[i] = 0;
}
}
void Game::AddDebugMsg(epro::stringview msg) {
if (gGameConfig->coreLogOutput & CORE_LOG_TO_CHAT)
AddChatMsg(BufferIO::DecodeUTF8(msg), 9, 2);
if (gGameConfig->coreLogOutput & CORE_LOG_TO_FILE)
ErrorLog("{}: {}", BufferIO::EncodeUTF8(gDataManager->GetSysString(1440)), msg);
}
void Game::ClearTextures() {
matManager.mCard.setTexture(0, 0);
imgCard->setImage(imageManager.tCover[0]);
btnPSAU->setImage();
btnPSDU->setImage();
for(int i=0; i<=4; ++i) {
btnCardSelect[i]->setImage();
btnCardDisplay[i]->setImage();
}
imageManager.ClearTexture();
}
void Game::CloseDuelWindow() {
for(auto wit = fadingList.begin(); wit != fadingList.end(); ++wit) {
if(wit->isFadein)
wit->autoFadeoutFrame = 1;
}
mTopMenu->setVisible(true);
wACMessage->setVisible(false);
wANAttribute->setVisible(false);
wANCard->setVisible(false);
wANNumber->setVisible(false);
wANRace->setVisible(false);
wCardImg->setVisible(false);
wCardSelect->setVisible(false);
wCardDisplay->setVisible(false);
wCmdMenu->setVisible(false);
wFTSelect->setVisible(false);
wHand->setVisible(false);
wInfos->setVisible(false);
wMessage->setVisible(false);
wOptions->setVisible(false);
wPhase->setVisible(false);
wPosSelect->setVisible(false);
wQuery->setVisible(false);
wReplayControl->setVisible(false);
wFileSave->setVisible(false);
stHintMsg->setVisible(false);
btnSideOK->setVisible(false);
btnSideShuffle->setVisible(false);
btnSideSort->setVisible(false);
btnSideReload->setVisible(false);
btnLeaveGame->setVisible(false);
btnRestartSingle->setVisible(false);
btnSpectatorSwap->setVisible(false);
btnChainIgnore->setVisible(false);
btnChainAlways->setVisible(false);
btnChainWhenAvail->setVisible(false);
btnCancelOrFinish->setVisible(false);
btnShuffle->setVisible(false);
wChat->setVisible(false);
lstLog->clear();
logParam.clear();
lstHostList->clear();
DuelClient::hosts.clear();
ClearTextures();
stName->setText(L"");
stInfo->setText(L"");
stDataInfo->setText(L"");
stSetName->setText(L"");
stPasscodeScope->setText(L"");
stText->setText(L"");
stTip->setText(L"");
cardimagetextureloading = false;
showingcard = 0;
closeDuelWindow = false;
closeDoneSignal.Set();
}
void Game::PopupMessage(epro::wstringview text, epro::wstringview caption) {
std::lock_guard<epro::mutex> lock(popupCheck);
queued_msg = text.data();
queued_caption = caption.data();
}
void Game::PopupSaveWindow(epro::wstringview caption, epro::wstringview text, epro::wstringview hint) {
wFileSave->setText(caption.data());
ebFileSaveName->setText(text.data());
stFileSaveHint->setText(hint.data());
PopupElement(wFileSave);
}
uint8_t Game::LocalPlayer(uint8_t player) {
return dInfo.isFirst ? player : 1 - player;
}
void Game::UpdateDuelParam() {
ReloadCBDuelRule();
uint64_t flag = 0;
for(auto i = 0u; i < sizeofarr(chkCustomRules); ++i) {
if(chkCustomRules[i]->isChecked()) {
if(i == 19)
flag |= DUEL_USE_TRAPS_IN_NEW_CHAIN;
else if(i == 20)
flag |= DUEL_6_STEP_BATLLE_STEP;
else if(i == 21)
flag |= DUEL_TRIGGER_WHEN_PRIVATE_KNOWLEDGE;
else if(i > 21)
flag |= 0x100ULL << (i - 3);
else
flag |= 0x100ULL << i;
}
}
constexpr uint32_t limits[] = { TYPE_FUSION, TYPE_SYNCHRO, TYPE_XYZ, TYPE_PENDULUM, TYPE_LINK };
uint32_t flag2 = 0;
for (auto i = 0u; i < sizeofarr(chkTypeLimit); ++i) {
if (chkTypeLimit[i]->isChecked()) {
flag2 |= limits[i];
}
}
switch (flag) {
case DUEL_MODE_SPEED: {
cbDuelRule->setSelected(5);
if(flag2 == DUEL_MODE_MR5_FORB) {
cbDuelRule->removeItem(8);
break;
}
}
[[fallthrough]];
case DUEL_MODE_RUSH: {
cbDuelRule->setSelected(6);
if(flag2 == DUEL_MODE_MR5_FORB) {
cbDuelRule->removeItem(8);
break;
}
}
[[fallthrough]];
case DUEL_MODE_GOAT: {
cbDuelRule->setSelected(7);
if(flag2 == DUEL_MODE_MR1_FORB) {
cbDuelRule->removeItem(8);
break;
}
}
[[fallthrough]];
default:
switch(flag & ~DUEL_TCG_SEGOC_NONPUBLIC) {
#define CHECK(MR) \
case DUEL_MODE_MR##MR:{ \
cbDuelRule->setSelected(MR - 1); \
if (flag2 == DUEL_MODE_MR##MR##_FORB) { \
cbDuelRule->removeItem(8); break; } \
} \
[[fallthrough]];
CHECK(1)
CHECK(2)
CHECK(3)
CHECK(4)
CHECK(5)
#undef CHECK
default: {
cbDuelRule->addItem(gDataManager->GetSysString(1630).data());
cbDuelRule->setSelected(8);
break;
}
}
break;
}
duel_param = flag;
forbiddentypes = flag2;
}
void Game::UpdateExtraRules(bool set) {
for(auto i = 0u; i < sizeofarr(chkRules); i++)
chkRules[i]->setEnabled(true);
if(set) {
for(auto flag = 1u, i = 0u; i < sizeofarr(chkRules); i++, flag = flag << 1) {
chkRules[i]->setChecked(extra_rules & flag);
}
return;
}
if(chkRules[0]->isChecked()) {
chkRules[1]->setEnabled(false);
chkRules[4]->setEnabled(false);
}
if(chkRules[1]->isChecked()) {
chkRules[0]->setEnabled(false);
chkRules[4]->setEnabled(false);
}
if(chkRules[4]->isChecked()) {
chkRules[0]->setEnabled(false);
chkRules[1]->setEnabled(false);
}
if(chkRules[5]->isChecked()) {
chkRules[6]->setEnabled(false);
chkRules[7]->setEnabled(false);
}
if(chkRules[6]->isChecked()) {
chkRules[5]->setEnabled(false);
chkRules[7]->setEnabled(false);
}
if(chkRules[7]->isChecked()) {
chkRules[5]->setEnabled(false);
chkRules[6]->setEnabled(false);
}
extra_rules = 0;
for(auto flag = 1u, i = 0u; i < sizeofarr(chkRules); i++, flag <<= 1) {
if(chkRules[i]->isChecked())
extra_rules |= flag;
}
}
int Game::GetMasterRule(uint64_t param, uint32_t forbidden, int* truerule) {
if(truerule)
*truerule = 0;
#define CHECK(MR) case DUEL_MODE_MR##MR:{ if (truerule && forbidden == DUEL_MODE_MR##MR##_FORB) *truerule = MR; break; }
switch(param) {
CHECK(1)
CHECK(2)
CHECK(3)
CHECK(4)
CHECK(5)
}
#undef CHECK
if (truerule && !*truerule)
*truerule = 6;
if ((param & DUEL_PZONE) && (param & DUEL_SEPARATE_PZONE) && (param & DUEL_EMZONE))
return 5;
else if(param & DUEL_EMZONE)
return 4;
else if ((param & DUEL_PZONE) && (param & DUEL_SEPARATE_PZONE))
return 3;
else
return 2;
}
void Game::ResizePhaseButtons() {
if(gGameConfig->alternative_phase_layout) {
wPhase->setRelativePosition(Resize(940, 80, 990, 340));
return;
} else if(!gGameConfig->keep_aspect_ratio) {
if(dInfo.HasFieldFlag(DUEL_3_COLUMNS_FIELD | DUEL_EMZONE))
wPhase->setRelativePosition(Resize(480, 290, 855, 350));
else
wPhase->setRelativePosition(Resize(480, 310, 855, 330));
return;
}
// do some random magic computation to get the buttons to align properly
constexpr irr::s32 DEFAULT_X1 = 480;
constexpr irr::s32 DEFAULT_X2 = 855;
constexpr irr::s32 DEFAULT_WIDTH = DEFAULT_X2 - DEFAULT_X1;
const auto ratio = (window_size.Height * 1.6f) / static_cast<float>(window_size.Width);
const auto total = DEFAULT_WIDTH * (ratio - 1.f) * window_scale.X;
const auto offx1 = (1.f / 1.85f) * total;
const auto offx2 = total - offx1;
irr::s32 x1 = static_cast<irr::s32>(std::round(DEFAULT_X1 * window_scale.X - offx1));
irr::s32 x2 = static_cast<irr::s32>(std::round(DEFAULT_X2 * window_scale.X + offx2));
irr::s32 y1, y2;
if(dInfo.HasFieldFlag(DUEL_3_COLUMNS_FIELD | DUEL_EMZONE)) {
y1 = static_cast<irr::s32>(std::round(290 * window_scale.Y));
y2 = static_cast<irr::s32>(std::round(350 * window_scale.Y));
} else {
y1 = static_cast<irr::s32>(std::round(310 * window_scale.Y));
y2 = static_cast<irr::s32>(std::round(330 * window_scale.Y));
}
wPhase->setRelativePosition(Scale(x1, y1, x2, y2));
}
void Game::SetPhaseButtons(bool visibility) {
if(visibility) {
btnDP->setVisible(gGameConfig->alternative_phase_layout || btnDP->isSubElement());
btnSP->setVisible(gGameConfig->alternative_phase_layout || btnSP->isSubElement());
btnM1->setVisible(gGameConfig->alternative_phase_layout || btnM1->isSubElement());
btnM2->setVisible(gGameConfig->alternative_phase_layout || btnM2->isSubElement());
btnBP->setVisible(gGameConfig->alternative_phase_layout || btnBP->isSubElement());
btnEP->setVisible(gGameConfig->alternative_phase_layout || btnEP->isSubElement());
}
// set the phase window to the "default" size so that it's easier to
// work with the relative button positions by using non scaled values
if(gGameConfig->alternative_phase_layout)
wPhase->setRelativePosition({ 940, 80, 990, 340 });
else if(dInfo.HasFieldFlag(DUEL_3_COLUMNS_FIELD | DUEL_EMZONE))
wPhase->setRelativePosition({ 480, 290, 855, 350 });
else
wPhase->setRelativePosition({ 480, 310, 855, 330 });
auto UpdatePhaseButtons = [&] {
if(gGameConfig->alternative_phase_layout) {
btnDP->setRelativePosition({ 0, 0, 50, 20 });
btnSP->setRelativePosition({ 0, 40, 50, 60 });
btnM1->setRelativePosition({ 0, 80, 50, 100 });
btnBP->setRelativePosition({ 0, 120, 50, 140 });
btnM2->setRelativePosition({ 0, 160, 50, 180 });
btnEP->setRelativePosition({ 0, 200, 50, 220 });
btnShuffle->setRelativePosition({ 0, 240, 50, 260 });
return;
}
// reset master rule 4 phase button position
if(dInfo.HasFieldFlag(DUEL_3_COLUMNS_FIELD)) {
if(dInfo.HasFieldFlag(DUEL_EMZONE)) {
btnShuffle->setRelativePosition({ 0, 40, 50, 60 });
btnDP->setRelativePosition({ 0, 40, 50, 60 });
btnSP->setRelativePosition({ 0, 40, 50, 60 });
btnM1->setRelativePosition({ 160, 20, 210, 40 });
btnBP->setRelativePosition({ 160, 20, 210, 40 });
btnM2->setRelativePosition({ 160, 20, 210, 40 });
btnEP->setRelativePosition({ 310, 0, 360, 20 });
return;
}
btnShuffle->setRelativePosition({ 65, 0, 115, 20 });
btnDP->setRelativePosition({ 65, 0, 115, 20 });
btnSP->setRelativePosition({ 65, 0, 115, 20 });
btnM1->setRelativePosition({ 130, 0, 180, 20 });
btnBP->setRelativePosition({ 195, 0, 245, 20 });
btnM2->setRelativePosition({ 195, 0, 245, 20 });
btnEP->setRelativePosition({ 260, 0, 310, 20 });
return;
}
btnDP->setRelativePosition({ 0, 0, 50, 20 });
btnEP->setRelativePosition({ 320, 0, 370, 20 });
btnShuffle->setRelativePosition({ 0, 0, 50, 20 });
if(dInfo.HasFieldFlag(DUEL_EMZONE)) {
btnSP->setRelativePosition({ 0, 0, 50, 20 });
btnM1->setRelativePosition({ 160, 0, 210, 20 });
btnBP->setRelativePosition({ 160, 0, 210, 20 });
btnM2->setRelativePosition({ 160, 0, 210, 20 });
return;
}
btnSP->setRelativePosition({ 65, 0, 115, 20 });
btnM1->setRelativePosition({ 130, 0, 180, 20 });
btnBP->setRelativePosition({ 195, 0, 245, 20 });
btnM2->setRelativePosition({ 260, 0, 310, 20 });
};
UpdatePhaseButtons();
ResizePhaseButtons();
}
void Game::SetMessageWindow() {
if(is_building || dInfo.isInDuel) {
wMessage->setRelativePosition(ResizeWin(490, 200, 840, 340));
wACMessage->setRelativePosition(ResizeWin(490, 240, 840, 300));
} else {
SetCentered(wMessage);
SetCentered(wACMessage);
}
}
void Game::ResizeCardinfoWindow(bool keep_ratio) {
const auto rect = keep_ratio ?
ResizeWithCappedWidth(1, 1, 1 + CARD_IMG_WRAPPER_WIDTH, 1 + CARD_IMG_WRAPPER_HEIGHT, CARD_IMG_WRAPPER_ASPECT_RATIO) :
Resize(1, 1, 1 + CARD_IMG_WIDTH + 20, 1 + CARD_IMG_HEIGHT + 18);
wCardImg->setRelativePosition(rect);
}
bool Game::HasFocus(irr::gui::EGUI_ELEMENT_TYPE type) const {
irr::gui::IGUIElement* focus = env->getFocus();
return focus && focus->hasType(type);
}
void Game::RefreshUICoreVersion() {
auto label = corename.length()
? epro::format(gDataManager->GetSysString(2013), ocgcore->ver_major, ocgcore->ver_minor, corename)
: epro::format(gDataManager->GetSysString(2010), ocgcore->ver_major, ocgcore->ver_minor);
stCoreVersion->setText(label.data());
auto w1 = stVersion->getTextWidth();
auto w2 = stCoreVersion->getTextWidth();
wVersion->setRelativePosition(irr::core::recti(0, 0, Scale(20) + std::max({ Scale(280), w1, w2 }), Scale(135)));
}
std::wstring Game::GetLocalizedExpectedCore() {
return epro::format(gDataManager->GetSysString(2011), Core::EXPECTED_VERSION_MAJOR, Core::EXPECTED_VERSION_MINOR);
}
std::wstring Game::GetLocalizedCompatVersion() {
return epro::format(gDataManager->GetSysString(2012), PRO_VERSION >> 12, (PRO_VERSION >> 4) & 0xff, PRO_VERSION & 0xf);
}
void Game::ReloadCBSortType() {
static constexpr std::array<std::pair<uint32_t, DeckBuilder::SORT_MODIFIER>, 6> items{{
{1370, DeckBuilder::SORT_MODIFIER_LEVEL_DESC},
{1371, DeckBuilder::SORT_MODIFIER_ATK_DESC},
{1372, DeckBuilder::SORT_MODIFIER_DEF_DESC},
{1373, DeckBuilder::SORT_MODIFIER_NAME_ASC},
{11374, DeckBuilder::SORT_MODIFIER_PASSCODE_DESC},
{11375, DeckBuilder::SORT_MODIFIER_PASSCODE_ASC},
}};
cbSortType->clear();
for(const auto& [stringid, val] : items) {
cbSortType->addItem(gDataManager->GetSysString(stringid).data(), val);
}
}
void Game::ReloadCBCardType() {
static constexpr std::array<std::pair<uint32_t, DeckBuilder::CARD_TYPE_FILTER>, 5> items{ {
{1310, DeckBuilder::CARD_TYPE_FILTER_ALL},
{1312, DeckBuilder::CARD_TYPE_FILTER_MONSTER},
{1313, DeckBuilder::CARD_TYPE_FILTER_SPELL},
{1314, DeckBuilder::CARD_TYPE_FILTER_TRAP},
{1077, DeckBuilder::CARD_TYPE_FILTER_SKILL},
} };
cbCardType->clear();
for(const auto& [stringid, val] : items) {
cbCardType->addItem(gDataManager->GetSysString(stringid).data(), val);
}
}
void Game::ReloadCBCardType2() {
cbCardType2->clear();
cbCardType2->setEnabled(true);
switch (cbCardType->getItemData(cbCardType->getSelected())) {
case DeckBuilder::CARD_TYPE_FILTER_ALL:
case DeckBuilder::CARD_TYPE_FILTER_SKILL:
cbCardType2->setEnabled(false);
cbCardType2->addItem(gDataManager->GetSysString(1310).data(), 0);
break;
case DeckBuilder::CARD_TYPE_FILTER_MONSTER:
cbCardType2->addItem(gDataManager->GetSysString(1080).data(), 0);
cbCardType2->addItem(gDataManager->GetSysString(1054).data(), TYPE_MONSTER + TYPE_NORMAL);
cbCardType2->addItem(gDataManager->GetSysString(1055).data(), TYPE_MONSTER + TYPE_EFFECT);
cbCardType2->addItem(gDataManager->GetSysString(1056).data(), TYPE_MONSTER + TYPE_FUSION);
cbCardType2->addItem(gDataManager->GetSysString(1057).data(), TYPE_MONSTER + TYPE_RITUAL);
cbCardType2->addItem(gDataManager->GetSysString(1063).data(), TYPE_MONSTER + TYPE_SYNCHRO);
cbCardType2->addItem(gDataManager->GetSysString(1073).data(), TYPE_MONSTER + TYPE_XYZ);
cbCardType2->addItem(gDataManager->GetSysString(1074).data(), TYPE_MONSTER + TYPE_PENDULUM);
cbCardType2->addItem(gDataManager->GetSysString(1076).data(), TYPE_MONSTER + TYPE_LINK);
cbCardType2->addItem(gDataManager->GetSysString(1075).data(), TYPE_MONSTER + TYPE_SPSUMMON);
cbCardType2->addItem(epro::format(L"{}|{}", gDataManager->GetSysString(1054), gDataManager->GetSysString(1062)).data(), TYPE_MONSTER + TYPE_NORMAL + TYPE_TUNER);
cbCardType2->addItem(epro::format(L"{}|{}", gDataManager->GetSysString(1054), gDataManager->GetSysString(1074)).data(), TYPE_MONSTER + TYPE_NORMAL + TYPE_PENDULUM);
cbCardType2->addItem(epro::format(L"{}|{}", gDataManager->GetSysString(1063), gDataManager->GetSysString(1062)).data(), TYPE_MONSTER + TYPE_SYNCHRO + TYPE_TUNER);
cbCardType2->addItem(gDataManager->GetSysString(1062).data(), TYPE_MONSTER + TYPE_TUNER);
cbCardType2->addItem(gDataManager->GetSysString(1061).data(), TYPE_MONSTER + TYPE_GEMINI);
cbCardType2->addItem(gDataManager->GetSysString(1060).data(), TYPE_MONSTER + TYPE_UNION);
cbCardType2->addItem(gDataManager->GetSysString(1059).data(), TYPE_MONSTER + TYPE_SPIRIT);
cbCardType2->addItem(gDataManager->GetSysString(1071).data(), TYPE_MONSTER + TYPE_FLIP);
cbCardType2->addItem(gDataManager->GetSysString(1072).data(), TYPE_MONSTER + TYPE_TOON);
cbCardType2->addItem(gDataManager->GetSysString(1065).data(), TYPE_MONSTER + TYPE_MAXIMUM);
break;
case DeckBuilder::CARD_TYPE_FILTER_SPELL:
cbCardType2->addItem(gDataManager->GetSysString(1080).data(), 0);
cbCardType2->addItem(gDataManager->GetSysString(1054).data(), TYPE_SPELL);
cbCardType2->addItem(gDataManager->GetSysString(1066).data(), TYPE_SPELL + TYPE_QUICKPLAY);
cbCardType2->addItem(gDataManager->GetSysString(1067).data(), TYPE_SPELL + TYPE_CONTINUOUS);
cbCardType2->addItem(gDataManager->GetSysString(1057).data(), TYPE_SPELL + TYPE_RITUAL);
cbCardType2->addItem(gDataManager->GetSysString(1068).data(), TYPE_SPELL + TYPE_EQUIP);
cbCardType2->addItem(gDataManager->GetSysString(1069).data(), TYPE_SPELL + TYPE_FIELD);
cbCardType2->addItem(gDataManager->GetSysString(1076).data(), TYPE_SPELL + TYPE_LINK);
break;
case DeckBuilder::CARD_TYPE_FILTER_TRAP:
cbCardType2->addItem(gDataManager->GetSysString(1080).data(), 0);
cbCardType2->addItem(gDataManager->GetSysString(1054).data(), TYPE_TRAP);
cbCardType2->addItem(gDataManager->GetSysString(1067).data(), TYPE_TRAP + TYPE_CONTINUOUS);
cbCardType2->addItem(gDataManager->GetSysString(1070).data(), TYPE_TRAP + TYPE_COUNTER);
break;
}
}
void Game::ReloadCBLimit() {
bool white = deckBuilder.filterList && deckBuilder.filterList->whitelist;
cbLimit->clear();
cbLimit->addItem(gDataManager->GetSysString(white ? 1269 : 1310).data(), DeckBuilder::LIMITATION_FILTER_NONE);
cbLimit->addItem(gDataManager->GetSysString(1316).data(), DeckBuilder::LIMITATION_FILTER_BANNED);
cbLimit->addItem(gDataManager->GetSysString(1317).data(), DeckBuilder::LIMITATION_FILTER_LIMITED);
cbLimit->addItem(gDataManager->GetSysString(1318).data(), DeckBuilder::LIMITATION_FILTER_SEMI_LIMITED);
cbLimit->addItem(gDataManager->GetSysString(1320).data(), DeckBuilder::LIMITATION_FILTER_UNLIMITED);
if(!white) {
chkAnime->setEnabled(true);
cbLimit->addItem(gDataManager->GetSysString(1900).data(), DeckBuilder::LIMITATION_FILTER_OCG);
cbLimit->addItem(gDataManager->GetSysString(1901).data(), DeckBuilder::LIMITATION_FILTER_TCG);
cbLimit->addItem(gDataManager->GetSysString(1902).data(), DeckBuilder::LIMITATION_FILTER_TCG_OCG);
cbLimit->addItem(gDataManager->GetSysString(1903).data(), DeckBuilder::LIMITATION_FILTER_PRERELEASE);
cbLimit->addItem(gDataManager->GetSysString(1910).data(), DeckBuilder::LIMITATION_FILTER_SPEED);
cbLimit->addItem(gDataManager->GetSysString(1911).data(), DeckBuilder::LIMITATION_FILTER_RUSH);
cbLimit->addItem(gDataManager->GetSysString(1912).data(), DeckBuilder::LIMITATION_FILTER_LEGEND);
if(chkAnime->isChecked()) {
cbLimit->addItem(gDataManager->GetSysString(1265).data(), DeckBuilder::LIMITATION_FILTER_ANIME);
cbLimit->addItem(gDataManager->GetSysString(1266).data(), DeckBuilder::LIMITATION_FILTER_ILLEGAL);
cbLimit->addItem(gDataManager->GetSysString(1267).data(), DeckBuilder::LIMITATION_FILTER_VIDEOGAME);
cbLimit->addItem(gDataManager->GetSysString(1268).data(), DeckBuilder::LIMITATION_FILTER_CUSTOM);
}
} else {
chkAnime->setEnabled(false);
cbLimit->addItem(gDataManager->GetSysString(1912).data(), DeckBuilder::LIMITATION_FILTER_LEGEND);
cbLimit->addItem(gDataManager->GetSysString(1266).data(), DeckBuilder::LIMITATION_FILTER_ILLEGAL);
cbLimit->addItem(gDataManager->GetSysString(1903).data(), DeckBuilder::LIMITATION_FILTER_PRERELEASE);
cbLimit->addItem(gDataManager->GetSysString(1310).data(), DeckBuilder::LIMITATION_FILTER_ALL);
}
}
void Game::ReloadCBAttribute() {
cbAttribute->clear();
cbAttribute->addItem(gDataManager->GetSysString(1310).data(), 0);
for (uint32_t filter = 0x1, i = 1010; filter <= ATTRIBUTE_DIVINE; filter <<= 1, i++)
cbAttribute->addItem(gDataManager->GetSysString(i).data(), filter);
}
void Game::ReloadCBRace() {
cbRace->clear();
cbRace->addItem(gDataManager->GetSysString(1310).data(), 0);
//currently corresponding to RACE_GALAXY
static constexpr auto CURRENTLY_KNOWN_RACES = 32;
uint32_t i = 0;
for(; i < CURRENTLY_KNOWN_RACES; ++i)
cbRace->addItem(gDataManager->GetSysString(gDataManager->GetRaceStringIndex(i)).data(), i + 1);
for(; i < 64; ++i) {
auto idx = gDataManager->GetRaceStringIndex(i);
if(gDataManager->HasSysString(idx))
cbRace->addItem(gDataManager->GetSysString(idx).data(), i + 1);
}
}
void Game::ReloadCBFilterRule() {
cbFilterRule->clear();
cbFilterRule->addItem(epro::format(L"[{}]", gDataManager->GetSysString(1225)).data());
for (auto i = 1900; i <= 1904; ++i)
cbFilterRule->addItem(gDataManager->GetSysString(i).data());
}
void Game::ReloadCBDuelRule(irr::gui::IGUIComboBox* cb) {
if (!cb) cb = cbDuelRule;
cb->clear();
cb->addItem(gDataManager->GetSysString(1260).data());
cb->addItem(gDataManager->GetSysString(1261).data());
cb->addItem(gDataManager->GetSysString(1262).data());
cb->addItem(gDataManager->GetSysString(1263).data());
cb->addItem(gDataManager->GetSysString(1264).data());
cb->addItem(gDataManager->GetSysString(1258).data());
cb->addItem(gDataManager->GetSysString(1259).data());
cb->addItem(gDataManager->GetSysString(1248).data());
}
void Game::ReloadCBRule() {
cbRule->clear();
for (auto i = 1900; i <= 1904; ++i)
cbRule->addItem(gDataManager->GetSysString(i).data());
}
void Game::ReloadCBCurrentSkin() {
gSettings.cbCurrentSkin->clear();
int selectedSkin = gSettings.cbCurrentSkin->addItem(gDataManager->GetSysString(2065).data());
for(const auto& skin : skinSystem->listSkins()) {
auto itemIndex = gSettings.cbCurrentSkin->addItem(Utils::ToUnicodeIfNeeded(skin).data());
if (gGameConfig->skin == skin)
selectedSkin = itemIndex;
}
gSettings.cbCurrentSkin->setSelected(selectedSkin);
}
void Game::ReloadCBCoreLogOutput() {
gSettings.cbCoreLogOutput->clear();
for (uint32_t i = CORE_LOG_NONE; i <= 3; i++) {
auto itemIndex = gSettings.cbCoreLogOutput->addItem(gDataManager->GetSysString(2000 + i).data(), i);
if (gGameConfig->coreLogOutput == i) {
gSettings.cbCoreLogOutput->setSelected(itemIndex);
}
}
}
void Game::ReloadCBVsync() {
gSettings.cbVSync->clear();
auto max = 12118;
#if (IRRLICHT_VERSION_MAJOR==1 && IRRLICHT_VERSION_MINOR==9) && EDOPRO_WINDOWS
const auto type = driver->getDriverType();
if(type == irr::video::EDT_DIRECT3D9 || type == irr::video::EDT_DIRECT3D9_ON_12)
#endif
max = 12115;
for(int i = 12114; i <= max; ++i)
gSettings.cbVSync->addItem(gDataManager->GetSysString(i).data());
}
void Game::ReloadElementsStrings() {
ShowCardInfo(showingcard, true);
for(auto& elem : defaultStrings) {
elem.first->setText(gDataManager->GetSysString(elem.second).data());
}
size_t nullLFlist = gdeckManager->_lfList.size() - 1;
gdeckManager->_lfList[nullLFlist].listName = gDataManager->GetSysString(1442).data();
auto prev = cbDBLFList->getSelected();
cbDBLFList->removeItem(static_cast<irr::u32>(nullLFlist));
cbDBLFList->addItem(gdeckManager->_lfList[nullLFlist].listName.data(), gdeckManager->_lfList[nullLFlist].hash);
cbDBLFList->setSelected(prev);
prev = cbHostLFList->getSelected();
cbHostLFList->removeItem(static_cast<irr::u32>(nullLFlist));
cbHostLFList->addItem(gdeckManager->_lfList[nullLFlist].listName.data(), gdeckManager->_lfList[nullLFlist].hash);
cbHostLFList->setSelected(prev);
prev = cbSortType->getSelected();
ReloadCBSortType();
cbSortType->setSelected(prev);
prev = cbCardType->getSelected();
ReloadCBCardType();
cbCardType->setSelected(prev);
prev = cbCardType2->getSelected();
ReloadCBCardType2();
cbCardType2->setSelected(prev);
prev = cbLimit->getSelected();
ReloadCBLimit();
cbLimit->setSelected(prev);
prev = cbAttribute->getSelected();
ReloadCBAttribute();
cbAttribute->setSelected(prev);
prev = cbRace->getSelected();
ReloadCBRace();
cbRace->setSelected(prev);
if(is_building) {
btnLeaveGame->setText(gDataManager->GetSysString(1306).data());
} else if(!dInfo.isReplay && !dInfo.isSingleMode && dInfo.player_type < (dInfo.team1 + dInfo.team2)) {
btnLeaveGame->setText(gDataManager->GetSysString(1351).data());
} else if(dInfo.player_type == 7) {
btnLeaveGame->setText(gDataManager->GetSysString(1350).data());
} else if(dInfo.isSingleMode) {
btnLeaveGame->setText(gDataManager->GetSysString(1210).data());
}
prev = cbFilterRule->getSelected();
ReloadCBFilterRule();
cbFilterRule->setSelected(prev);
prev = cbDuelRule->getSelected();
if (prev >= 7) {
UpdateDuelParam();
} else {
ReloadCBDuelRule();
cbDuelRule->setSelected(prev);
}
prev = cbHandTestDuelRule->getSelected();
ReloadCBDuelRule(cbHandTestDuelRule);
cbHandTestDuelRule->setSelected(prev);
prev = cbRule->getSelected();
ReloadCBRule();
cbRule->setSelected(prev);
prev = gSettings.cbCoreLogOutput->getSelected();
ReloadCBCoreLogOutput();
gSettings.cbCoreLogOutput->setSelected(prev);
prev = gSettings.cbVSync->getSelected();
ReloadCBVsync();
gSettings.cbVSync->setSelected(prev);
((irr::gui::CGUICustomTable*)roomListTable)->setColumnText(1, gDataManager->GetSysString(1225).data());
((irr::gui::CGUICustomTable*)roomListTable)->setColumnText(2, gDataManager->GetSysString(1227).data());
((irr::gui::CGUICustomTable*)roomListTable)->setColumnText(3, gDataManager->GetSysString(1236).data());
((irr::gui::CGUICustomTable*)roomListTable)->setColumnText(4, gDataManager->GetSysString(1226).data());
((irr::gui::CGUICustomTable*)roomListTable)->setColumnText(5, gDataManager->GetSysString(2030).data());
((irr::gui::CGUICustomTable*)roomListTable)->setColumnText(6, gDataManager->GetSysString(2024).data());
((irr::gui::CGUICustomTable*)roomListTable)->setColumnText(7, gDataManager->GetSysString(1988).data());
roomListTable->setColumnWidth(0, roomListTable->getColumnWidth(0));
mTopMenu->setItemText(0, gDataManager->GetSysString(2045).data()); //mRepositoriesInfo
mTopMenu->setItemText(1, gDataManager->GetSysString(1970).data()); //mAbout
mTopMenu->setItemText(2, gDataManager->GetSysString(2040).data()); //mVersion
RefreshUICoreVersion();
stExpectedCoreVersion->setText(GetLocalizedExpectedCore().data());
stCompatVersion->setText(GetLocalizedCompatVersion().data());
#define TYPECHK(id,stringid) chkTypeLimit[id]->setText(epro::sprintf(gDataManager->GetSysString(1627), gDataManager->GetSysString(stringid)).data());
TYPECHK(0, 1056);
TYPECHK(1, 1063);
TYPECHK(2, 1073);
TYPECHK(3, 1074);
TYPECHK(4, 1076);
#undef TYPECHK
ReloadCBCurrentSkin();
}
void Game::OnResize() {
env->getRootGUIElement()->setRelativePosition(irr::core::recti(0, 0, window_size.Width, window_size.Height));
{
const auto waboutcorner = wAbout->getAbsolutePosition().UpperLeftCorner;
using std::min;
const auto minwidth = min<uint32_t>(window_size.Width - waboutcorner.X,
min<uint32_t>(Scale(440), stAbout->getTextWidth() + Scale(10)));
const auto minheight = min<uint32_t>(window_size.Height - waboutcorner.Y,
min<uint32_t>(stAbout->getTextHeight() + Scale(10), Scale(690)));
stAbout->setRelativePosition(irr::core::recti(10, 10, minwidth, minheight));
}
wRoomListPlaceholder->setRelativePosition(irr::core::recti(0, 0, window_size.Width, window_size.Height));
wMainMenu->setRelativePosition(ResizeWin(mainMenuLeftX, 200, mainMenuRightX, 450));
wBtnSettings->setRelativePosition(ResizeWin(0, 610, 30, 640));
SetCentered(wCommitsLog);
SetCentered(updateWindow, false);
SetCentered(wYdkeManage, false);
SetCentered(wHandTest, false);
wCategories->setRelativePosition(ResizeWin(450, 60, 1000, 270));
wLinkMarks->setRelativePosition(ResizeWin(700, 30, 820, 150));
stBanlist->setRelativePosition(ResizeWin(10, 9, 100, 29));
stDeck->setRelativePosition(ResizeWin(10, 39, 100, 59));
stCategory->setRelativePosition(ResizeWin(10, 5, 70, 25));
stLimit->setRelativePosition(ResizeWin(205, 5, 280, 25));
stAttribute->setRelativePosition(ResizeWin(10, 28, 70, 48));
stRace->setRelativePosition(ResizeWin(10, 51, 70, 71));
stAttack->setRelativePosition(ResizeWin(205, 28, 280, 48));
stDefense->setRelativePosition(ResizeWin(205, 51, 280, 71));
stStar->setRelativePosition(ResizeWin(10, 74, 80, 94));
stSearch->setRelativePosition(ResizeWin(205, 74, 280, 94));
stScale->setRelativePosition(ResizeWin(110, 74, 150, 94));
wLanWindow->setRelativePosition(ResizeWin(220, 100, 800, 520));
SetCentered(wCreateHost, false);
if(dInfo.opponames.size() + dInfo.selfnames.size() >= 5) {
wHostPrepare->setRelativePosition(ResizeWin(270, 120, 750, 500));
wHostPrepareR->setRelativePosition(ResizeWin(750, 120, 950, 500));
wHostPrepareL->setRelativePosition(ResizeWin(70, 120, 270, 500));
} else {
wHostPrepare->setRelativePosition(ResizeWin(270, 120, 750, 440));
wHostPrepareR->setRelativePosition(ResizeWin(750, 120, 950, 440));
wHostPrepareL->setRelativePosition(ResizeWin(70, 120, 270, 440));
}
wRules->setRelativePosition(ResizeWin(630, 100, 1000, 310));
wReplay->setRelativePosition(ResizeWin(220, 100, 800, 520));
wSinglePlay->setRelativePosition(ResizeWin(220, 100, 800, 520));
gBot.window->setRelativePosition(irr::core::vector2di(wHostPrepare->getAbsolutePosition().LowerRightCorner.X, wHostPrepare->getAbsolutePosition().UpperLeftCorner.Y));
wHand->setRelativePosition(ResizeWin(500, 450, 825, 605));
wFTSelect->setRelativePosition(ResizeWin(550, 240, 780, 340));
SetMessageWindow();
wQuery->setRelativePosition(ResizeWin(490, 200, 840, 340));
wOptions->setRelativePosition(ResizeWinFromCenter(0, 0, wOptions->getRelativePosition().getWidth(), wOptions->getRelativePosition().getHeight(), 135));
wPosSelect->setRelativePosition(ResizeWin(340, 200, 935, 410));
wCardSelect->setRelativePosition(ResizeWin(320, 100, 1000, 400));
wCardDisplay->setRelativePosition(ResizeWin(320, 100, 1000, 400));
wANNumber->setRelativePosition(ResizeWin(550, 200, 780, 295));
wANCard->setRelativePosition(ResizeWin(430, 170, 840, 370));
wANAttribute->setRelativePosition(ResizeWin(500, 200, 830, 285));
wANRace->setRelativePosition(ResizeWin(480, 200, 850, 410));
wFileSave->setRelativePosition(ResizeWin(510, 200, 820, 320));
stHintMsg->setRelativePosition(ResizeWin(500, 60, 820, 90));
ResizeCardinfoWindow(gGameConfig->keep_cardinfo_aspect_ratio);
for(auto& window : repoInfoGui) {
window.second.progress2->setRelativePosition(Scale(5, 20 + 15, (300 - 8) * window_scale.X, 20 + 30));
window.second.history_button2->setRelativePosition(irr::core::recti(ResizeX(200), 5, ResizeX(300 - 5), Scale(20 + 10)));
}
stName->setRelativePosition(Scale(10, 10, 287 * window_scale.X, 32));
auto clearSize = Resize(160, 300 - Scale(7), 260, 325 - Scale(7));
auto expandSize = Resize(40, 300 - Scale(7), 140, 325 - Scale(7));
btnClearLog->setRelativePosition(clearSize);
btnExpandLog->setRelativePosition(expandSize);
btnClearChat->setRelativePosition(clearSize);
btnExpandChat->setRelativePosition(expandSize);
auto lstsSize = Resize(10, 10, infosExpanded ? 1012 : 290, 0);
lstsSize.LowerRightCorner.Y = expandSize.UpperLeftCorner.Y - Scale(10);
lstLog->setRelativePosition(lstsSize);
lstChat->setRelativePosition(lstsSize);
imageManager.ClearTexture(true);
btnPSAD->setImage(imageManager.tCover[0]);
btnPSDD->setImage(imageManager.tCover[0]);
ShowCardInfo(showingcard, true);
tabSystem->setRelativePosition({ {}, tabSystem->getParent()->getAbsolutePosition().getSize() });
auto repos_with_min_x = [x=std::min(tabSystem->getSubpanel()->getRelativePosition().getWidth() - 21, Scale(300))](irr::gui::IGUIElement* elem) {
auto cur_pos = elem->getRelativePosition();
cur_pos.LowerRightCorner.X = x;
elem->setRelativePosition(cur_pos);
};
repos_with_min_x(tabSettings.scrSoundVolume);
repos_with_min_x(tabSettings.scrMusicVolume);
repos_with_min_x(btnTabShowSettings);
SetCentered(gSettings.window);
ResizePhaseButtons();
auto prev = roomListTable->getSelected();
roomListTable->setRelativePosition(irr::core::recti(ResizeX(1), chkShowActiveRooms->getRelativePosition().LowerRightCorner.Y + ResizeY(10), ResizeX(1024 - 2), btnLanRefresh2->getRelativePosition().UpperLeftCorner.Y - ResizeY(25)));
roomListTable->setColumnWidth(0, window_scale.X * Scale(30)); // lock
roomListTable->setColumnWidth(1, window_scale.X * Scale(110)); // Allowed Cards:
roomListTable->setColumnWidth(2, window_scale.X * Scale(150)); // Duel Mode:
roomListTable->setColumnWidth(3, window_scale.X * Scale(50)); // Master Rule
roomListTable->setColumnWidth(4, window_scale.X * Scale(130)); // Forbidden List:
roomListTable->setColumnWidth(5, window_scale.X * Scale(115)); // Players:
roomListTable->setColumnWidth(6, window_scale.X * Scale(355)); // Notes:
roomListTable->setColumnWidth(7, window_scale.X * Scale(60)); // Status
roomListTable->addRow(roomListTable->getRowCount());
roomListTable->removeRow(roomListTable->getRowCount() - 1);
roomListTable->setSelected(prev);
}
irr::core::recti Game::Resize(irr::s32 x, irr::s32 y, irr::s32 x2, irr::s32 y2) const {
x = x * window_scale.X;
y = y * window_scale.Y;
x2 = x2 * window_scale.X;
y2 = y2 * window_scale.Y;
return Scale(x, y, x2, y2);
}
irr::core::recti Game::Resize(irr::s32 x, irr::s32 y, irr::s32 x2, irr::s32 y2, irr::s32 dx, irr::s32 dy, irr::s32 dx2, irr::s32 dy2) const {
x = x * window_scale.X + dx;
y = y * window_scale.Y + dy;
x2 = x2 * window_scale.X + dx2;
y2 = y2 * window_scale.Y + dy2;
return Scale(x, y, x2, y2);
}
irr::core::vector2di Game::Resize(irr::s32 x, irr::s32 y, bool reverse) const {
if(reverse) {
x = (x / window_scale.X) / gGameConfig->dpi_scale;
y = (y / window_scale.Y) / gGameConfig->dpi_scale;
} else {
x = x * window_scale.X * gGameConfig->dpi_scale;
y = y * window_scale.Y * gGameConfig->dpi_scale;
}
return { x, y };
}
// Resizes the bounds and caps the width if necessary to maintain the provided targetAspectRatio.
// This prevents the GUI element from appearing too wide if its width is overproportional.
irr::core::recti Game::ResizeWithCappedWidth(irr::s32 x, irr::s32 y, irr::s32 x2, irr::s32 y2, float targetAspectRatio, bool scale) const {
x *= window_scale.X;
y *= window_scale.Y;
x2 *= window_scale.X;
y2 *= window_scale.Y;
const auto dx = x2 - x;
const auto dy = y2 - y;
const auto incx = static_cast<irr::s32>(dy * targetAspectRatio);
if(dx > incx) {
x2 = x + incx;
} /* else {
y2 = y + dx / targetAspectRatio;
} */ // This commented out part is left in case it is ever necessary to adjust the height too to maintain the aspect ratio.
return scale ? Scale(x, y, x2, y2) : irr::core::recti{ x, y, x2, y2 };
}
irr::core::recti Game::ResizeWin(irr::s32 x, irr::s32 y, irr::s32 x2, irr::s32 y2, bool chat) const {
irr::s32 sx = x2 - x;
irr::s32 sy = y2 - y;
if(chat) {
y = window_size.Height - sy;
x2 = window_size.Width;
y2 = y + sy;
return Scale(x, y, x2, y2);
}
x = (x + sx / 2) * window_scale.X - sx / 2;
y = (y + sy / 2) * window_scale.Y - sy / 2;
x2 = sx + x;
y2 = sy + y;
return Scale(x, y, x2, y2);
}
void Game::SetCentered(irr::gui::IGUIElement* elem, bool use_offset) const {
if(use_offset && (is_building || dInfo.isInDuel))
elem->setRelativePosition(ResizeWinFromCenter(0, 0, elem->getRelativePosition().getWidth(), elem->getRelativePosition().getHeight(), Scale(155)));
else
elem->setRelativePosition(ResizeWinFromCenter(0, 0, elem->getRelativePosition().getWidth(), elem->getRelativePosition().getHeight()));
}
irr::core::recti Game::ResizeElem(irr::s32 x, irr::s32 y, irr::s32 x2, irr::s32 y2, bool scale) const {
irr::s32 sx = x2 - x;
irr::s32 sy = y2 - y;
x = (x + sx / 2 - 100) * window_scale.X - sx / 2 + 100;
y = y * window_scale.Y;
x2 = sx + x;
y2 = sy + y;
return scale ? Scale(x, y, x2, y2) : irr::core::recti{x, y, x2, y2};
}
irr::core::recti Game::ResizePhaseHint(irr::s32 x, irr::s32 y, irr::s32 x2, irr::s32 y2, irr::s32 width) const {
auto res = Resize(x, y, x2, y2);
res.UpperLeftCorner.X -= width / 2;
return res;
}
irr::core::recti Game::ResizeWinFromCenter(irr::s32 x, irr::s32 y, irr::s32 x2, irr::s32 y2, irr::s32 xoff, irr::s32 yoff) const {
auto size = driver->getScreenSize();
irr::core::recti rect(0, 0, size.Width, size.Height);
auto center = rect.getCenter();
irr::core::dimension2du sizes((x + x2) / 2, (y + y2) / 2);
return irr::core::recti((center.X - sizes.Width) + xoff, center.Y - sizes.Height + yoff, center.X + sizes.Width + xoff, center.Y + sizes.Height + yoff);
}
void Game::ValidateName(irr::gui::IGUIElement* obj) {
std::wstring text = obj->getText();
const auto oldsize = text.size();
static constexpr wchar_t chars[] = LR"(<>:"/\|?*)";
for(auto& forbid : chars)
text.erase(std::remove(text.begin(), text.end(), forbid), text.end());
if(text.size() != oldsize)
obj->setText(text.data());
}
epro::path_string Game::FindScript(epro::path_stringview name, irr::io::IReadFile** retarchive) {
for(auto& path : script_dirs) {
if(path == EPRO_TEXT("archives")) {
if(auto tmp = Utils::FindFileInArchives(EPRO_TEXT("script/"), name)) {
if(retarchive)
*retarchive = tmp;
else
tmp->drop();
return path;
}
} else {
auto tmp = path + name.data();
if(Utils::FileExists(tmp))
return tmp;
}
}
if(Utils::FileExists(name))
return name.data();
return EPRO_TEXT("");
}
static inline void seek(irr::io::IReadFile& file) { file.seek(0); }
static inline void seek(FileStream& file) { file.seekg(0); }
std::vector<char> Game::FindAndReadScript(epro::stringview name) {
irr::io::IReadFile* tmp{ nullptr };
auto path = FindScript(Utils::ToPathString(name), &tmp);
return ReadScript(path, tmp);
}
std::vector<char> Game::ReadScript(epro::path_stringview path, irr::io::IReadFile* archive) {
auto SkipBom = [](auto& stream) -> auto& {
char bom[3]{};
stream.read(bom, 3);
if(bom[0] != '\xEF' || bom[1] != '\xBB' || bom[2] != '\xBF')
seek(stream);
return stream;
};
if(archive) {
SkipBom(*archive);
std::vector<char> buffer(archive->getSize() - archive->getPos());
if(archive->read(buffer.data(), buffer.size()) != buffer.size())
buffer.clear();
archive->drop();
return buffer;
}
if(path.empty())
return {};
FileStream script{ path.data(), FileStream::in | FileStream::binary };
if(!script.fail())
return { std::istreambuf_iterator<char>(SkipBom(script)), std::istreambuf_iterator<char>() };
return {};
}
bool Game::LoadScript(const Duel* pduel, epro::stringview script_name) {
auto buf = FindAndReadScript(script_name);
return buf.size() && pduel->LoadScript(buf.data(), static_cast<uint32_t>(buf.size()), script_name.data());
}
DuelPtr Game::SetupDuel(OCG_DuelOptions opts) {
opts.cardReader = DataManager::CardReader;
opts.payload1 = gDataManager;
opts.scriptReader = ScriptReader;
opts.payload2 = this;
opts.logHandler = MessageHandler;
opts.payload3 = this;
opts.enableUnsafeLibraries = 1;
auto pduel = ocgcore->CreateDuel(&opts);
if(!pduel)
return nullptr;
LoadScript(pduel.get(), "constant.lua");
LoadScript(pduel.get(), "utility.lua");
for(const auto& script : init_scripts) {
auto buf = ReadScript(script);
if(buf.size())
pduel->LoadScript(buf.data(), static_cast<uint32_t>(buf.size()), Utils::ToUTF8IfNeeded(script).data());
}
return pduel;
}
int Game::ScriptReader(void* ocg_payload, OCG_Duel duel, const char* name) {
auto* payload = static_cast<Duel::ScriptReaderPayload*>(ocg_payload);
return static_cast<Game*>(payload->ogPayload)->LoadScript(payload->duel, name);
}
void Game::MessageHandler(void* payload, const char* string, int type) {
Game* game = static_cast<Game*>(payload);
std::stringstream ss(string);
std::string str;
while(std::getline(ss, str)) {
auto pos = str.find('\r');
if(str.size() && pos != std::string::npos)
str.erase(pos);
game->AddDebugMsg(str);
if(type > 1)
epro::print("{}\n", str);
}
}
void Game::UpdateDownloadBar(int percentage, int cur, int tot, const char* filename, bool is_new, void* payload) {
Game* game = static_cast<Game*>(payload);
std::lock_guard<epro::mutex> lk(game->progressStatusLock);
auto& status = game->progressStatus;
status.progressBottom = percentage;
game->updateProgressBottom->setProgress(percentage);
if((status.newFile |= is_new) == true)
status.progressText = epro::format(L"{}\n{}",
epro::format(gDataManager->GetSysString(1462), BufferIO::DecodeUTF8(filename)),
epro::format(gDataManager->GetSysString(1464), cur, tot));
}
void Game::UpdateUnzipBar(unzip_payload* payload) {
UnzipperPayload* unzipper = static_cast<UnzipperPayload*>(payload->payload);
Game* game = static_cast<Game*>(unzipper->payload);
std::lock_guard<epro::mutex> lk(game->progressStatusLock);
auto& status = game->progressStatus;
// current archive
if((status.newFile |= payload->is_new) == true) {
status.progressText = epro::format(L"{}\n{}",
epro::format(gDataManager->GetSysString(1463), Utils::ToUnicodeIfNeeded(unzipper->filename)),
epro::format(gDataManager->GetSysString(1464), unzipper->cur, unzipper->tot));
status.subProgressText = epro::format(gDataManager->GetSysString(1465), Utils::ToUnicodeIfNeeded(payload->filename));
}
status.progressTop = static_cast<irr::s32>(((double)payload->cur / (double)payload->tot) * 100);
// current file in archive
status.progressBottom = payload->percentage;
}
void Game::PopulateResourcesDirectories() {
if(Utils::FileExists(EPRO_TEXT("./init.lua")))
init_scripts.push_back(EPRO_TEXT("./init.lua"));
script_dirs.push_back(EPRO_TEXT("./expansions/script/"));
auto expansions_subdirs = Utils::FindSubfolders(EPRO_TEXT("./expansions/script/"));
script_dirs.insert(script_dirs.end(), std::make_move_iterator(expansions_subdirs.begin()), std::make_move_iterator(expansions_subdirs.end()));
script_dirs.push_back(EPRO_TEXT("archives"));
script_dirs.push_back(EPRO_TEXT("./script/"));
auto script_subdirs = Utils::FindSubfolders(EPRO_TEXT("./script/"));
script_dirs.insert(script_dirs.end(), std::make_move_iterator(script_subdirs.begin()), std::make_move_iterator(script_subdirs.end()));
pic_dirs.push_back(EPRO_TEXT("./expansions/pics/"));
pic_dirs.push_back(EPRO_TEXT("archives"));
pic_dirs.push_back(EPRO_TEXT("./pics/"));
cover_dirs.push_back(EPRO_TEXT("./expansions/pics/cover/"));
cover_dirs.push_back(EPRO_TEXT("archives"));
cover_dirs.push_back(EPRO_TEXT("./pics/cover/"));
field_dirs.push_back(EPRO_TEXT("./expansions/pics/field/"));
field_dirs.push_back(EPRO_TEXT("archives"));
field_dirs.push_back(EPRO_TEXT("./pics/field/"));
}
void Game::PopulateLocales() {
locales.clear();
for(auto& locale : Utils::FindSubfolders(EPRO_TEXT("./config/languages/"), 1, false)) {
locales.emplace_back(locale, std::vector<epro::path_string>());
}
}
void Game::ApplyLocale(size_t index, bool forced) {
static size_t previndex = 0;
if(index > locales.size())
return;
if(previndex == index && !forced)
return;
previndex = index;
gDataManager->ClearLocaleStrings();
gDataManager->ClearLocaleTexts();
if(index > 0) {
try {
gGameConfig->locale = locales[index - 1].first;
auto locale = epro::format(EPRO_TEXT("./config/languages/{}"), gGameConfig->locale);
for(auto& file : Utils::FindFiles(locale, { EPRO_TEXT("cdb") })) {
gDataManager->LoadLocaleDB(epro::format(EPRO_TEXT("{}/{}"), locale, file));
}
gDataManager->LoadLocaleStrings(epro::format(EPRO_TEXT("{}/strings.conf"), locale));
auto& extra = locales[index - 1].second;
bool refresh_db = false;
for(auto& path : extra) {
for(auto& file : Utils::FindFiles(path, { EPRO_TEXT("cdb") }, 0))
refresh_db = gDataManager->LoadLocaleDB(path + file) || refresh_db;
gDataManager->LoadLocaleStrings(path + EPRO_TEXT("strings.conf"));
}
if(refresh_db && is_building && deckBuilder.results.size())
deckBuilder.StartFilter(true);
}
catch(...) {
return;
}
} else
gGameConfig->locale = EPRO_TEXT("en");
ReloadElementsStrings();
}
}