og-edopro/gframe/duelclient.cpp
Edoardo Lolletti 14309283a7 remove "coreloaded" global variable
Serves no purpose anymore
2026-08-20 13:37:28 +02:00

4658 lines
178 KiB
C++

#include <algorithm>
#include "config.h"
#if EDOPRO_WINDOWS
#include <ws2tcpip.h>
#else
#include <arpa/inet.h>
#include <unistd.h>
#if !EDOPRO_ANDROID
#include <sys/types.h>
#include <signal.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <sys/wait.h>
#endif
#endif
#include "game_config.h"
#include <irrlicht.h>
#include "duelclient.h"
#include "client_card.h"
#include "materials.h"
#include "image_manager.h"
#include "single_mode.h"
#include "game.h"
#include "replay.h"
#include "replay_mode.h"
#include "sound_manager.h"
#include "CGUIImageButton/CGUIImageButton.h"
#include "progressivebuffer.h"
#include "utils.h"
#include "porting.h"
#include "fmt.h"
#include "localtime.h"
#define DEFAULT_DUEL_RULE 5
namespace ygo {
uint32_t DuelClient::connect_state = 0;
std::vector<uint8_t> DuelClient::response_buf;
uint32_t DuelClient::watching = 0;
uint8_t DuelClient::selftype = 0;
bool DuelClient::is_host = false;
bool DuelClient::is_local_host = false;
std::atomic<bool> DuelClient::answered{ false };
event_base* DuelClient::client_base = nullptr;
bufferevent* DuelClient::client_bev = nullptr;
bool DuelClient::is_closing = false;
uint64_t DuelClient::select_hint = 0;
std::wstring DuelClient::event_string;
RNG::mt19937 DuelClient::rnd;
ReplayStream DuelClient::replay_stream;
Replay DuelClient::last_replay;
bool DuelClient::is_swapping = false;
bool DuelClient::stop_threads{ true };
std::deque<std::vector<uint8_t>> DuelClient::to_analyze;
epro::mutex DuelClient::analyzeMutex;
epro::mutex DuelClient::to_analyze_mutex;
epro::thread DuelClient::parsing_thread;
epro::thread DuelClient::client_thread;
epro::condition_variable DuelClient::cv;
bool DuelClient::is_refreshing = false;
int DuelClient::match_kill = 0;
std::vector<epro::Host> DuelClient::hosts;
event* DuelClient::resp_event = 0;
epro::Address DuelClient::temp_ip{};
uint16_t DuelClient::temp_port = 0;
uint16_t DuelClient::temp_ver = 0;
bool DuelClient::try_needed = false;
void DuelClient::JoinFromDiscord() {
const auto& secret = mainGame->dInfo.secret;
mainGame->isHostingOnline = true;
if(!StartClient(secret.host.address, secret.host.port, secret.game_id, false))
return;
#define HIDE_AND_CHECK(obj) do {if(obj->isVisible()) mainGame->HideElement(obj);} while(0)
if(mainGame->is_building)
mainGame->deckBuilder.Terminate(false);
HIDE_AND_CHECK(mainGame->wMainMenu);
HIDE_AND_CHECK(mainGame->wLanWindow);
HIDE_AND_CHECK(mainGame->wCreateHost);
HIDE_AND_CHECK(mainGame->wReplay);
HIDE_AND_CHECK(mainGame->wSinglePlay);
HIDE_AND_CHECK(mainGame->wDeckEdit);
HIDE_AND_CHECK(mainGame->wRules);
HIDE_AND_CHECK(mainGame->wRoomListPlaceholder);
HIDE_AND_CHECK(mainGame->wCardImg);
HIDE_AND_CHECK(mainGame->wInfos);
HIDE_AND_CHECK(mainGame->btnLeaveGame);
HIDE_AND_CHECK(mainGame->wFileSave);
mainGame->device->setEventReceiver(&mainGame->menuHandler);
#undef HIDE_AND_CHECK
}
bool DuelClient::StartClient(const epro::Address& ip, uint16_t port, uint32_t gameid, bool create_game) {
if(connect_state)
return false;
client_base = event_base_new();
if(!client_base)
return false;
sockaddr_storage sin;
memset(&sin, 0, sizeof(sin));
if(ip.family == ip.INET) {
sockaddr_in& sin4 = reinterpret_cast<sockaddr_in&>(sin);
sin4.sin_family = AF_INET;
ip.toInAddr(sin4.sin_addr);
sin4.sin_port = htons(port);
} else if(ip.family == ip.INET6) {
sockaddr_in6& sin6 = reinterpret_cast<sockaddr_in6&>(sin);
sin6.sin6_family = AF_INET6;
ip.toIn6Addr(sin6.sin6_addr);
sin6.sin6_port = htons(port);
} else
return false;
client_bev = bufferevent_socket_new(client_base, -1, BEV_OPT_CLOSE_ON_FREE | BEV_OPT_THREADSAFE);
bufferevent_setcb(client_bev, ClientRead, nullptr, ClientEvent, (void*)create_game);
bufferevent_enable(client_bev, EV_READ);
temp_ip = ip;
temp_port = port;
if(bufferevent_socket_connect(client_bev, reinterpret_cast<sockaddr*>(&sin),
ip.family == ip.INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6)) < 0) {
bufferevent_free(client_bev);
event_base_free(client_base);
client_bev = 0;
client_base = 0;
return false;
}
connect_state = 0x1;
rnd.seed(time(0));
if(!create_game) {
timeval timeout = {5, 0};
event* timeout_event = event_new(client_base, 0, EV_TIMEOUT, ConnectTimeout, 0);
event_add(timeout_event, &timeout);
}
mainGame->dInfo.secret.game_id = gameid;
mainGame->dInfo.secret.host.port = port;
mainGame->dInfo.secret.host.address = ip;
mainGame->dInfo.isCatchingUp = false;
mainGame->dInfo.checkRematch = false;
mainGame->frameSignal.SetNoWait(true);
if(client_thread.joinable())
client_thread.join();
mainGame->frameSignal.SetNoWait(false);
stop_threads = false;
parsing_thread = epro::thread(ParserThread);
client_thread = epro::thread(ClientThread);
return true;
}
void DuelClient::ConnectTimeout([[maybe_unused]] evutil_socket_t fd, [[maybe_unused]] short events, [[maybe_unused]] void* arg) {
if(connect_state & 0x7)
return;
if(!is_closing) {
temp_ver = 0;
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->btnCreateHost->setEnabled(true);
mainGame->btnJoinHost->setEnabled(true);
mainGame->btnJoinCancel->setEnabled(true);
if(mainGame->isHostingOnline) {
if(!mainGame->wRoomListPlaceholder->isVisible())
mainGame->ShowElement(mainGame->wRoomListPlaceholder);
} else {
if(!mainGame->wLanWindow->isVisible())
mainGame->ShowElement(mainGame->wLanWindow);
}
mainGame->PopupMessage(gDataManager->GetSysString(1400));
}
event_base_loopbreak(client_base);
}
void DuelClient::StopClient(bool is_exiting) {
mainGame->frameSignal.SetNoWait(true);
if((connect_state & 0x7) != 0) {
is_closing = is_exiting;
to_analyze_mutex.lock();
to_analyze.clear();
event_base_loopbreak(client_base);
to_analyze_mutex.unlock();
#if EDOPRO_LINUX || EDOPRO_MACOS
for(auto& pid : mainGame->gBot.windbotsPids) {
kill(pid, SIGKILL);
(void)waitpid(pid, nullptr, 0);
}
mainGame->gBot.windbotsPids.clear();
#endif
if(!is_closing) {
}
}
if(client_thread.joinable())
client_thread.join();
mainGame->frameSignal.SetNoWait(false);
}
void DuelClient::ClientRead(bufferevent* bev, [[maybe_unused]] void* ctx) {
evbuffer* input = bufferevent_get_input(bev);
size_t len = evbuffer_get_length(input);
uint16_t packet_len = 0;
while(true) {
if(len < 2)
return;
evbuffer_copyout(input, &packet_len, 2);
if(len < packet_len + 2u)
return;
evbuffer_drain(input, 2);
std::vector<uint8_t> duel_client_read(packet_len);
evbuffer_remove(input, duel_client_read.data(), packet_len);
if(packet_len)
HandleSTOCPacketLanSync(std::move(duel_client_read));
len = evbuffer_get_length(input);
}
}
#define INTERNAL_HANDLE_CONNECTION_END 0
void DuelClient::ClientEvent([[maybe_unused]] bufferevent *bev, short events, void *ctx) {
if (events & BEV_EVENT_CONNECTED) {
bool create_game = (size_t)ctx != 0;
CTOS_PlayerInfo cspi;
BufferIO::EncodeUTF16(mainGame->ebNickName->getText(), cspi.name, 20);
SendPacketToServer(CTOS_PLAYER_INFO, cspi);
if(create_game) {
#define TOI(what, from, def) try { what = std::stoi(from); }\
catch(...) { what = def; }
CTOS_CreateGame cscg;
mainGame->dInfo.secret.game_id = 0;
BufferIO::EncodeUTF16(mainGame->ebServerName->getText(), cscg.name, 20);
BufferIO::EncodeUTF16(mainGame->ebServerPass->getText(), cscg.pass, 20);
mainGame->dInfo.secret.pass = mainGame->ebServerPass->getText();
cscg.info.rule = mainGame->cbRule->getSelected();
cscg.info.mode = 0;
TOI(cscg.info.start_hand, mainGame->ebStartHand->getText(), 5);
TOI(cscg.info.start_lp, mainGame->ebStartLP->getText(), 8000);
TOI(cscg.info.draw_count, mainGame->ebDrawCount->getText(), 1);
TOI(cscg.info.time_limit, mainGame->ebTimeLimit->getText(), 0);
cscg.info.lflist = gGameConfig->lastlflist = mainGame->cbHostLFList->getItemData(mainGame->cbHostLFList->getSelected());
cscg.info.duel_rule = 0;
cscg.info.duel_flag_low = mainGame->duel_param & 0xffffffff;
cscg.info.duel_flag_high = (mainGame->duel_param >> 32) & 0xffffffff;
cscg.info.no_check_deck_content = mainGame->chkNoCheckDeckContent->isChecked();
cscg.info.no_shuffle_deck = mainGame->chkNoShuffleDeck->isChecked();
cscg.info.handshake = SERVER_HANDSHAKE;
cscg.info.version = { EXPAND_VERSION(CLIENT_VERSION) };
TOI(cscg.info.team1, mainGame->ebTeam1->getText(), 1);
TOI(cscg.info.team2, mainGame->ebTeam2->getText(), 1);
TOI(cscg.info.best_of, mainGame->ebBestOf->getText(), 1);
static constexpr DeckSizes nolimit_deck_sizes{ {0,999},{0,999},{0,999} };
auto& sizes = cscg.info.sizes;
if(mainGame->chkNoCheckDeckSize->isChecked()) {
sizes = nolimit_deck_sizes;
} else {
TOI(sizes.main.min, mainGame->ebMainMin->getText(), 40);
TOI(sizes.main.max, mainGame->ebMainMax->getText(), 60);
TOI(sizes.extra.min, mainGame->ebExtraMin->getText(), 0);
TOI(sizes.extra.max, mainGame->ebExtraMax->getText(), 15);
TOI(sizes.side.min, mainGame->ebSideMin->getText(), 0);
TOI(sizes.side.max, mainGame->ebSideMax->getText(), 15);
}
#undef TOI
if(mainGame->btnRelayMode->isPressed())
cscg.info.duel_flag_low |= DUEL_RELAY;
if(cscg.info.no_shuffle_deck)
cscg.info.duel_flag_low |= DUEL_PSEUDO_SHUFFLE;
cscg.info.forbiddentypes = mainGame->forbiddentypes;
cscg.info.extra_rules = mainGame->extra_rules;
if(mainGame->ebHostNotes->isVisible()) {
BufferIO::EncodeUTF8(mainGame->ebHostNotes->getText(), cscg.notes, 200);
}
SendPacketToServer(CTOS_CREATE_GAME, cscg);
} else {
CTOS_JoinGame csjg;
if (temp_ver)
csjg.version = temp_ver;
else {
csjg.version = PRO_VERSION;
csjg.version2 = { EXPAND_VERSION(CLIENT_VERSION) };
}
csjg.gameid = mainGame->dInfo.secret.game_id;
BufferIO::EncodeUTF16(mainGame->dInfo.secret.pass.data(), csjg.pass, 20);
SendPacketToServer(CTOS_JOIN_GAME, csjg);
}
connect_state |= 0x2;
} else if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
if(!is_closing) {
std::vector<uint8_t> tmp;
tmp.resize(2);
tmp[0] = INTERNAL_HANDLE_CONNECTION_END;
tmp[1] = (events & BEV_EVENT_ERROR) == 0;
to_analyze_mutex.lock();
to_analyze.push_back(std::move(tmp));
to_analyze_mutex.unlock();
cv.notify_one();
}
event_base_loopexit(client_base, 0);
}
}
void DuelClient::ClientThread() {
Utils::SetThreadName("ClientThread");
event_base_dispatch(client_base);
to_analyze_mutex.lock();
stop_threads = true;
cv.notify_one();
to_analyze_mutex.unlock();
parsing_thread.join();
bufferevent_free(client_bev);
event_base_free(client_base);
connect_state = 0;
client_bev = 0;
client_base = 0;
}
void DuelClient::ParserThread() {
Utils::SetThreadName("ParserThread");
while(true) {
std::unique_lock<epro::mutex> lck(to_analyze_mutex);
while(to_analyze.empty()) {
if(stop_threads)
return;
cv.wait(lck);
}
auto pkt = std::move(to_analyze.front());
to_analyze.pop_front();
lck.unlock();
HandleSTOCPacketLanAsync(pkt);
}
}
void DuelClient::HandleSTOCPacketLanSync(std::vector<uint8_t>&& data) {
uint8_t pktType = data[0];
if(pktType != STOC_CHAT_2) {
to_analyze_mutex.lock();
to_analyze.push_back(std::move(data));
to_analyze_mutex.unlock();
cv.notify_one();
return;
}
if(mainGame->dInfo.isCatchingUp)
return;
auto* pdata = data.data() + 1;
switch(pktType) {
case STOC_CHAT_2: {
auto pkt = BufferIO::getStruct<STOC_Chat2>(pdata, data.size());
if(pkt.type == STOC_Chat2::PTYPE_DUELIST && (mainGame->dInfo.player_type >= 7 || !pkt.is_team) && mainGame->tabSettings.chkIgnoreOpponents->isChecked())
return;
if(pkt.type == STOC_Chat2::PTYPE_OBS && mainGame->tabSettings.chkIgnoreSpectators->isChecked())
return;
wchar_t name[20];
BufferIO::DecodeUTF16(pkt.client_name, name, 20);
wchar_t msg[256];
BufferIO::DecodeUTF16(pkt.msg, msg, 256);
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->AddChatMsg(name, msg, pkt.type);
break;
}
}
}
void DuelClient::HandleSTOCPacketLanAsync(const std::vector<uint8_t>& data) {
auto* pdata = data.data();
auto len = data.size();
uint8_t pktType = BufferIO::Read<uint8_t>(pdata);
switch(pktType) {
case INTERNAL_HANDLE_CONNECTION_END: {
bool iseof = !!BufferIO::Read<uint8_t>(pdata);
mainGame->dInfo.isInLobby = false;
if(connect_state == 0x1) {
temp_ver = 0;
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->btnCreateHost->setEnabled(true);
mainGame->btnJoinHost->setEnabled(true);
mainGame->btnJoinCancel->setEnabled(true);
if(mainGame->isHostingOnline) {
if(!mainGame->wCreateHost->isVisible() && !mainGame->wRoomListPlaceholder->isVisible())
mainGame->ShowElement(mainGame->wRoomListPlaceholder);
} else {
if(!mainGame->wLanWindow->isVisible())
mainGame->ShowElement(mainGame->wLanWindow);
}
mainGame->PopupMessage(gDataManager->GetSysString(1400));
} else if(connect_state == 0x7) {
if(!mainGame->dInfo.isInDuel && !mainGame->is_building) {
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->btnCreateHost->setEnabled(true);
mainGame->btnJoinHost->setEnabled(true);
mainGame->btnJoinCancel->setEnabled(true);
mainGame->HideElement(mainGame->wCreateHost);
mainGame->HideElement(mainGame->wHostPrepare);
mainGame->HideElement(mainGame->wHostPrepareL);
mainGame->HideElement(mainGame->wHostPrepareR);
mainGame->HideElement(mainGame->gBot.window);
if(mainGame->isHostingOnline) {
mainGame->ShowElement(mainGame->wRoomListPlaceholder);
} else {
mainGame->ShowElement(mainGame->wLanWindow);
}
mainGame->wChat->setVisible(false);
if(iseof)
mainGame->PopupMessage(gDataManager->GetSysString(1401));
else mainGame->PopupMessage(gDataManager->GetSysString(1402));
} else {
gSoundManager->StopSounds();
if(mainGame->dInfo.isStarted) {
ReplayPrompt(true);
}
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->PopupMessage(gDataManager->GetSysString(1502));
mainGame->btnCreateHost->setEnabled(true);
mainGame->btnJoinHost->setEnabled(true);
mainGame->btnJoinCancel->setEnabled(true);
mainGame->stTip->setVisible(false);
mainGame->stHintMsg->setVisible(false);
mainGame->closeDuelWindow = true;
mainGame->closeDoneSignal.Wait(lock);
mainGame->dInfo.checkRematch = false;
mainGame->dInfo.isInDuel = false;
mainGame->dInfo.isStarted = false;
mainGame->dField.Clear();
mainGame->is_building = false;
mainGame->device->setEventReceiver(&mainGame->menuHandler);
if(mainGame->isHostingOnline) {
mainGame->ShowElement(mainGame->wRoomListPlaceholder);
} else {
mainGame->ShowElement(mainGame->wLanWindow);
}
mainGame->SetMessageWindow();
}
}
break;
}
case STOC_GAME_MSG: {
analyzeMutex.lock();
answered = false;
ClientAnalyze(pdata, static_cast<uint32_t>(len - 1));
analyzeMutex.unlock();
break;
}
case STOC_ERROR_MSG: {
auto _pkt = BufferIO::getStruct<STOC_ErrorMsg>(pdata, len);
switch(_pkt.type) {
case ERROR_TYPE::JOINERROR: {
auto pkt = BufferIO::getStruct<JoinError>(pdata, len);
temp_ver = 0;
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
if(mainGame->isHostingOnline) {
#define HIDE_AND_CHECK(obj) if(obj->isVisible()) mainGame->HideElement(obj);
HIDE_AND_CHECK(mainGame->wCreateHost);
HIDE_AND_CHECK(mainGame->wRules);
#undef HIDE_AND_CHECK
mainGame->ShowElement(mainGame->wRoomListPlaceholder);
} else {
mainGame->btnCreateHost->setEnabled(true);
mainGame->btnJoinHost->setEnabled(true);
mainGame->btnJoinCancel->setEnabled(true);
}
int stringid = 1406;
switch(pkt.error) {
case JoinError::JERR_UNABLE: stringid--;
[[fallthrough]];
case JoinError::JERR_PASSWORD: stringid--;
[[fallthrough]];
case JoinError::JERR_REFUSED: stringid--;
}
if(stringid < 1406)
mainGame->PopupMessage(gDataManager->GetSysString(stringid));
connect_state |= 0x100;
event_base_loopbreak(client_base);
break;
}
case ERROR_TYPE::DECKERROR: {
auto pkt = BufferIO::getStruct<DeckError>(pdata, len);
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
int mainmin = 40, mainmax = 60, extramax = 15, sidemax = 15;
uint32_t code = 0, curcount = 0;
DeckError::DERR_TYPE flag = DeckError::NONE;
if(mainGame->dInfo.compat_mode) {
curcount = pkt.code;
code = pkt.code & 0xFFFFFFF;
flag = static_cast<DeckError::DERR_TYPE>(pkt.code >> 28);
} else {
code = pkt.code;
flag = pkt.type;
mainmin = pkt.count.minimum;
mainmax = extramax = sidemax = pkt.count.maximum;
curcount = pkt.count.current;
}
std::wstring text;
switch(flag) {
case DeckError::LFLIST: {
text = epro::sprintf(gDataManager->GetSysString(1407), gDataManager->GetName(code));
break;
}
case DeckError::OCGONLY: {
text = epro::sprintf(gDataManager->GetSysString(1413), gDataManager->GetName(code));
break;
}
case DeckError::TCGONLY: {
text = epro::sprintf(gDataManager->GetSysString(1414), gDataManager->GetName(code));
break;
}
case DeckError::UNKNOWNCARD: {
text = epro::sprintf(gDataManager->GetSysString(1415), gDataManager->GetName(code), code);
break;
}
case DeckError::CARDCOUNT: {
text = epro::sprintf(gDataManager->GetSysString(1416), gDataManager->GetName(code));
break;
}
case DeckError::MAINCOUNT: {
text = epro::sprintf(gDataManager->GetSysString(1417), mainmin, mainmax, curcount);
break;
}
case DeckError::EXTRACOUNT: {
if(curcount > 0)
text = epro::sprintf(gDataManager->GetSysString(1418), extramax, curcount);
else
text = gDataManager->GetSysString(1420).data();
break;
}
case DeckError::SIDECOUNT: {
text = epro::sprintf(gDataManager->GetSysString(1419), sidemax, curcount);
break;
}
case DeckError::FORBTYPE: {
text = gDataManager->GetSysString(1421).data();
break;
}
case DeckError::UNOFFICIALCARD: {
text = epro::sprintf(gDataManager->GetSysString(1422), gDataManager->GetName(code));
break;
}
case DeckError::INVALIDSIZE: {
text = gDataManager->GetSysString(1425).data();
break;
}
case DeckError::TOOMANYLEGENDS: {
text = gDataManager->GetSysString(1426).data();
break;
}
case DeckError::TOOMANYSKILLS: {
text = gDataManager->GetSysString(1427).data();
break;
}
default: {
text = gDataManager->GetSysString(1406).data();
break;
}
}
mainGame->PopupMessage(text);
mainGame->cbDeckSelect->setEnabled(true);
if(mainGame->dInfo.team1 + mainGame->dInfo.team2 > 2)
mainGame->btnHostPrepDuelist->setEnabled(true);
break;
}
case ERROR_TYPE::SIDEERROR: {
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->PopupMessage(gDataManager->GetSysString(1408));
break;
}
case ERROR_TYPE::VERERROR:
case ERROR_TYPE::VERERROR2: {
if(temp_ver || (_pkt.type == ERROR_TYPE::VERERROR2)) {
temp_ver = 0;
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->btnCreateHost->setEnabled(true);
mainGame->btnJoinHost->setEnabled(true);
mainGame->btnJoinCancel->setEnabled(true);
mainGame->btnHostConfirm->setEnabled(true);
mainGame->btnHostCancel->setEnabled(true);
if(_pkt.type == ERROR_TYPE::VERERROR2) {
auto version = BufferIO::getStruct<VersionError>(pdata, len).version;
mainGame->PopupMessage(epro::format(gDataManager->GetSysString(1423),
version.client.major, version.client.minor,
version.core.major, version.core.minor));
} else {
mainGame->PopupMessage(epro::sprintf(gDataManager->GetSysString(1411), _pkt.code >> 12, (_pkt.code >> 4) & 0xff, _pkt.code & 0xf));
}
if(mainGame->isHostingOnline) {
#define HIDE_AND_CHECK(obj) if(obj->isVisible()) mainGame->HideElement(obj);
HIDE_AND_CHECK(mainGame->wCreateHost);
HIDE_AND_CHECK(mainGame->wRules);
#undef HIDE_AND_CHECK
mainGame->ShowElement(mainGame->wRoomListPlaceholder);
} else {
mainGame->btnCreateHost->setEnabled(true);
}
} else {
temp_ver = _pkt.code;
try_needed = true;
}
connect_state |= 0x100;
event_base_loopbreak(client_base);
break;
}
}
break;
}
case STOC_SELECT_HAND: {
std::lock_guard lock(mainGame->gMutex);
if(mainGame->gSettings.chkAutoRPS->isChecked()) {
mainGame->dField.SendRPSResult(std::uniform_int_distribution<>(1, 3)(rnd));
} else {
mainGame->wHand->setVisible(true);
}
break;
}
case STOC_SELECT_TP: {
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->PopupElement(mainGame->wFTSelect);
break;
}
case STOC_HAND_RESULT: {
if(mainGame->dInfo.isCatchingUp)
break;
auto pkt = BufferIO::getStruct<STOC_HandResult>(pdata, len);
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->stHintMsg->setVisible(false);
mainGame->showcardcode = (pkt.res1 - 1) + ((pkt.res2 - 1) << 16);
mainGame->showcarddif = 50;
mainGame->showcardp = 0;
mainGame->showcard = 100;
mainGame->WaitFrameSignal(60, lock);
break;
}
case STOC_TP_RESULT: {
break;
}
case STOC_CHANGE_SIDE: {
gSoundManager->StopSounds();
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->dInfo.checkRematch = false;
mainGame->dInfo.isInLobby = false;
mainGame->dInfo.isInDuel = false;
mainGame->dInfo.isStarted = false;
mainGame->dField.Clear();
mainGame->is_building = true;
mainGame->is_siding = true;
mainGame->wChat->setVisible(false);
mainGame->wPhase->setVisible(false);
mainGame->wDeckEdit->setVisible(false);
mainGame->wFilter->setVisible(false);
mainGame->wSort->setVisible(false);
mainGame->stTip->setVisible(false);
mainGame->btnSideOK->setVisible(true);
mainGame->btnSideShuffle->setVisible(true);
mainGame->btnSideSort->setVisible(true);
mainGame->btnSideReload->setVisible(true);
if(mainGame->wQuery->isVisible())
mainGame->HideElement(mainGame->wQuery);
if(mainGame->wOptions->isVisible())
mainGame->HideElement(mainGame->wOptions);
if(mainGame->wPosSelect->isVisible())
mainGame->HideElement(mainGame->wPosSelect);
if(mainGame->wCardSelect->isVisible())
mainGame->HideElement(mainGame->wCardSelect);
if(mainGame->wCardDisplay->isVisible())
mainGame->HideElement(mainGame->wCardDisplay);
if(mainGame->wANNumber->isVisible())
mainGame->HideElement(mainGame->wANNumber);
if(mainGame->wANCard->isVisible())
mainGame->HideElement(mainGame->wANCard);
if(mainGame->dInfo.player_type < 7)
mainGame->btnLeaveGame->setVisible(false);
mainGame->btnSpectatorSwap->setVisible(false);
mainGame->btnChainIgnore->setVisible(false);
mainGame->btnChainAlways->setVisible(false);
mainGame->btnChainWhenAvail->setVisible(false);
mainGame->btnCancelOrFinish->setVisible(false);
mainGame->deckBuilder.result_string = L"0";
mainGame->deckBuilder.results.clear();
mainGame->deckBuilder.hovered_code = 0;
mainGame->deckBuilder.is_draging = false;
gdeckManager->pre_deck = gdeckManager->sent_deck;
mainGame->deckBuilder.SetCurrentDeck(gdeckManager->sent_deck);
mainGame->device->setEventReceiver(&mainGame->deckBuilder);
mainGame->dInfo.isFirst = (mainGame->dInfo.player_type < mainGame->dInfo.team1) || (mainGame->dInfo.player_type >=7);
mainGame->dInfo.isTeam1 = mainGame->dInfo.isFirst;
mainGame->SetMessageWindow();
break;
}
case STOC_WAITING_SIDE:
case STOC_WAITING_REMATCH: {
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->dField.Clear();
mainGame->stHintMsg->setText(gDataManager->GetSysString(pktType == STOC_WAITING_SIDE ? 1409 : 1424).data());
mainGame->stHintMsg->setVisible(true);
break;
}
case STOC_CREATE_GAME: {
mainGame->dInfo.secret.game_id = BufferIO::getStruct<STOC_CreateGame>(pdata, len).gameid;
break;
}
case STOC_JOIN_GAME: {
temp_ver = 0;
auto pkt = BufferIO::getStruct<STOC_JoinGame>(pdata, len);
mainGame->dInfo.isInLobby = true;
mainGame->dInfo.compat_mode = pkt.info.handshake != SERVER_HANDSHAKE;
mainGame->dInfo.legacy_race_size = mainGame->dInfo.compat_mode || (pkt.info.version.core.major < 10);
if(mainGame->dInfo.compat_mode) {
pkt.info.duel_flag_low = 0;
pkt.info.duel_flag_high = 0;
pkt.info.forbiddentypes = 0;
pkt.info.extra_rules = 0;
pkt.info.best_of = 1;
pkt.info.team1 = 1;
pkt.info.team2 = 1;
if(pkt.info.mode == MODE_MATCH) {
pkt.info.best_of = 3;
}
if(pkt.info.mode == MODE_TAG) {
pkt.info.team1 = 2;
pkt.info.team2 = 2;
}
#define CHK(rule) case rule : pkt.info.duel_flag_low = DUEL_MODE_MR##rule;break;
switch(pkt.info.duel_rule) {
CHK(1)
CHK(2)
CHK(3)
CHK(4)
CHK(5)
}
#undef CHK
}
uint64_t params = (pkt.info.duel_flag_low | ((uint64_t)pkt.info.duel_flag_high) << 32);
mainGame->dInfo.duel_params = params;
mainGame->dInfo.isRelay = params & DUEL_RELAY;
params &= ~DUEL_RELAY;
pkt.info.no_shuffle_deck = (pkt.info.no_shuffle_deck != 0) || ((params & DUEL_PSEUDO_SHUFFLE) != 0);
params &= ~DUEL_PSEUDO_SHUFFLE;
mainGame->dInfo.team1 = pkt.info.team1;
mainGame->dInfo.team2 = pkt.info.team2;
mainGame->dInfo.best_of = pkt.info.best_of;
std::wstring str, strR, strL;
str.append(epro::format(L"{}{}\n", gDataManager->GetSysString(1226), gdeckManager->GetLFListName(pkt.info.lflist)));
str.append(epro::format(L"{}{}\n", gDataManager->GetSysString(1225), gDataManager->GetSysString(1900 + pkt.info.rule)));
if(mainGame->dInfo.compat_mode)
str.append(epro::format(L"{}{}\n", gDataManager->GetSysString(1227), gDataManager->GetSysString(1244 + pkt.info.mode)));
else {
str.append(epro::format(L"{}{} {}{}\n", gDataManager->GetSysString(1227), gDataManager->GetSysString(1381), mainGame->dInfo.best_of, mainGame->dInfo.isRelay ? L" Relay" : L""));
}
if(pkt.info.time_limit) {
str.append(epro::format(L"{}{}\n", gDataManager->GetSysString(1237), pkt.info.time_limit));
}
str.append(L"==========\n");
str.append(epro::format(L"{}{}\n", gDataManager->GetSysString(1231), pkt.info.start_lp));
str.append(epro::format(L"{}{}\n", gDataManager->GetSysString(1232), pkt.info.start_hand));
str.append(epro::format(L"{}{}\n", gDataManager->GetSysString(1233), pkt.info.draw_count));
int rule;
mainGame->dInfo.duel_field = mainGame->GetMasterRule(params & ~DUEL_TCG_SEGOC_NONPUBLIC, pkt.info.forbiddentypes, &rule);
if(mainGame->dInfo.compat_mode)
rule = pkt.info.duel_rule;
if (rule >= 6) {
if(params == DUEL_MODE_SPEED) {
str.append(epro::format(L"*{}\n", gDataManager->GetSysString(1258)));
} else if(params == DUEL_MODE_RUSH) {
str.append(epro::format(L"*{}\n", gDataManager->GetSysString(1259)));
} else if(params == DUEL_MODE_GOAT) {
str.append(epro::format(L"*{}\n", gDataManager->GetSysString(1248)));
} else {
auto custom_rules_params = params & ~(DUEL_TEST_MODE | DUEL_ATTACK_FIRST_TURN | DUEL_PSEUDO_SHUFFLE | DUEL_SIMPLE_AI | DUEL_RELAY);
for(uint32_t i = 0; i < sizeofarr(mainGame->chkCustomRules); ++i) {
bool set = false;
if(i == 19)
set = custom_rules_params & DUEL_USE_TRAPS_IN_NEW_CHAIN;
else if(i == 20)
set = custom_rules_params & DUEL_6_STEP_BATLLE_STEP;
else if(i == 21)
set = custom_rules_params & DUEL_TRIGGER_WHEN_PRIVATE_KNOWLEDGE;
else if(i > 21)
set = custom_rules_params & 0x100ULL << (i - 3);
else
set = custom_rules_params & 0x100ULL << i;
if(set) {
strR.append(epro::format(L"*{}\n", gDataManager->GetSysString(1631 + i)));
}
}
str.append(epro::format(L"*{}\n", gDataManager->GetSysString(1630)));
}
} else if (rule != DEFAULT_DUEL_RULE) {
str.append(epro::format(L"*{}\n", gDataManager->GetSysString(1260 + rule - 1)));
}
if(params & DUEL_TCG_SEGOC_NONPUBLIC && params != DUEL_MODE_GOAT)
strR.append(epro::format(L"*{}\n", gDataManager->GetSysString(1631 + (TCG_SEGOC_NONPUBLIC - CHECKBOX_OBSOLETE))));
if(!mainGame->dInfo.compat_mode) {
for(int flag = SEALED_DUEL, i = 0; flag < ACTION_DUEL + 1; flag = flag << 1, i++)
if(pkt.info.extra_rules & flag) {
strR.append(epro::format(L"*{}\n", gDataManager->GetSysString(1132 + i)));
}
}
if(pkt.info.no_check_deck_content) {
str.append(epro::format(L"*{}\n", gDataManager->GetSysString(1229)));
}
if(pkt.info.no_shuffle_deck) {
str.append(epro::format(L"*{}\n", gDataManager->GetSysString(1230)));
}
static constexpr DeckSizes ocg_deck_sizes{ {40,60}, {0,15}, {0,15} };
static constexpr DeckSizes rush_deck_sizes{ {40,60}, {0,15}, {0,15} };
static constexpr DeckSizes speed_deck_sizes{ {20,30}, {0,6}, {0,6} };
static constexpr DeckSizes goat_deck_sizes{ {40,60}, {0,999}, {0,15} };
static constexpr DeckSizes empty_deck_sizes{ {0,0}, {0,0}, {0,0} }; // compat mode
if(pkt.info.sizes != empty_deck_sizes) {
do {
if(rule < 6) {
if(pkt.info.sizes == ocg_deck_sizes)
break;
} else {
if(params == DUEL_MODE_RUSH && pkt.info.sizes == rush_deck_sizes)
break;
if(params == DUEL_MODE_GOAT && pkt.info.sizes == goat_deck_sizes)
break;
if(params == DUEL_MODE_SPEED && pkt.info.sizes == speed_deck_sizes)
break;
}
str.append(epro::format(L"*{}\n", gDataManager->GetSysString(12112)));
} while(0);
}
static constexpr std::pair<uint32_t, uint32_t> MONSTER_TYPES[]{
{ TYPE_FUSION, 1056 },
{ TYPE_SYNCHRO, 1063 },
{ TYPE_XYZ, 1073 },
{ TYPE_PENDULUM, 1074 },
{ TYPE_LINK, 1076 }
};
for (const auto& pair : MONSTER_TYPES) {
if (pkt.info.forbiddentypes & pair.first) {
strL += epro::sprintf(gDataManager->GetSysString(1627), gDataManager->GetSysString(pair.second));
strL += L"\n";
}
}
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
matManager.SetActiveVertices(mainGame->dInfo.HasFieldFlag(DUEL_3_COLUMNS_FIELD),
!mainGame->dInfo.HasFieldFlag(DUEL_SEPARATE_PZONE));
int x = (pkt.info.team1 + pkt.info.team2 >= 5) ? 60 : 0;
mainGame->btnHostPrepOB->setRelativePosition(mainGame->Scale<irr::s32>(10, 180 + x, 110, 205 + x));
mainGame->stHostPrepOB->setRelativePosition(mainGame->Scale<irr::s32>(10, 210 + x, 270, 230 + x));
mainGame->stHostPrepRule->setRelativePosition(mainGame->Scale<irr::s32>(280, 30, 460, 270 + x));
mainGame->stDeckSelect->setRelativePosition(mainGame->Scale<irr::s32>(10, 235 + x, 110, 255 + x));
mainGame->cbDeckSelect->setRelativePosition(mainGame->Scale<irr::s32>(120, 230 + x, 270, 255 + x));
mainGame->btnHostPrepReady->setRelativePosition(mainGame->Scale<irr::s32>(170, 180 + x, 270, 205 + x));
mainGame->btnHostPrepNotReady->setRelativePosition(mainGame->Scale<irr::s32>(170, 180 + x, 270, 205 + x));
mainGame->btnHostPrepStart->setRelativePosition(mainGame->Scale<irr::s32>(230, 280 + x, 340, 305 + x));
mainGame->btnHostPrepCancel->setRelativePosition(mainGame->Scale<irr::s32>(350, 280 + x, 460, 305 + x));
mainGame->wHostPrepare->setRelativePosition(mainGame->ResizeWin(270, 120, 750, 440 + x));
mainGame->wHostPrepareR->setRelativePosition(mainGame->ResizeWin(750, 120, 950, 440 + x));
mainGame->wHostPrepareL->setRelativePosition(mainGame->ResizeWin(70, 120, 270, 440 + x));
mainGame->gBot.window->setRelativePosition(irr::core::vector2di(mainGame->wHostPrepare->getAbsolutePosition().LowerRightCorner.X, mainGame->wHostPrepare->getAbsolutePosition().UpperLeftCorner.Y));
for(int i = 0; i < 6; i++) {
mainGame->chkHostPrepReady[i]->setVisible(false);
mainGame->chkHostPrepReady[i]->setChecked(false);
mainGame->btnHostPrepKick[i]->setVisible(false);
mainGame->stHostPrepDuelist[i]->setVisible(false);
mainGame->stHostPrepDuelist[i]->setText(L"");
}
for(int i = 0; i < pkt.info.team1; i++) {
mainGame->chkHostPrepReady[i]->setVisible(true);
mainGame->stHostPrepDuelist[i]->setVisible(true);
mainGame->btnHostPrepKick[i]->setRelativePosition(mainGame->Scale<irr::s32>(10, 65 + i * 25, 30, 85 + i * 25));
mainGame->stHostPrepDuelist[i]->setRelativePosition(mainGame->Scale<irr::s32>(40, 65 + i * 25, 240, 85 + i * 25));
mainGame->chkHostPrepReady[i]->setRelativePosition(mainGame->Scale<irr::s32>(250, 65 + i * 25, 270, 85 + i * 25));
}
for(int i = pkt.info.team1; i < pkt.info.team1 + pkt.info.team2; i++) {
mainGame->chkHostPrepReady[i]->setVisible(true);
mainGame->stHostPrepDuelist[i]->setVisible(true);
mainGame->btnHostPrepKick[i]->setRelativePosition(mainGame->Scale<irr::s32>(10, 10 + 65 + i * 25, 30, 10 + 85 + i * 25));
mainGame->stHostPrepDuelist[i]->setRelativePosition(mainGame->Scale<irr::s32>(40, 10 + 65 + i * 25, 240, 10 + 85 + i * 25));
mainGame->chkHostPrepReady[i]->setRelativePosition(mainGame->Scale<irr::s32>(250, 10 + 65 + i * 25, 270, 10 + 85 + i * 25));
}
mainGame->dInfo.selfnames.resize(pkt.info.team1);
mainGame->dInfo.opponames.resize(pkt.info.team2);
mainGame->btnHostPrepReady->setVisible(true);
mainGame->btnHostPrepNotReady->setVisible(false);
mainGame->dInfo.time_limit = pkt.info.time_limit;
mainGame->dInfo.time_left[0] = 0;
mainGame->dInfo.time_left[1] = 0;
mainGame->deckBuilder.filterList = 0;
for(auto lit = gdeckManager->_lfList.begin(); lit != gdeckManager->_lfList.end(); ++lit)
if(lit->hash == pkt.info.lflist)
mainGame->deckBuilder.filterList = &(*lit);
if(mainGame->deckBuilder.filterList == 0)
mainGame->deckBuilder.filterList = &gdeckManager->_lfList[0];
watching = 0;
mainGame->stHostPrepOB->setText(epro::format(L"{} {}", gDataManager->GetSysString(1253), watching).data());
mainGame->stHostPrepRule->setText(str.data());
mainGame->stHostPrepRuleR->setText(strR.data());
mainGame->stHostPrepRuleL->setText(strL.data());
mainGame->RefreshDeck(mainGame->cbDeckSelect);
mainGame->cbDeckSelect->setEnabled(true);
if(mainGame->wCreateHost->isVisible())
mainGame->HideElement(mainGame->wCreateHost);
else if (mainGame->wLanWindow->isVisible())
mainGame->HideElement(mainGame->wLanWindow);
mainGame->ShowElement(mainGame->wHostPrepare);
if(strR.size())
mainGame->ShowElement(mainGame->wHostPrepareR);
if(strL.size())
mainGame->ShowElement(mainGame->wHostPrepareL);
mainGame->wChat->setVisible(true);
mainGame->dInfo.isFirst = (mainGame->dInfo.player_type < mainGame->dInfo.team1) || (mainGame->dInfo.player_type >= 7);
mainGame->dInfo.isTeam1 = mainGame->dInfo.isFirst;
connect_state |= 0x4;
break;
}
case STOC_TYPE_CHANGE: {
auto pkt = BufferIO::getStruct<STOC_TypeChange>(pdata, len);
selftype = pkt.type & 0xf;
is_host = ((pkt.type >> 4) & 0xf) != 0;
for(int i = 0; i < mainGame->dInfo.team1 + mainGame->dInfo.team2; i++) {
mainGame->btnHostPrepKick[i]->setVisible(is_host);
}
for(int i = 0; i < mainGame->dInfo.team1 + mainGame->dInfo.team2; i++) {
mainGame->chkHostPrepReady[i]->setEnabled(false);
}
if(selftype >= mainGame->dInfo.team1 + mainGame->dInfo.team2) {
mainGame->btnHostPrepDuelist->setEnabled(true);
mainGame->btnHostPrepOB->setEnabled(false);
mainGame->btnHostPrepReady->setVisible(false);
mainGame->btnHostPrepNotReady->setVisible(false);
} else {
mainGame->chkHostPrepReady[selftype]->setEnabled(true);
mainGame->chkHostPrepReady[selftype]->setChecked(false);
mainGame->btnHostPrepDuelist->setEnabled(mainGame->dInfo.team1 + mainGame->dInfo.team2 > 2);
mainGame->btnHostPrepOB->setEnabled(true);
mainGame->btnHostPrepReady->setVisible(true);
mainGame->btnHostPrepNotReady->setVisible(false);
}
mainGame->btnHostPrepWindBot->setVisible(is_host && !mainGame->isHostingOnline);
mainGame->btnHostPrepStart->setVisible(is_host);
mainGame->btnHostPrepStart->setEnabled(is_host && CheckReady());
mainGame->dInfo.player_type = selftype;
mainGame->dInfo.isFirst = (mainGame->dInfo.player_type < mainGame->dInfo.team1) || (mainGame->dInfo.player_type >= 7);
mainGame->dInfo.isTeam1 = mainGame->dInfo.isFirst;
break;
}
case STOC_DUEL_START: {
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->HideElement(mainGame->wHostPrepare);
mainGame->HideElement(mainGame->gBot.window);
mainGame->HideElement(mainGame->wHostPrepareL);
mainGame->HideElement(mainGame->wHostPrepareR);
mainGame->WaitFrameSignal(11, lock);
mainGame->dField.Clear();
mainGame->dInfo.isInLobby = false;
mainGame->is_siding = false;
mainGame->dInfo.checkRematch = false;
mainGame->dInfo.isInDuel = true;
mainGame->dInfo.isStarted = false;
mainGame->dInfo.lp[0] = 0;
mainGame->dInfo.lp[1] = 0;
mainGame->dInfo.turn = 0;
mainGame->dInfo.time_left[0] = 0;
mainGame->dInfo.time_left[1] = 0;
mainGame->dInfo.time_player = 2;
mainGame->dInfo.current_player[0] = 0;
mainGame->dInfo.current_player[1] = 0;
mainGame->dInfo.isReplaySwapped = false;
mainGame->is_building = false;
mainGame->mTopMenu->setVisible(false);
mainGame->wCardImg->setVisible(true);
mainGame->wInfos->setVisible(true);
mainGame->wPhase->setVisible(true);
mainGame->btnSideOK->setVisible(false);
mainGame->btnDP->setVisible(false);
mainGame->btnDP->setSubElement(false);
mainGame->btnSP->setVisible(false);
mainGame->btnSP->setSubElement(false);
mainGame->btnM1->setVisible(false);
mainGame->btnM1->setSubElement(false);
mainGame->btnBP->setVisible(false);
mainGame->btnBP->setSubElement(false);
mainGame->btnM2->setVisible(false);
mainGame->btnM2->setSubElement(false);
mainGame->btnEP->setVisible(false);
mainGame->btnEP->setSubElement(false);
mainGame->btnShuffle->setVisible(false);
mainGame->btnSideShuffle->setVisible(false);
mainGame->btnSideSort->setVisible(false);
mainGame->btnSideReload->setVisible(false);
mainGame->wChat->setVisible(true);
mainGame->device->setEventReceiver(&mainGame->dField);
mainGame->SetPhaseButtons();
mainGame->SetMessageWindow();
mainGame->dInfo.selfnames.clear();
mainGame->dInfo.opponames.clear();
int i;
for(i = 0; i < mainGame->dInfo.team1; i++) {
mainGame->dInfo.selfnames.push_back(mainGame->stHostPrepDuelist[i]->getText());
}
for(; i < mainGame->dInfo.team1 + mainGame->dInfo.team2; i++) {
mainGame->dInfo.opponames.push_back(mainGame->stHostPrepDuelist[i]->getText());
}
if(selftype >= mainGame->dInfo.team1 + mainGame->dInfo.team2) {
mainGame->dInfo.player_type = 7;
mainGame->btnLeaveGame->setText(gDataManager->GetSysString(1350).data());
mainGame->btnLeaveGame->setVisible(true);
mainGame->btnSpectatorSwap->setVisible(true);
mainGame->dInfo.isFirst = true;
mainGame->dInfo.isTeam1 = true;
} else {
mainGame->dInfo.isFirst = selftype < mainGame->dInfo.team1;
mainGame->dInfo.isTeam1 = mainGame->dInfo.isFirst;
}
mainGame->dInfo.current_player[0] = 0;
mainGame->dInfo.current_player[1] = 0;
match_kill = 0;
replay_stream.clear();
break;
}
case STOC_DUEL_END: {
gSoundManager->StopSounds();
{
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
if(mainGame->dInfo.player_type < 7)
mainGame->btnLeaveGame->setVisible(false);
mainGame->btnSpectatorSwap->setVisible(false);
mainGame->btnChainIgnore->setVisible(false);
mainGame->btnChainAlways->setVisible(false);
mainGame->btnChainWhenAvail->setVisible(false);
mainGame->stMessage->setText(gDataManager->GetSysString(1500).data());
mainGame->btnCancelOrFinish->setVisible(false);
if(mainGame->wQuery->isVisible())
mainGame->HideElement(mainGame->wQuery);
if(mainGame->wOptions->isVisible())
mainGame->HideElement(mainGame->wOptions);
if(mainGame->wPosSelect->isVisible())
mainGame->HideElement(mainGame->wPosSelect);
if(mainGame->wCardSelect->isVisible())
mainGame->HideElement(mainGame->wCardSelect);
if(mainGame->wCardDisplay->isVisible())
mainGame->HideElement(mainGame->wCardDisplay);
if(mainGame->wANNumber->isVisible())
mainGame->HideElement(mainGame->wANNumber);
if(mainGame->wANCard->isVisible())
mainGame->HideElement(mainGame->wANCard);
mainGame->PopupElement(mainGame->wMessage);
mainGame->actionSignal.Wait(lock);
mainGame->closeDuelWindow = true;
mainGame->closeDoneSignal.Wait(lock);
mainGame->dInfo.isInLobby = false;
mainGame->dInfo.checkRematch = false;
mainGame->dInfo.isInDuel = false;
mainGame->dInfo.isStarted = false;
mainGame->dField.Clear();
mainGame->is_building = false;
mainGame->is_siding = false;
mainGame->wDeckEdit->setVisible(false);
mainGame->btnCreateHost->setEnabled(true);
mainGame->btnJoinHost->setEnabled(true);
mainGame->btnJoinCancel->setEnabled(true);
mainGame->stTip->setVisible(false);
mainGame->device->setEventReceiver(&mainGame->menuHandler);
if(mainGame->isHostingOnline) {
mainGame->ShowElement(mainGame->wRoomListPlaceholder);
} else {
mainGame->ShowElement(mainGame->wLanWindow);
}
mainGame->SetMessageWindow();
}
connect_state |= 0x100;
event_base_loopbreak(client_base);
break;
}
case STOC_REPLAY: {
ReplayPrompt(mainGame->dInfo.compat_mode || last_replay.pheader.base.id == 0);
break;
}
case STOC_TIME_LIMIT: {
auto pkt = BufferIO::getStruct<STOC_TimeLimit>(pdata, len);
int lplayer = mainGame->LocalPlayer(pkt.player);
if(lplayer == 0)
DuelClient::SendPacketToServer(CTOS_TIME_CONFIRM);
mainGame->dInfo.time_player = lplayer;
mainGame->dInfo.time_left[lplayer] = pkt.left_time;
break;
}
case STOC_CHAT: {
auto pkt = BufferIO::getStruct<STOC_Chat>(pdata, data.size());
int player = pkt.player;
int type = -1;
if(player < mainGame->dInfo.team1 + mainGame->dInfo.team2) {
int team1 = mainGame->dInfo.team1;
int team2 = mainGame->dInfo.team2;
if((mainGame->dInfo.isTeam1 && !mainGame->dInfo.isFirst) ||
(!mainGame->dInfo.isTeam1 && mainGame->dInfo.isFirst)) {
std::swap(team1, team2);
}
if(player >= team1) {
player -= team1;
type = 1;
} else {
type = 0;
}
if((!mainGame->dInfo.isFirst && mainGame->dInfo.isTeam1) ||
(mainGame->dInfo.isFirst && !mainGame->dInfo.isTeam1))
type = 1 - type;
if(((type == 1 && mainGame->dInfo.isTeam1) || (type == 0 && !mainGame->dInfo.isTeam1)) && mainGame->tabSettings.chkIgnoreOpponents->isChecked())
return;
} else {
type = 2;
if(player == 8) { //system custom message.
if(mainGame->tabSettings.chkIgnoreOpponents->isChecked())
return;
} else if(player < 11 || player > 19) {
if(mainGame->tabSettings.chkIgnoreSpectators->isChecked())
return;
player = 10;
}
}
wchar_t msg[256];
BufferIO::DecodeUTF16(pkt.msg, msg, 256);
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->AddChatMsg(msg, player, type);
break;
}
case STOC_HS_PLAYER_ENTER: {
gSoundManager->PlaySoundEffect(SoundManager::SFX::PLAYER_ENTER);
auto pkt = BufferIO::getStruct<STOC_HS_PlayerEnter>(pdata, len);
if(pkt.pos > 5)
break;
wchar_t name[20];
BufferIO::DecodeUTF16(pkt.name, name, 20);
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
if(pkt.pos < mainGame->dInfo.team1)
mainGame->dInfo.selfnames[pkt.pos] = name;
else
mainGame->dInfo.opponames[pkt.pos - mainGame->dInfo.team1] = name;
mainGame->stHostPrepDuelist[pkt.pos]->setText(name);
mainGame->btnHostPrepStart->setVisible(is_host);
mainGame->btnHostPrepStart->setEnabled(is_host && CheckReady());
break;
}
case STOC_HS_PLAYER_CHANGE: {
auto pkt = BufferIO::getStruct<STOC_HS_PlayerChange>(pdata, len);
uint8_t pos = (pkt.status >> 4) & 0xf;
uint8_t state = pkt.status & 0xf;
if(pos > 5)
break;
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
if(state < 8) {
gSoundManager->PlaySoundEffect(SoundManager::SFX::PLAYER_ENTER);
std::wstring prename = mainGame->stHostPrepDuelist[pos]->getText();
mainGame->stHostPrepDuelist[state]->setText(prename.data());
mainGame->stHostPrepDuelist[pos]->setText(L"");
mainGame->chkHostPrepReady[pos]->setChecked(false);
if(pos < mainGame->dInfo.team1)
mainGame->dInfo.selfnames[pos] = L"";
else
mainGame->dInfo.opponames[pos - mainGame->dInfo.team1] = L"";
if(state < mainGame->dInfo.team1)
mainGame->dInfo.selfnames[state] = prename;
else
mainGame->dInfo.opponames[state - mainGame->dInfo.team1] = prename;
} else if(state == PLAYERCHANGE_READY) {
mainGame->chkHostPrepReady[pos]->setChecked(true);
if(pos == selftype) {
mainGame->btnHostPrepReady->setVisible(false);
mainGame->btnHostPrepNotReady->setVisible(true);
}
} else if(state == PLAYERCHANGE_NOTREADY) {
mainGame->chkHostPrepReady[pos]->setChecked(false);
if(pos == selftype) {
mainGame->btnHostPrepReady->setVisible(true);
mainGame->btnHostPrepNotReady->setVisible(false);
}
} else if(state == PLAYERCHANGE_LEAVE) {
mainGame->stHostPrepDuelist[pos]->setText(L"");
mainGame->chkHostPrepReady[pos]->setChecked(false);
} else if(state == PLAYERCHANGE_OBSERVE) {
watching++;
mainGame->stHostPrepDuelist[pos]->setText(L"");
mainGame->chkHostPrepReady[pos]->setChecked(false);
mainGame->stHostPrepOB->setText(epro::format(L"{} {}", gDataManager->GetSysString(1253), watching).data());
}
mainGame->btnHostPrepStart->setVisible(is_host);
mainGame->btnHostPrepStart->setEnabled(is_host && CheckReady());
break;
}
case STOC_HS_WATCH_CHANGE: {
auto pkt = BufferIO::getStruct<STOC_HS_WatchChange>(pdata, len);
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
watching = pkt.watch_count;
mainGame->stHostPrepOB->setText(epro::format(L"{} {}", gDataManager->GetSysString(1253), watching).data());
break;
}
case STOC_NEW_REPLAY: {
last_replay.pheader.base.id = 0;
uint32_t replay_header_size;
if(ExtendedReplayHeader::ParseReplayHeader(pdata, len, last_replay.pheader, &replay_header_size)) {
const auto total_data_size = len - (replay_header_size - sizeof(uint8_t));
last_replay.comp_data.resize(total_data_size);
memcpy(last_replay.comp_data.data(), pdata + replay_header_size, total_data_size);
}
break;
}
case STOC_CATCHUP: {
mainGame->dInfo.isCatchingUp = !!BufferIO::Read<uint8_t>(pdata);
if(!mainGame->dInfo.isCatchingUp) {
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->dField.RefreshAllCards();
}
break;
}
case STOC_REMATCH: {
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->dInfo.checkRematch = true;
if(mainGame->wQuery->isVisible())
mainGame->HideElement(mainGame->wQuery);
if(mainGame->wOptions->isVisible())
mainGame->HideElement(mainGame->wOptions);
if(mainGame->wPosSelect->isVisible())
mainGame->HideElement(mainGame->wPosSelect);
if(mainGame->wCardSelect->isVisible())
mainGame->HideElement(mainGame->wCardSelect);
if(mainGame->wCardDisplay->isVisible())
mainGame->HideElement(mainGame->wCardDisplay);
if(mainGame->wANNumber->isVisible())
mainGame->HideElement(mainGame->wANNumber);
if(mainGame->wANCard->isVisible())
mainGame->HideElement(mainGame->wANCard);
mainGame->stQMessage->setText(gDataManager->GetSysString(1989).data());
mainGame->PopupElement(mainGame->wQuery);
break;
}
}
}
bool DuelClient::CheckReady() {
bool ready1 = false, ready2 = false;
for(int i = 0; i < mainGame->dInfo.team1; i++) {
if(mainGame->stHostPrepDuelist[i]->getText()[0]) {
if(ready1 = mainGame->chkHostPrepReady[i]->isChecked(); !ready1) {
return false;
}
} else if(!mainGame->dInfo.isRelay) {
return false;
}
}
for(int i = mainGame->dInfo.team1; i < mainGame->dInfo.team1 + mainGame->dInfo.team2; i++) {
if(mainGame->stHostPrepDuelist[i]->getText()[0]) {
if(ready2 = mainGame->chkHostPrepReady[i]->isChecked(); !ready2) {
return false;
}
} else if(!mainGame->dInfo.isRelay) {
return false;
}
}
return ready1 && ready2;
}
std::pair<uint32_t, uint32_t> DuelClient::GetPlayersCount() {
uint32_t count1 = 0, count2 = 0;
for(int i = 0; i < mainGame->dInfo.team1; i++) {
if(mainGame->stHostPrepDuelist[i]->getText()[0]) {
count1++;
}
}
for(int i = mainGame->dInfo.team1; i < mainGame->dInfo.team1 + mainGame->dInfo.team2; i++) {
if(mainGame->stHostPrepDuelist[i]->getText()[0]) {
count2++;
}
}
return { count1, count2 };
}
template<typename T1, typename T2>
inline T2 CompatRead(uint8_t*& buf) {
if(mainGame->dInfo.compat_mode)
return static_cast<T2>(BufferIO::Read<T1>(buf));
return BufferIO::Read<T2>(buf);
}
template<typename T1, typename T2>
inline T2 CompatRead(const uint8_t*& buf) {
if(mainGame->dInfo.compat_mode)
return static_cast<T2>(BufferIO::Read<T1>(buf));
return BufferIO::Read<T2>(buf);
}
inline void Play(SoundManager::SFX sound) {
if(!mainGame->dInfo.isCatchingUp)
gSoundManager->PlaySoundEffect(sound);
}
inline bool PlayChant(SoundManager::CHANT sound, uint32_t code) {
if(!mainGame->dInfo.isCatchingUp)
return gSoundManager->PlayChant(sound, code);
return true;
}
inline std::unique_lock<epro::mutex> LockIf() {
if(!mainGame->dInfo.isCatchingUp || !mainGame->dInfo.isReplay)
return std::unique_lock<epro::mutex>(mainGame->gMutex);
return std::unique_lock<epro::mutex>();
}
int DuelClient::ClientAnalyze(const uint8_t* msg, uint32_t len) {
auto& curMsg = mainGame->dInfo.curMsg;
auto PerformQueuedPanelConfirm = [&] {
if(mainGame->dField.queued_panel_confirm_cards.empty())
return;
if(!mainGame->dInfo.isCatchingUp && !mainGame->dInfo.isReplay) {
std::swap(mainGame->dField.selectable_cards, mainGame->dField.queued_panel_confirm_cards);
auto old = std::exchange(curMsg, MSG_CONFIRM_CARDS);
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->wCardSelect->setText(epro::sprintf(gDataManager->GetSysString(208), mainGame->dField.selectable_cards.size()).data());
mainGame->dField.ShowSelectCard(true);
mainGame->actionSignal.Wait(lock);
std::swap(mainGame->dField.selectable_cards, mainGame->dField.queued_panel_confirm_cards);
std::swap(old, curMsg);
mainGame->dField.queued_panel_confirm_cards.clear();
}
};
auto PerformQueuedPanelConfirmIfDifferent = [&](auto& select_vector) {
if(mainGame->dField.queued_panel_confirm_cards.empty() ||
(select_vector.size() == mainGame->dField.queued_panel_confirm_cards.size()
&& std::memcmp(select_vector.data(), mainGame->dField.queued_panel_confirm_cards.data(), sizeof(ClientCard*)) == 0)) {
mainGame->dField.queued_panel_confirm_cards.clear();
return;
}
PerformQueuedPanelConfirm();
};
const auto* pbuf = msg;
if(!mainGame->dInfo.isReplay && !mainGame->dInfo.isSingleMode) {
curMsg = BufferIO::Read<uint8_t>(pbuf);
len--;
if(curMsg != MSG_WAITING) {
replay_stream.emplace_back(curMsg, pbuf, len);
}
}
mainGame->wCmdMenu->setVisible(false);
if(curMsg != MSG_HINT && curMsg != MSG_SELECT_CARD && curMsg != MSG_SELECT_UNSELECT_CARD && curMsg != MSG_SELECT_SUM)
PerformQueuedPanelConfirm();
if(!mainGame->dInfo.isReplay && curMsg != MSG_WAITING) {
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->waitFrame = -1;
mainGame->stHintMsg->setVisible(false);
if(mainGame->wCardSelect->isVisible()) {
mainGame->HideElement(mainGame->wCardSelect);
mainGame->WaitFrameSignal(11, lock);
}
/*if(mainGame->wCardDisplay->isVisible()) {
mainGame->HideElement(mainGame->wCardDisplay);
mainGame->WaitFrameSignal(11, lock);
}*/
if(mainGame->wOptions->isVisible()) {
mainGame->HideElement(mainGame->wOptions);
mainGame->WaitFrameSignal(11, lock);
}
}
if(mainGame->dInfo.time_player == 1)
mainGame->dInfo.time_player = 2;
if(is_swapping) {
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->dField.ReplaySwap();
is_swapping = false;
}
switch(curMsg) {
case MSG_RETRY: {
if(!mainGame->dInfo.compat_mode) {
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->stMessage->setText(gDataManager->GetSysString(1434).data());
mainGame->PopupElement(mainGame->wMessage);
mainGame->actionSignal.Wait(lock);
return true;
} else {
gSoundManager->StopSounds();
{
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->stMessage->setText(gDataManager->GetSysString(1434).data());
mainGame->PopupElement(mainGame->wMessage);
mainGame->actionSignal.Wait(lock);
mainGame->closeDuelWindow = true;
mainGame->closeDoneSignal.Wait(lock);
lock.unlock();
ReplayPrompt(true);
lock.lock();
mainGame->dField.Clear();
mainGame->dInfo.isInLobby = false;
mainGame->dInfo.isInDuel = false;
mainGame->dInfo.checkRematch = false;
mainGame->dInfo.isStarted = false;
mainGame->btnCreateHost->setEnabled(true);
mainGame->btnJoinHost->setEnabled(true);
mainGame->btnJoinCancel->setEnabled(true);
mainGame->stTip->setVisible(false);
gSoundManager->StopSounds();
mainGame->device->setEventReceiver(&mainGame->menuHandler);
if(mainGame->isHostingOnline) {
mainGame->ShowElement(mainGame->wRoomListPlaceholder);
} else {
mainGame->ShowElement(mainGame->wLanWindow);
}
mainGame->SetMessageWindow();
}
connect_state |= 0x100;
event_base_loopbreak(client_base);
return false;
}
}
case MSG_HINT: {
const auto type = BufferIO::Read<uint8_t>(pbuf);
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
uint64_t data = CompatRead<uint32_t, uint64_t>(pbuf);
if(type != HINT_SELECTMSG)
PerformQueuedPanelConfirm();
if(mainGame->dInfo.isCatchingUp && type < HINT_SKILL)
return true;
if(mainGame->dInfo.isReplay && (type == HINT_EVENT || type == HINT_MESSAGE || type == HINT_SELECTMSG || type == HINT_EFFECT))
return true;
switch (type) {
case HINT_EVENT: {
event_string = gDataManager->GetDesc(data, mainGame->dInfo.compat_mode).data();
break;
}
case HINT_MESSAGE: {
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->stMessage->setText(gDataManager->GetDesc(data, mainGame->dInfo.compat_mode).data());
mainGame->PopupElement(mainGame->wMessage);
mainGame->actionSignal.Wait(lock);
break;
}
case HINT_SELECTMSG: {
select_hint = data;
break;
}
case HINT_OPSELECTED: {
std::wstring text(epro::format(gDataManager->GetSysString(player == 0 ? 1510 : 1512), gDataManager->GetDesc(data, mainGame->dInfo.compat_mode)));
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->AddLog(text);
mainGame->stACMessage->setText(text.data());
mainGame->PopupElement(mainGame->wACMessage, 20);
mainGame->WaitFrameSignal(40, lock);
break;
}
case HINT_EFFECT: {
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->showcardcode = data;
mainGame->showcarddif = 0;
mainGame->showcard = 1;
mainGame->WaitFrameSignal(30, lock);
break;
}
case HINT_RACE: {
std::wstring text(epro::format(gDataManager->GetSysString(1511), gDataManager->FormatRace(data)));
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->AddLog(text);
mainGame->stACMessage->setText(text.data());
mainGame->PopupElement(mainGame->wACMessage, 20);
mainGame->WaitFrameSignal(40, lock);
break;
}
case HINT_ATTRIB: {
std::wstring text(epro::format(gDataManager->GetSysString(1511), gDataManager->FormatAttribute(data)));
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->AddLog(text);
mainGame->stACMessage->setText(text.data());
mainGame->PopupElement(mainGame->wACMessage, 20);
mainGame->WaitFrameSignal(40, lock);
break;
}
case HINT_CODE: {
std::wstring text(epro::format(gDataManager->GetSysString(1511), gDataManager->GetName(data)));
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->AddLog(text);
mainGame->stACMessage->setText(text.data());
mainGame->PopupElement(mainGame->wACMessage, 20);
mainGame->WaitFrameSignal(40, lock);
break;
}
case HINT_NUMBER: {
std::wstring text(epro::format(gDataManager->GetSysString(1512), data));
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->AddLog(text);
mainGame->stACMessage->setText(text.data());
mainGame->PopupElement(mainGame->wACMessage, 20);
mainGame->WaitFrameSignal(40, lock);
break;
}
case HINT_CARD: {
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->showcardcode = data;
mainGame->showcarddif = 0;
mainGame->showcard = 1;
Play(SoundManager::SFX::ACTIVATE);
mainGame->WaitFrameSignal(30, lock);
break;
}
case HINT_ZONE: {
if(player == 1)
data = (data >> 16) | (data << 16);
std::vector<std::wstring> tmp;
for(uint32_t filter = 0x1; filter != 0; filter <<= 1) {
if(uint32_t s = filter & data) {
uint32_t player_string{};
uint32_t zone_string{};
uint32_t seq = 1;
if(s & 0x60) {
player_string = 1081;
data &= ~0x600000;
} else if(s & 0xffff) {
player_string = 102;
} else if(s & 0xffff0000) {
player_string = 103;
s >>= 16;
}
if(s & 0x1f) {
zone_string = 1002;
} else if(s & 0xff00) {
s >>= 8;
if(s & 0x1f)
zone_string = 1003;
else if(s & 0x20)
zone_string = 1008;
else if(s & 0xc0)
zone_string = 1009;
}
for(int i = 0x1; i < 0x100; i <<= 1) {
if(s & i)
break;
++seq;
}
const auto tmp_string = epro::format(L"{}{}({})", gDataManager->GetSysString(player_string), gDataManager->GetSysString(zone_string), seq);
tmp.push_back(epro::format(gDataManager->GetSysString(1512), tmp_string));
}
}
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
for(const auto& str : tmp)
mainGame->AddLog(str);
mainGame->dField.selectable_field = data;
mainGame->WaitFrameSignal(40, lock);
mainGame->dField.selectable_field = 0;
break;
}
case HINT_SKILL: {
auto lock = LockIf();
auto& pcard = mainGame->dField.skills[player];
if(!pcard) {
pcard = new ClientCard{};
pcard->controler = player;
pcard->sequence = 0;
pcard->position = POS_FACEUP;
pcard->location = LOCATION_SKILL;
}
pcard->SetCode(data & 0xffffffff);
if(!mainGame->dInfo.isCatchingUp) {
mainGame->dField.MoveCard(pcard, 10);
mainGame->WaitFrameSignal(11, lock);
}
break;
}
case HINT_SKILL_COVER: {
auto lock = LockIf();
auto& pcard = mainGame->dField.skills[player];
if(!pcard) {
pcard = new ClientCard{};
pcard->controler = player;
pcard->sequence = 0;
pcard->position = POS_FACEDOWN;
pcard->location = LOCATION_SKILL;
}
pcard->cover = data & 0xffffffff;
pcard->SetCode((data >> 32) & 0xffffffff);
if(!mainGame->dInfo.isCatchingUp) {
mainGame->dField.MoveCard(pcard, 10);
mainGame->WaitFrameSignal(11, lock);
}
break;
}
case HINT_SKILL_FLIP: {
auto lock = LockIf();
auto& pcard = mainGame->dField.skills[player];
if(!pcard) {
pcard = new ClientCard{};
pcard->controler = player;
pcard->sequence = 0;
pcard->position = POS_FACEDOWN;
pcard->location = LOCATION_SKILL;
}
pcard->SetCode(data & 0xffffffff);
if(data & 0x100000000) {
pcard->position = POS_FACEUP;
} else if(data & 0x200000000) {
pcard->position = POS_FACEDOWN;
}
if(!mainGame->dInfo.isCatchingUp) {
mainGame->dField.MoveCard(pcard, 10);
mainGame->WaitFrameSignal(11, lock);
}
break;
}
case HINT_SKILL_REMOVE: {
auto lock = LockIf();
auto& pcard = mainGame->dField.skills[player];
if(pcard) {
if(!mainGame->dInfo.isCatchingUp) {
int frames = 20;
if(gGameConfig->quick_animation)
frames = 12;
mainGame->dField.FadeCard(pcard, 5, frames);
mainGame->WaitFrameSignal(frames, lock);
}
if(pcard == mainGame->dField.hovered_card)
mainGame->dField.hovered_card = nullptr;
delete pcard;
pcard = nullptr;
}
break;
}
}
break;
}
case MSG_WIN: {
uint8_t player = BufferIO::Read<uint8_t>(pbuf);
uint8_t type = BufferIO::Read<uint8_t>(pbuf);
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->showcarddif = 110;
mainGame->showcardp = 0;
mainGame->dInfo.vic_string = L"";
mainGame->showcardcode = 3;
std::wstring formatted_string = L"";
if(player < 2) {
player = mainGame->LocalPlayer(player);
mainGame->showcardcode = player + 1;
if(match_kill)
mainGame->dInfo.vic_string = epro::sprintf(gDataManager->GetVictoryString(0x20), gDataManager->GetName(match_kill));
else if(type < 0x10) {
auto curplayer = mainGame->dInfo.current_player[1 - player];
auto& self = mainGame->dInfo.isTeam1 ? mainGame->dInfo.selfnames : mainGame->dInfo.opponames;
auto& oppo = mainGame->dInfo.isTeam1 ? mainGame->dInfo.opponames : mainGame->dInfo.selfnames;
auto& names = (player == 0) ? oppo : self;
mainGame->dInfo.vic_string = epro::format(L"[{}] {}", names[curplayer], gDataManager->GetVictoryString(type));
} else
mainGame->dInfo.vic_string = gDataManager->GetVictoryString(type).data();
}
mainGame->showcard = 101;
mainGame->WaitFrameSignal(120, lock);
mainGame->dInfo.vic_string = L"";
mainGame->showcard = 0;
break;
}
case MSG_WAITING: {
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->waitFrame = 0;
mainGame->stHintMsg->setText(gDataManager->GetSysString(1390).data());
mainGame->stHintMsg->setVisible(true);
return true;
}
case MSG_START: {
const auto playertype = BufferIO::Read<uint8_t>(pbuf);
if(mainGame->dInfo.compat_mode)
/*duel_rule = */BufferIO::Read<uint8_t>(pbuf);
auto lock = LockIf();
mainGame->wPhase->setVisible(true);
if(!mainGame->dInfo.isCatchingUp) {
mainGame->showcardcode = 11;
mainGame->showcarddif = 30;
mainGame->showcardp = 0;
mainGame->showcard = 101;
mainGame->WaitFrameSignal(40, lock);
mainGame->showcard = 0;
}
mainGame->dInfo.isStarted = true;
mainGame->dInfo.isFirst = (playertype & 0xf) ? false : true;
if(playertype & 0xf0)
mainGame->dInfo.player_type = 7;
if(!mainGame->dInfo.isRelay) {
if(mainGame->dInfo.isFirst) {
if(mainGame->dInfo.isTeam1)
mainGame->dInfo.current_player[1] = mainGame->dInfo.team2 - 1;
else
mainGame->dInfo.current_player[1] = mainGame->dInfo.team1 - 1;
} else {
if(mainGame->dInfo.isTeam1)
mainGame->dInfo.current_player[0] = mainGame->dInfo.team1 - 1;
else
mainGame->dInfo.current_player[0] = mainGame->dInfo.team2 - 1;
}
}
mainGame->dInfo.lp[mainGame->LocalPlayer(0)] = BufferIO::Read<uint32_t>(pbuf);
mainGame->dInfo.lp[mainGame->LocalPlayer(1)] = BufferIO::Read<uint32_t>(pbuf);
if(mainGame->dInfo.lp[mainGame->LocalPlayer(0)] > 0)
mainGame->dInfo.startlp = mainGame->dInfo.lp[mainGame->LocalPlayer(0)];
else
mainGame->dInfo.startlp = 8000;
mainGame->dInfo.strLP[0] = epro::to_wstring(mainGame->dInfo.lp[0]);
mainGame->dInfo.strLP[1] = epro::to_wstring(mainGame->dInfo.lp[1]);
uint16_t deckc = BufferIO::Read<uint16_t>(pbuf);
uint16_t extrac = BufferIO::Read<uint16_t>(pbuf);
mainGame->dField.Initial(mainGame->LocalPlayer(0), deckc, extrac);
deckc = BufferIO::Read<uint16_t>(pbuf);
extrac = BufferIO::Read<uint16_t>(pbuf);
mainGame->dField.Initial(mainGame->LocalPlayer(1), deckc, extrac);
mainGame->dInfo.turn = 0;
mainGame->dInfo.is_shuffling = false;
return true;
}
case MSG_UPDATE_DATA: {
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto location = BufferIO::Read<uint8_t>(pbuf);
auto lock = LockIf();
mainGame->dField.UpdateFieldCard(player, location, pbuf, len - 2);
return true;
}
case MSG_UPDATE_CARD: {
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto loc = BufferIO::Read<uint8_t>(pbuf);
const auto seq = BufferIO::Read<uint8_t>(pbuf);
auto lock = LockIf();
mainGame->dField.UpdateCard(player, loc, seq, pbuf, len - 3);
break;
}
case MSG_SELECT_BATTLECMD: {
/*uint8_t selecting_player = */BufferIO::Read<uint8_t>(pbuf);
uint64_t desc;
uint32_t code, count, seq;
uint8_t con, loc/*, diratt*/;
ClientCard* pcard;
mainGame->dField.activatable_cards.clear();
mainGame->dField.activatable_descs.clear();
mainGame->dField.conti_cards.clear();
//cards with effects that can be activated, can be an arbitrary size
count = CompatRead<uint8_t, uint32_t>(pbuf);
for(uint32_t i = 0; i < count; ++i) {
code = BufferIO::Read<uint32_t>(pbuf);
con = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
loc = BufferIO::Read<uint8_t>(pbuf);
seq = CompatRead<uint8_t, uint32_t>(pbuf);
desc = CompatRead<uint32_t, uint64_t>(pbuf);
pcard = mainGame->dField.GetCard(con, loc, seq);
uint8_t flag = EFFECT_CLIENT_MODE_NORMAL;
if(!mainGame->dInfo.compat_mode) {
flag = BufferIO::Read<uint8_t>(pbuf);
} else if(code & 0x80000000) {
flag = EFFECT_CLIENT_MODE_RESOLVE;
code &= 0x7fffffff;
}
if(!pcard) {
pcard = new ClientCard{};
pcard->code = code;
pcard->controler = con;
pcard->sequence = static_cast<uint32_t>(mainGame->dField.limbo_temp.size());
mainGame->dField.limbo_temp.push_back(pcard);
}
mainGame->dField.activatable_cards.push_back(pcard);
mainGame->dField.activatable_descs.push_back(std::make_pair(desc, flag));
if(flag == EFFECT_CLIENT_MODE_RESOLVE) {
pcard->chain_code = code;
mainGame->dField.conti_cards.push_back(pcard);
mainGame->dField.conti_act = true;
} else {
pcard->cmdFlag |= COMMAND_ACTIVATE;
if (pcard->location == LOCATION_GRAVE)
mainGame->dField.grave_act[pcard->controler] = true;
else if (pcard->location == LOCATION_REMOVED)
mainGame->dField.remove_act[pcard->controler] = true;
else if (pcard->location == LOCATION_EXTRA)
mainGame->dField.extra_act[pcard->controler] = true;
}
}
mainGame->dField.attackable_cards.clear();
//cards that can attack, will remain under 255
count = CompatRead<uint8_t, uint32_t>(pbuf);
for(uint32_t i = 0; i < count; ++i) {
/*code = */BufferIO::Read<uint32_t>(pbuf);
con = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
loc = BufferIO::Read<uint8_t>(pbuf);
seq = BufferIO::Read<uint8_t>(pbuf);
/*diratt = */BufferIO::Read<uint8_t>(pbuf);
pcard = mainGame->dField.GetCard(con, loc, seq);
mainGame->dField.attackable_cards.push_back(pcard);
pcard->cmdFlag |= COMMAND_ATTACK;
}
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
if(BufferIO::Read<uint8_t>(pbuf)) {
mainGame->btnM2->setVisible(true);
mainGame->btnM2->setSubElement(true);
mainGame->btnM2->setEnabled(true);
mainGame->btnM2->setPressed(false);
}
if(BufferIO::Read<uint8_t>(pbuf)) {
mainGame->btnEP->setVisible(true);
mainGame->btnEP->setSubElement(true);
mainGame->btnEP->setEnabled(true);
mainGame->btnEP->setPressed(false);
}
return false;
}
case MSG_SELECT_IDLECMD: {
/*uint8_t selecting_player = */BufferIO::Read<uint8_t>(pbuf);
uint32_t code, count, seq;
uint8_t con, loc;
uint64_t desc;
ClientCard* pcard;
mainGame->dField.summonable_cards.clear();
//cards that can be normal summoned, can be an arbitrary size
count = CompatRead<uint8_t, uint32_t>(pbuf);
for(uint32_t i = 0; i < count; ++i) {
code = BufferIO::Read<uint32_t>(pbuf);
con = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
loc = BufferIO::Read<uint8_t>(pbuf);
seq = CompatRead<uint8_t, uint32_t>(pbuf);
pcard = mainGame->dField.GetCard(con, loc, seq);
mainGame->dField.summonable_cards.push_back(pcard);
pcard->cmdFlag |= COMMAND_SUMMON;
}
mainGame->dField.spsummonable_cards.clear();
//cards that can be special summoned, can be an arbitrary size
count = CompatRead<uint8_t, uint32_t>(pbuf);
for(uint32_t i = 0; i < count; ++i) {
code = BufferIO::Read<uint32_t>(pbuf);
con = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
loc = BufferIO::Read<uint8_t>(pbuf);
seq = CompatRead<uint8_t, uint32_t>(pbuf);
pcard = mainGame->dField.GetCard(con, loc, seq);
mainGame->dField.spsummonable_cards.push_back(pcard);
pcard->cmdFlag |= COMMAND_SPSUMMON;
switch(pcard->location) {
case LOCATION_DECK:
pcard->SetCode(code);
mainGame->dField.deck_act[pcard->controler] = true;
break;
case LOCATION_GRAVE:
mainGame->dField.grave_act[pcard->controler] = true;
break;
case LOCATION_REMOVED:
mainGame->dField.remove_act[pcard->controler] = true;
break;
case LOCATION_EXTRA:
mainGame->dField.extra_act[pcard->controler] = true;
break;
case LOCATION_SZONE: {
if((pcard->type & TYPE_PENDULUM) && !pcard->equipTarget && pcard->sequence == mainGame->dInfo.GetPzoneIndex(0))
mainGame->dField.pzone_act[pcard->controler] = true;
break;
}
default: break;
}
}
mainGame->dField.reposable_cards.clear();
//cards whose position can be changed, will remain under 255
count = CompatRead<uint8_t, uint32_t>(pbuf);
for(uint32_t i = 0; i < count; ++i) {
code = BufferIO::Read<uint32_t>(pbuf);
con = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
loc = BufferIO::Read<uint8_t>(pbuf);
seq = BufferIO::Read<uint8_t>(pbuf);
pcard = mainGame->dField.GetCard(con, loc, seq);
mainGame->dField.reposable_cards.push_back(pcard);
pcard->cmdFlag |= COMMAND_REPOS;
}
mainGame->dField.msetable_cards.clear();
//cards that can be set in the mzone, can be an arbitrary size
count = CompatRead<uint8_t, uint32_t>(pbuf);
for(uint32_t i = 0; i < count; ++i) {
code = BufferIO::Read<uint32_t>(pbuf);
con = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
loc = BufferIO::Read<uint8_t>(pbuf);
seq = CompatRead<uint8_t, uint32_t>(pbuf);
pcard = mainGame->dField.GetCard(con, loc, seq);
mainGame->dField.msetable_cards.push_back(pcard);
pcard->cmdFlag |= COMMAND_MSET;
}
mainGame->dField.ssetable_cards.clear();
//cards that can be set in the szone, can be an arbitrary size
count = CompatRead<uint8_t, uint32_t>(pbuf);
for(uint32_t i = 0; i < count; ++i) {
code = BufferIO::Read<uint32_t>(pbuf);
con = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
loc = BufferIO::Read<uint8_t>(pbuf);
seq = CompatRead<uint8_t, uint32_t>(pbuf);
pcard = mainGame->dField.GetCard(con, loc, seq);
mainGame->dField.ssetable_cards.push_back(pcard);
pcard->cmdFlag |= COMMAND_SSET;
}
mainGame->dField.activatable_cards.clear();
mainGame->dField.activatable_descs.clear();
mainGame->dField.conti_cards.clear();
//cards with effects that can be activated, can be an arbitrary size
count = CompatRead<uint8_t, uint32_t>(pbuf);
for(uint32_t i = 0; i < count; ++i) {
code = BufferIO::Read<uint32_t>(pbuf);
con = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
loc = BufferIO::Read<uint8_t>(pbuf);
seq = CompatRead<uint8_t, uint32_t>(pbuf);
desc = CompatRead<uint32_t, uint64_t>(pbuf);
pcard = mainGame->dField.GetCard(con, loc, seq);
uint8_t flag = EFFECT_CLIENT_MODE_NORMAL;
if(!mainGame->dInfo.compat_mode) {
flag = BufferIO::Read<uint8_t>(pbuf);
} else if(code & 0x80000000) {
flag = EFFECT_CLIENT_MODE_RESOLVE;
code &= 0x7fffffff;
}
if(!pcard) {
pcard = new ClientCard{};
pcard->code = code;
pcard->controler = con;
pcard->sequence = static_cast<uint32_t>(mainGame->dField.limbo_temp.size());
mainGame->dField.limbo_temp.push_back(pcard);
}
mainGame->dField.activatable_cards.push_back(pcard);
mainGame->dField.activatable_descs.push_back(std::make_pair(desc, flag));
if(flag == EFFECT_CLIENT_MODE_RESOLVE) {
pcard->chain_code = code;
mainGame->dField.conti_cards.push_back(pcard);
mainGame->dField.conti_act = true;
} else {
pcard->cmdFlag |= COMMAND_ACTIVATE;
if (pcard->location == LOCATION_GRAVE)
mainGame->dField.grave_act[pcard->controler] = true;
else if (pcard->location == LOCATION_REMOVED)
mainGame->dField.remove_act[pcard->controler] = true;
else if (pcard->location == LOCATION_EXTRA)
mainGame->dField.extra_act[pcard->controler] = true;
}
}
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
if(BufferIO::Read<uint8_t>(pbuf)) {
mainGame->btnBP->setVisible(true);
mainGame->btnBP->setSubElement(true);
mainGame->btnBP->setEnabled(true);
mainGame->btnBP->setPressed(false);
}
if(BufferIO::Read<uint8_t>(pbuf)) {
mainGame->btnEP->setVisible(true);
mainGame->btnEP->setSubElement(true);
mainGame->btnEP->setEnabled(true);
mainGame->btnEP->setPressed(false);
}
if (BufferIO::Read<uint8_t>(pbuf)) {
mainGame->btnShuffle->setVisible(true);
} else {
mainGame->btnShuffle->setVisible(false);
}
return false;
}
case MSG_SELECT_EFFECTYN: {
/*uint8_t selecting_player = */BufferIO::Read<uint8_t>(pbuf);
uint32_t code = BufferIO::Read<uint32_t>(pbuf);
CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info.controler = mainGame->LocalPlayer(info.controler);
uint64_t desc = CompatRead<uint32_t, uint64_t>(pbuf);
std::wstring text;
if(desc == 0) {
text = epro::format(L"{}\n{}", event_string,
epro::sprintf(gDataManager->GetSysString(200), gDataManager->GetName(code), gDataManager->FormatLocation(info.location, info.sequence)));
} else if(desc == 221) {
text = epro::format(L"{}\n{}\n{}", event_string,
epro::sprintf(gDataManager->GetSysString(221), gDataManager->GetName(code), gDataManager->FormatLocation(info.location, info.sequence)),
gDataManager->GetSysString(223));
} else {
text = epro::sprintf(gDataManager->GetDesc(desc, mainGame->dInfo.compat_mode), gDataManager->GetName(code));
}
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
ClientCard* pcard = mainGame->dField.GetCard(info.controler, info.location, info.sequence);
if (pcard->code != code)
pcard->SetCode(code);
if(info.location != LOCATION_DECK) {
pcard->is_highlighting = true;
mainGame->dField.highlighting_card = pcard;
}
mainGame->stQMessage->setText(text.data());
mainGame->PopupElement(mainGame->wQuery);
return false;
}
case MSG_SELECT_YESNO: {
/*uint8_t selecting_player = */BufferIO::Read<uint8_t>(pbuf);
uint64_t desc = CompatRead<uint32_t, uint64_t>(pbuf);
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->dField.highlighting_card = 0;
mainGame->stQMessage->setText(gDataManager->GetDesc(desc, mainGame->dInfo.compat_mode).data());
mainGame->PopupElement(mainGame->wQuery);
return false;
}
case MSG_SELECT_OPTION: {
/*uint8_t selecting_player = */BufferIO::Read<uint8_t>(pbuf);
uint8_t count = BufferIO::Read<uint8_t>(pbuf);
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->dField.select_options.clear();
for(int i = 0; i < count; ++i)
mainGame->dField.select_options.push_back(CompatRead<uint32_t, uint64_t>(pbuf));
mainGame->dField.ShowSelectOption(select_hint, false);
select_hint = 0;
return false;
}
case MSG_SELECT_CARD: {
/*uint8_t selecting_player = */BufferIO::Read<uint8_t>(pbuf);
mainGame->dField.select_cancelable = BufferIO::Read<uint8_t>(pbuf) != 0;
mainGame->dField.select_min = CompatRead<uint8_t, uint32_t>(pbuf);
mainGame->dField.select_max = CompatRead<uint8_t, uint32_t>(pbuf);
uint32_t count = CompatRead<uint8_t, uint32_t>(pbuf);
mainGame->dField.selectable_cards.clear();
mainGame->dField.selected_cards.clear();
uint32_t code;
bool panelmode = false;
bool select_ready = mainGame->dField.select_min == 0;
mainGame->dField.select_ready = select_ready;
ClientCard* pcard;
for(uint32_t i = 0; i < count; ++i) {
code = BufferIO::Read<uint32_t>(pbuf);
CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info.controler = mainGame->LocalPlayer(info.controler);
if ((info.location & LOCATION_OVERLAY) > 0)
pcard = mainGame->dField.GetCard(info.controler, info.location & (~LOCATION_OVERLAY) & 0xff, info.sequence)->overlayed[info.position];
else if (info.location == 0) {
pcard = new ClientCard{};
pcard->sequence = static_cast<uint32_t>(mainGame->dField.limbo_temp.size());
mainGame->dField.limbo_temp.push_back(pcard);
panelmode = true;
} else
pcard = mainGame->dField.GetCard(info.controler, info.location, info.sequence);
if (code != 0 && pcard->code != code)
pcard->SetCode(code);
pcard->select_seq = i;
mainGame->dField.selectable_cards.push_back(pcard);
pcard->is_selectable = true;
pcard->is_selected = false;
if (info.location & 0xf1)
panelmode = true;
}
std::sort(mainGame->dField.selectable_cards.begin(), mainGame->dField.selectable_cards.end(), ClientCard::client_card_sort);
PerformQueuedPanelConfirmIfDifferent(mainGame->dField.selectable_cards);
std::wstring text = epro::format(L"{}({}-{})", gDataManager->GetDesc(select_hint ? select_hint : 560, mainGame->dInfo.compat_mode),
mainGame->dField.select_min, mainGame->dField.select_max);
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
select_hint = 0;
if (panelmode) {
mainGame->wCardSelect->setText(text.data());
mainGame->dField.ShowSelectCard(select_ready);
} else {
mainGame->stHintMsg->setText(text.data());
mainGame->stHintMsg->setVisible(true);
}
if (mainGame->dField.select_cancelable) {
mainGame->dField.ShowCancelOrFinishButton(1);
} else if (select_ready) {
mainGame->dField.ShowCancelOrFinishButton(2);
} else {
mainGame->dField.ShowCancelOrFinishButton(0);
}
return false;
}
case MSG_SELECT_UNSELECT_CARD: {
/*uint8_t selecting_player = */BufferIO::Read<uint8_t>(pbuf);
bool finishable = BufferIO::Read<uint8_t>(pbuf) != 0;;
bool cancelable = BufferIO::Read<uint8_t>(pbuf) != 0;
mainGame->dField.select_cancelable = finishable || cancelable;
mainGame->dField.select_min = CompatRead<uint8_t, uint32_t>(pbuf);
mainGame->dField.select_max = CompatRead<uint8_t, uint32_t>(pbuf);
uint32_t count1 = CompatRead<uint8_t, uint32_t>(pbuf);
mainGame->dField.selectable_cards.clear();
mainGame->dField.selected_cards.clear();
uint32_t code;
bool panelmode = false;
mainGame->dField.select_ready = false;
ClientCard* pcard;
for(uint32_t i = 0; i < count1; ++i) {
code = BufferIO::Read<uint32_t>(pbuf);
CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info.controler = mainGame->LocalPlayer(info.controler);
if ((info.location & LOCATION_OVERLAY) > 0)
pcard = mainGame->dField.GetCard(info.controler, info.location & (~LOCATION_OVERLAY) & 0xff, info.sequence)->overlayed[info.position];
else if (info.location == 0) {
pcard = new ClientCard{};
pcard->sequence = static_cast<uint32_t>(mainGame->dField.limbo_temp.size());
mainGame->dField.limbo_temp.push_back(pcard);
panelmode = true;
} else
pcard = mainGame->dField.GetCard(info.controler, info.location, info.sequence);
if (code != 0 && pcard->code != code)
pcard->SetCode(code);
pcard->select_seq = i;
mainGame->dField.selectable_cards.push_back(pcard);
pcard->is_selectable = true;
pcard->is_selected = false;
if (info.location & 0xf1)
panelmode = true;
}
uint32_t count2 = CompatRead<uint8_t, uint32_t>(pbuf);
for(uint32_t i = count1; i < count1 + count2; ++i) {
code = BufferIO::Read<uint32_t>(pbuf);
CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info.controler = mainGame->LocalPlayer(info.controler);
if ((info.location & LOCATION_OVERLAY) > 0)
pcard = mainGame->dField.GetCard(info.controler, info.location & (~LOCATION_OVERLAY) & 0xff, info.sequence)->overlayed[info.position];
else if (info.location == 0) {
pcard = new ClientCard{};
pcard->sequence = static_cast<uint32_t>(mainGame->dField.limbo_temp.size());
mainGame->dField.limbo_temp.push_back(pcard);
panelmode = true;
} else
pcard = mainGame->dField.GetCard(info.controler, info.location, info.sequence);
if (code != 0 && pcard->code != code)
pcard->SetCode(code);
pcard->select_seq = i;
mainGame->dField.selectable_cards.push_back(pcard);
pcard->is_selectable = true;
pcard->is_selected = true;
if (info.location & 0xf1)
panelmode = true;
}
std::sort(mainGame->dField.selectable_cards.begin(), mainGame->dField.selectable_cards.end(), ClientCard::client_card_sort);
PerformQueuedPanelConfirmIfDifferent(mainGame->dField.selectable_cards);
std::wstring text = epro::format(L"{}({}-{})", gDataManager->GetDesc(select_hint ? select_hint : 560, mainGame->dInfo.compat_mode),
mainGame->dField.select_min, mainGame->dField.select_max);
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
select_hint = 0;
if (panelmode) {
mainGame->wCardSelect->setText(text.data());
mainGame->dField.ShowSelectCard(mainGame->dField.select_cancelable);
} else {
mainGame->stHintMsg->setText(text.data());
mainGame->stHintMsg->setVisible(true);
}
if (mainGame->dField.select_cancelable) {
if (count2 == 0)
mainGame->dField.ShowCancelOrFinishButton(1);
else
mainGame->dField.ShowCancelOrFinishButton(2);
}
else
mainGame->dField.ShowCancelOrFinishButton(0);
return false;
}
case MSG_SELECT_CHAIN: {
/*uint8_t selecting_player = */BufferIO::Read<uint8_t>(pbuf);
uint32_t count{};
if(mainGame->dInfo.compat_mode)
count = BufferIO::Read<uint8_t>(pbuf);
const auto specount = BufferIO::Read<uint8_t>(pbuf);
if(!mainGame->dInfo.compat_mode)
mainGame->dField.chain_forced = BufferIO::Read<uint8_t>(pbuf) != 0;
/*uint32_t hint0 = */BufferIO::Read<uint32_t>(pbuf);
/*uint32_t hint1 = */BufferIO::Read<uint32_t>(pbuf);
if(!mainGame->dInfo.compat_mode)
count = BufferIO::Read<uint32_t>(pbuf);
uint32_t code;
uint64_t desc;
ClientCard* pcard;
bool panelmode = false;
bool conti_exist = false;
bool select_trigger = (specount == 0x7f);
mainGame->dField.activatable_cards.clear();
mainGame->dField.activatable_descs.clear();
mainGame->dField.conti_cards.clear();
for(uint32_t i = 0; i < count; ++i) {
uint8_t flag{};
if(mainGame->dInfo.compat_mode) {
flag = BufferIO::Read<uint8_t>(pbuf);
mainGame->dField.chain_forced = (BufferIO::Read<uint8_t>(pbuf) != 0) || mainGame->dField.chain_forced;
}
code = BufferIO::Read<uint32_t>(pbuf);
CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info.controler = mainGame->LocalPlayer(info.controler);
desc = CompatRead<uint32_t, uint64_t>(pbuf);
if(!mainGame->dInfo.compat_mode)
flag = BufferIO::Read<uint8_t>(pbuf);
pcard = mainGame->dField.GetCard(info.controler, info.location, info.sequence, info.position);
if(!pcard) {
pcard = new ClientCard{};
pcard->code = code;
pcard->controler = info.controler;
pcard->sequence = static_cast<uint32_t>(mainGame->dField.limbo_temp.size());
mainGame->dField.limbo_temp.push_back(pcard);
}
mainGame->dField.activatable_cards.push_back(pcard);
mainGame->dField.activatable_descs.push_back(std::make_pair(desc, flag));
pcard->is_selected = false;
if(flag == EFFECT_CLIENT_MODE_RESOLVE) {
pcard->chain_code = code;
mainGame->dField.conti_cards.push_back(pcard);
mainGame->dField.conti_act = true;
conti_exist = true;
} else {
pcard->is_selectable = true;
if(flag == EFFECT_CLIENT_MODE_RESET)
pcard->cmdFlag |= COMMAND_RESET;
else
pcard->cmdFlag |= COMMAND_ACTIVATE;
if(pcard->location == LOCATION_DECK) {
pcard->SetCode(code);
mainGame->dField.deck_act[pcard->controler] = true;
} else if(info.location == LOCATION_GRAVE)
mainGame->dField.grave_act[pcard->controler] = true;
else if(info.location == LOCATION_REMOVED)
mainGame->dField.remove_act[pcard->controler] = true;
else if(info.location == LOCATION_EXTRA)
mainGame->dField.extra_act[pcard->controler] = true;
else if(info.location == LOCATION_OVERLAY)
panelmode = true;
}
}
const auto ignore_chain = mainGame->btnChainIgnore->isPressed();
const auto always_chain = mainGame->btnChainAlways->isPressed();
const auto chain_when_avail = mainGame->btnChainWhenAvail->isPressed();
if(!select_trigger && !mainGame->dField.chain_forced && (ignore_chain || ((count == 0 || specount == 0) && !always_chain)) && (count == 0 || !chain_when_avail)) {
SetResponseI(-1);
mainGame->dField.ClearChainSelect();
if(mainGame->tabSettings.chkNoChainDelay->isChecked() && !ignore_chain) {
std::unique_lock<epro::mutex> tmp(mainGame->gMutex);
mainGame->WaitFrameSignal(20, tmp);
}
DuelClient::SendResponse();
return true;
}
if(mainGame->tabSettings.chkAutoChainOrder->isChecked() && mainGame->dField.chain_forced && !(always_chain || chain_when_avail)) {
SetResponseI(0);
mainGame->dField.ClearChainSelect();
DuelClient::SendResponse();
return true;
}
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
if(!conti_exist)
mainGame->stHintMsg->setText(gDataManager->GetSysString(550).data());
else
mainGame->stHintMsg->setText(gDataManager->GetSysString(556).data());
mainGame->stHintMsg->setVisible(true);
if(panelmode) {
mainGame->dField.list_command = COMMAND_ACTIVATE;
mainGame->dField.selectable_cards = mainGame->dField.activatable_cards;
std::sort(mainGame->dField.selectable_cards.begin(), mainGame->dField.selectable_cards.end());
auto eit = std::unique(mainGame->dField.selectable_cards.begin(), mainGame->dField.selectable_cards.end());
mainGame->dField.selectable_cards.erase(eit, mainGame->dField.selectable_cards.end());
mainGame->dField.ShowChainCard();
} else {
if(!mainGame->dField.chain_forced) {
if(count == 0)
mainGame->stQMessage->setText(epro::format(L"{}\n{}", gDataManager->GetSysString(201), gDataManager->GetSysString(202)).data());
else if(select_trigger)
mainGame->stQMessage->setText(epro::format(L"{}\n{}\n{}", event_string, gDataManager->GetSysString(222), gDataManager->GetSysString(223)).data());
else
mainGame->stQMessage->setText(epro::format(L"{}\n{}", event_string, gDataManager->GetSysString(203)).data());
mainGame->PopupElement(mainGame->wQuery);
}
}
return false;
}
case MSG_SELECT_PLACE:
case MSG_SELECT_DISFIELD: {
uint8_t selecting_player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
//fluo passes this as 0 if the selection can be "canceled", ignore for now
mainGame->dField.select_min = std::max<uint8_t>(BufferIO::Read<uint8_t>(pbuf), 1);
uint32_t flag = BufferIO::Read<uint32_t>(pbuf);
if(selecting_player == 1) {
flag = flag << 16 | flag >> 16;
}
mainGame->dField.selectable_field = ~flag;
mainGame->dField.selected_field = 0;
uint8_t respbuf[64];
bool pzone = false;
std::wstring text;
if (mainGame->dInfo.curMsg == MSG_SELECT_PLACE) {
if (select_hint) {
text = epro::sprintf(gDataManager->GetSysString(569), gDataManager->GetName(select_hint));
} else
text = gDataManager->GetDesc(560, mainGame->dInfo.compat_mode).data();
} else
text = gDataManager->GetDesc(select_hint ? select_hint : 570, mainGame->dInfo.compat_mode).data();
select_hint = 0;
mainGame->stHintMsg->setText(text.data());
mainGame->stHintMsg->setVisible(true);
if (mainGame->dInfo.curMsg == MSG_SELECT_PLACE && (
(mainGame->gSettings.chkMAutoPos->isChecked() && mainGame->dField.selectable_field & 0x7f007f) ||
(mainGame->gSettings.chkSTAutoPos->isChecked() && !(mainGame->dField.selectable_field & 0x7f007f)))) {
if(mainGame->gSettings.chkRandomPos->isChecked()) {
std::vector<char> positions;
for(char i = 0; i < 32; i++) {
if(mainGame->dField.selectable_field & (1 << i))
positions.push_back(i);
}
char res = positions[(std::uniform_int_distribution<>(0, static_cast<int>(positions.size() - 1)))(rnd)];
respbuf[0] = mainGame->LocalPlayer((res < 16) ? 0 : 1);
respbuf[1] = ((1 << res) & 0x7f007f) ? LOCATION_MZONE : LOCATION_SZONE;
respbuf[2] = (res%16) - (2 * (respbuf[1] - LOCATION_MZONE));
} else {
uint32_t filter;
if(mainGame->dField.selectable_field & 0x7f) {
respbuf[0] = mainGame->LocalPlayer(0);
respbuf[1] = LOCATION_MZONE;
filter = mainGame->dField.selectable_field & 0x7f;
} else if(mainGame->dField.selectable_field & 0x1f00) {
respbuf[0] = mainGame->LocalPlayer(0);
respbuf[1] = LOCATION_SZONE;
filter = (mainGame->dField.selectable_field >> 8) & 0x1f;
} else if(mainGame->dField.selectable_field & 0xc000) {
respbuf[0] = mainGame->LocalPlayer(0);
respbuf[1] = LOCATION_SZONE;
filter = (mainGame->dField.selectable_field >> 14) & 0x3;
pzone = true;
} else if(mainGame->dField.selectable_field & 0x7f0000) {
respbuf[0] = mainGame->LocalPlayer(1);
respbuf[1] = LOCATION_MZONE;
filter = (mainGame->dField.selectable_field >> 16) & 0x7f;
} else if(mainGame->dField.selectable_field & 0x1f000000) {
respbuf[0] = mainGame->LocalPlayer(1);
respbuf[1] = LOCATION_SZONE;
filter = (mainGame->dField.selectable_field >> 24) & 0x1f;
} else {
respbuf[0] = mainGame->LocalPlayer(1);
respbuf[1] = LOCATION_SZONE;
filter = (mainGame->dField.selectable_field >> 30) & 0x3;
pzone = true;
}
if(!pzone) {
if(filter & 0x40) respbuf[2] = 6;
else if(filter & 0x20) respbuf[2] = 5;
else if(filter & 0x4) respbuf[2] = 2;
else if(filter & 0x2) respbuf[2] = 1;
else if(filter & 0x8) respbuf[2] = 3;
else if(filter & 0x1) respbuf[2] = 0;
else if(filter & 0x10) respbuf[2] = 4;
} else {
if(filter & 0x1) respbuf[2] = 6;
else if(filter & 0x2) respbuf[2] = 7;
}
}
mainGame->dField.selectable_field = 0;
SetResponseB(respbuf, 3);
DuelClient::SendResponse();
return true;
}
return false;
}
case MSG_SELECT_POSITION: {
/*uint8_t selecting_player = */BufferIO::Read<uint8_t>(pbuf);
uint32_t code = BufferIO::Read<uint32_t>(pbuf);
uint8_t positions = BufferIO::Read<uint8_t>(pbuf);
if (positions == POS_FACEUP_ATTACK || positions == POS_FACEDOWN_ATTACK || positions == POS_FACEUP_DEFENSE || positions == POS_FACEDOWN_DEFENSE) {
SetResponseI(positions);
return true;
}
int count = 0, filter = 0x1, startpos;
while(filter != 0x10) {
if(positions & filter) count++;
filter <<= 1;
}
if(count == 4) startpos = 10;
else if(count == 3) startpos = 82;
else startpos = 155;
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
if(positions & POS_FACEUP_ATTACK) {
mainGame->imageLoading[mainGame->btnPSAU] = code;
mainGame->btnPSAU->setRelativePosition(mainGame->Scale<irr::s32>(startpos, 45, startpos + 140, 185));
mainGame->btnPSAU->setVisible(true);
startpos += 145;
} else mainGame->btnPSAU->setVisible(false);
if(positions & POS_FACEDOWN_ATTACK) {
mainGame->btnPSAD->setRelativePosition(mainGame->Scale<irr::s32>(startpos, 45, startpos + 140, 185));
mainGame->btnPSAD->setVisible(true);
startpos += 145;
} else mainGame->btnPSAD->setVisible(false);
if(positions & POS_FACEUP_DEFENSE) {
mainGame->imageLoading[mainGame->btnPSDU] = code;
mainGame->btnPSDU->setRelativePosition(mainGame->Scale<irr::s32>(startpos, 45, startpos + 140, 185));
mainGame->btnPSDU->setVisible(true);
startpos += 145;
} else mainGame->btnPSDU->setVisible(false);
if(positions & POS_FACEDOWN_DEFENSE) {
mainGame->btnPSDD->setRelativePosition(mainGame->Scale<irr::s32>(startpos, 45, startpos + 140, 185));
mainGame->btnPSDD->setVisible(true);
startpos += 145;
} else mainGame->btnPSDD->setVisible(false);
mainGame->PopupElement(mainGame->wPosSelect);
return false;
}
case MSG_SELECT_TRIBUTE: {
/*uint8_t selecting_player = */BufferIO::Read<uint8_t>(pbuf);
mainGame->dField.select_cancelable = BufferIO::Read<uint8_t>(pbuf) != 0;
mainGame->dField.select_min = CompatRead<uint8_t, uint32_t>(pbuf);
mainGame->dField.select_max = CompatRead<uint8_t, uint32_t>(pbuf);
uint32_t count = CompatRead<uint8_t, uint32_t>(pbuf);
mainGame->dField.selectable_cards.clear();
mainGame->dField.selected_cards.clear();
uint32_t code, s;
uint8_t c, l, t;
ClientCard* pcard;
mainGame->dField.select_ready = false;
for(uint32_t i = 0; i < count; ++i) {
code = BufferIO::Read<uint32_t>(pbuf);
c = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
l = BufferIO::Read<uint8_t>(pbuf);
s = CompatRead<uint8_t, uint32_t>(pbuf);
t = BufferIO::Read<uint8_t>(pbuf);
pcard = mainGame->dField.GetCard(c, l, s);
if (code && pcard->code != code)
pcard->SetCode(code);
mainGame->dField.selectable_cards.push_back(pcard);
pcard->opParam = t;
pcard->select_seq = i;
pcard->is_selectable = true;
}
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->stHintMsg->setText(epro::format(L"{}({}-{})", gDataManager->GetDesc(select_hint ? select_hint : 531, mainGame->dInfo.compat_mode), mainGame->dField.select_min, mainGame->dField.select_max).data());
mainGame->stHintMsg->setVisible(true);
if (mainGame->dField.select_cancelable) {
mainGame->dField.ShowCancelOrFinishButton(1);
}
select_hint = 0;
return false;
}
case MSG_SELECT_COUNTER: {
/*uint8_t selecting_player = */BufferIO::Read<uint8_t>(pbuf);
mainGame->dField.select_counter_type = BufferIO::Read<uint16_t>(pbuf);
mainGame->dField.select_counter_count = BufferIO::Read<uint16_t>(pbuf);
uint32_t count = CompatRead<uint8_t, uint32_t>(pbuf);
mainGame->dField.selectable_cards.clear();
/*uint32_t code;*/
uint16_t t;
uint8_t c, l, s;
ClientCard* pcard;
for(uint32_t i = 0; i < count; ++i) {
/*code = */BufferIO::Read<uint32_t>(pbuf);
c = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
l = BufferIO::Read<uint8_t>(pbuf);
s = BufferIO::Read<uint8_t>(pbuf);
t = BufferIO::Read<uint16_t>(pbuf);
pcard = mainGame->dField.GetCard(c, l, s);
mainGame->dField.selectable_cards.push_back(pcard);
pcard->opParam = (t << 16) | t;
pcard->is_selectable = true;
}
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->stHintMsg->setText(epro::sprintf(gDataManager->GetSysString(204), mainGame->dField.select_counter_count, gDataManager->GetCounterName(mainGame->dField.select_counter_type)).data());
mainGame->stHintMsg->setVisible(true);
return false;
}
case MSG_SELECT_SUM: {
auto GetCard = [&] {
CoreUtils::loc_info info{};
if(mainGame->dInfo.compat_mode) {
uint8_t c = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
uint8_t l = BufferIO::Read<uint8_t>(pbuf);
uint32_t s = CompatRead<uint8_t, uint32_t>(pbuf);
info.controler = c;
info.location = l;
info.sequence = s;
} else {
info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info.controler = mainGame->LocalPlayer(info.controler);
}
if((info.location & LOCATION_OVERLAY) > 0)
return mainGame->dField.GetCard(info.controler, info.location & (~LOCATION_OVERLAY) & 0xff, info.sequence)->overlayed[info.position];
return mainGame->dField.GetCard(info.controler, info.location, info.sequence);
};
/*uint8_t selecting_player*/
if(mainGame->dInfo.compat_mode) {
mainGame->dField.select_mode = BufferIO::Read<uint8_t>(pbuf);
/*selecting_player = */BufferIO::Read<uint8_t>(pbuf);
} else {
/*selecting_player = */BufferIO::Read<uint8_t>(pbuf);
mainGame->dField.select_mode = BufferIO::Read<uint8_t>(pbuf);
}
mainGame->dField.select_sumval = BufferIO::Read<uint32_t>(pbuf);
mainGame->dField.select_min = CompatRead<uint8_t, uint32_t>(pbuf);
mainGame->dField.select_max = CompatRead<uint8_t, uint32_t>(pbuf);
mainGame->dField.must_select_count = CompatRead<uint8_t, uint32_t>(pbuf);
mainGame->dField.selectable_cards.clear();
mainGame->dField.selectsum_all.clear();
mainGame->dField.selected_cards.clear();
mainGame->dField.must_select_cards.clear();
mainGame->dField.selectsum_cards.clear();
for (uint32_t i = 0; i < mainGame->dField.must_select_count; ++i) {
uint32_t code = BufferIO::Read<uint32_t>(pbuf);
ClientCard* pcard = GetCard();
if (code != 0 && pcard->code != code)
pcard->SetCode(code);
pcard->opParam = BufferIO::Read<uint32_t>(pbuf);
pcard->select_seq = 0;
mainGame->dField.must_select_cards.push_back(pcard);
}
uint32_t count = CompatRead<uint8_t, uint32_t>(pbuf);
for(uint32_t i = 0; i < count; ++i) {
uint32_t code = BufferIO::Read<uint32_t>(pbuf);
ClientCard* pcard = GetCard();
if (code != 0 && pcard->code != code)
pcard->SetCode(code);
pcard->opParam = BufferIO::Read<uint32_t>(pbuf);
pcard->select_seq = i;
mainGame->dField.selectsum_all.push_back(pcard);
}
std::sort(mainGame->dField.selectsum_all.begin(), mainGame->dField.selectsum_all.end(), ClientCard::client_card_sort);
PerformQueuedPanelConfirmIfDifferent(mainGame->dField.selectsum_all);
std::wstring text = epro::format(L"{}({})", gDataManager->GetDesc(select_hint ? select_hint : 560, mainGame->dInfo.compat_mode), mainGame->dField.select_sumval);
select_hint = 0;
mainGame->wCardSelect->setText(text.data());
mainGame->stHintMsg->setText(text.data());
return mainGame->dField.ShowSelectSum();
}
case MSG_SORT_CARD:
case MSG_SORT_CHAIN: {
/*const auto player = */BufferIO::Read<uint8_t>(pbuf);
const auto count = CompatRead<uint8_t, uint32_t>(pbuf);
mainGame->dField.selectable_cards.clear();
mainGame->dField.selected_cards.clear();
mainGame->dField.sort_list.clear();
ClientCard* pcard;
for(uint32_t i = 0; i < count; ++i) {
const auto code = BufferIO::Read<uint32_t>(pbuf);
const auto c = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto l = CompatRead<uint8_t, uint32_t>(pbuf);
const auto s = CompatRead<uint8_t, uint32_t>(pbuf);
pcard = mainGame->dField.GetCard(c, l, s);
if (code != 0 && pcard->code != code)
pcard->SetCode(code);
mainGame->dField.selectable_cards.push_back(pcard);
mainGame->dField.sort_list.push_back(0);
}
if (mainGame->tabSettings.chkAutoChainOrder->isChecked() && mainGame->dInfo.curMsg == MSG_SORT_CHAIN) {
mainGame->dField.sort_list.clear();
SetResponseI(-1);
DuelClient::SendResponse();
return true;
}
if(mainGame->dInfo.curMsg == MSG_SORT_CHAIN)
mainGame->wCardSelect->setText(gDataManager->GetSysString(206).data());
else
mainGame->wCardSelect->setText(gDataManager->GetSysString(205).data());
mainGame->dField.select_min = 0;
mainGame->dField.select_max = count;
mainGame->dField.ShowSelectCard();
return false;
}
case MSG_CONFIRM_DECKTOP: {
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto count = CompatRead<uint8_t, uint32_t>(pbuf);
uint32_t code;
ClientCard* pcard;
mainGame->dField.selectable_cards.clear();
for(uint32_t i = 0; i < count; ++i) {
code = BufferIO::Read<uint32_t>(pbuf);
pbuf += (mainGame->dInfo.compat_mode) ? 3 : 6;
pcard = *(mainGame->dField.deck[player].rbegin() + i);
if (code != 0)
pcard->SetCode(code);
}
if(mainGame->dInfo.isCatchingUp)
return true;
mainGame->AddLog(epro::sprintf(gDataManager->GetSysString(207), count));
constexpr float milliseconds = 5.0f * 1000.0f / 60.0f;
float shift = -0.75f / milliseconds;
if(!!player ^ mainGame->current_topdown) shift *= -1.0f;
for(auto it = mainGame->dField.deck[player].crbegin(), end = it + count; it != end; ++it) {
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
pcard = *it;
mainGame->AddLog(epro::format(L"*[{}]", gDataManager->GetName(pcard->code)), pcard->code);
pcard->dPos.set(shift, 0, 0);
if(!mainGame->dField.deck_reversed && !pcard->is_reversed)
pcard->dRot.set(0, irr::core::PI / milliseconds, 0);
else pcard->dRot.set(0, 0, 0);
pcard->is_moving = true;
pcard->aniFrame = milliseconds;
mainGame->WaitFrameSignal(45, lock);
mainGame->dField.MoveCard(pcard, 5);
mainGame->WaitFrameSignal(5, lock);
}
return true;
}
case MSG_CONFIRM_EXTRATOP: {
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto count = CompatRead<uint8_t, uint32_t>(pbuf);
uint32_t code;
ClientCard* pcard;
mainGame->dField.selectable_cards.clear();
const auto backit = mainGame->dField.extra[player].crbegin() + mainGame->dField.extra_p_count[player];
for(auto it = backit, end = it + count; it != end; ++it) {
code = BufferIO::Read<uint32_t>(pbuf);
pbuf += (mainGame->dInfo.compat_mode) ? 3 : 6;
pcard = *it;
if (code != 0)
pcard->SetCode(code);
}
if(mainGame->dInfo.isCatchingUp)
return true;
mainGame->AddLog(epro::sprintf(gDataManager->GetSysString(207), count));
for(auto it = backit, end = it + count; it != end; ++it) {
pcard = *it;
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->AddLog(epro::format(L"*[{}]", gDataManager->GetName(pcard->code)), pcard->code);
constexpr float milliseconds = 5.0f * 1000.0f / 60.0f;
if (player == 0)
pcard->dPos.set(0, -1.0f / milliseconds, 0);
else
pcard->dPos.set(0.75f / milliseconds, 0, 0);
pcard->dRot.set(0, irr::core::PI / milliseconds, 0);
pcard->is_moving = true;
pcard->aniFrame = milliseconds;
mainGame->WaitFrameSignal(45, lock);
mainGame->dField.MoveCard(pcard, 5);
mainGame->WaitFrameSignal(5, lock);
}
return true;
}
case MSG_CONFIRM_CARDS: {
/*const auto player = */mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
// for edopro we use heuristics
const auto skip_panel = mainGame->dInfo.compat_mode && (BufferIO::Read<uint8_t>(pbuf) != 0);
const auto count = CompatRead<uint8_t, uint32_t>(pbuf);
std::vector<ClientCard*> field_confirm;
std::vector<ClientCard*> panel_confirm;
if(mainGame->dInfo.isCatchingUp)
return true;
mainGame->AddLog(epro::sprintf(gDataManager->GetSysString(208), count));
for(uint32_t i = 0; i < count; ++i) {
auto code = BufferIO::Read<uint32_t>(pbuf);
auto controller = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
auto location = BufferIO::Read<uint8_t>(pbuf);
auto sequence = CompatRead<uint8_t, uint32_t>(pbuf);
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
ClientCard* pcard;
if (location == 0) {
pcard = new ClientCard{};
pcard->sequence = static_cast<uint32_t>(mainGame->dField.limbo_temp.size());
mainGame->dField.limbo_temp.push_back(pcard);
} else
pcard = mainGame->dField.GetCard(controller, location, sequence);
if (code != 0)
pcard->SetCode(code);
mainGame->AddLog(epro::format(L"*[{}]", gDataManager->GetName(code)), code);
if (location & (LOCATION_EXTRA | LOCATION_DECK) || location == 0) {
if(count == 1 && location != 0) {
constexpr float milliseconds = 5.0f * 1000.0f / 60.0f;
float shift = -0.75f / milliseconds;
if (controller == 0 && location == LOCATION_EXTRA) shift *= -1.0f;
pcard->dPos.set(shift, 0, 0);
if(((location == LOCATION_DECK) && mainGame->dField.deck_reversed) || pcard->is_reversed || (pcard->position & POS_FACEUP))
pcard->dRot.set(0, 0, 0);
else pcard->dRot.set(0, irr::core::PI / milliseconds, 0);
pcard->is_moving = true;
pcard->aniFrame = milliseconds;
mainGame->WaitFrameSignal(45, lock);
mainGame->dField.MoveCard(pcard, 5);
mainGame->WaitFrameSignal(5, lock);
} else {
if(!mainGame->dInfo.isReplay)
panel_confirm.push_back(pcard);
}
} else {
if(!mainGame->dInfo.isReplay || (location & LOCATION_ONFIELD) || (location & LOCATION_HAND && gGameConfig->hideHandsInReplays))
field_confirm.push_back(pcard);
}
}
if (field_confirm.size() > 0) {
std::map<ClientCard*, bool> public_status;
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->WaitFrameSignal(5, lock);
for(auto& pcard : field_confirm) {
auto location = pcard->location;
if (location == LOCATION_HAND) {
if(mainGame->dInfo.isReplay) {
public_status[pcard] = pcard->is_public;
pcard->is_public = true;
}
mainGame->dField.MoveCard(pcard, 5);
pcard->is_highlighting = true;
} else if (location == LOCATION_MZONE) {
if (pcard->position & POS_FACEUP)
continue;
constexpr float milliseconds = 5.0f * 1000.0f / 60.0f;
pcard->dPos.set(0, 0, 0);
if (pcard->position == POS_FACEDOWN_ATTACK)
pcard->dRot.set(0, irr::core::PI / milliseconds, 0);
else
pcard->dRot.set(irr::core::PI / milliseconds, 0, 0);
pcard->is_moving = true;
pcard->aniFrame = milliseconds;
} else if (location == LOCATION_SZONE) {
if (pcard->position & POS_FACEUP)
continue;
constexpr float milliseconds = 5.0f * 1000.0f / 60.0f;
pcard->dPos.set(0, 0, 0);
pcard->dRot.set(0, irr::core::PI / milliseconds, 0);
pcard->is_moving = true;
pcard->aniFrame = milliseconds;
}
}
if (mainGame->dInfo.isReplay)
mainGame->WaitFrameSignal(30, lock);
else
mainGame->WaitFrameSignal(90, lock);
for(auto& pcard : field_confirm) {
if(mainGame->dInfo.isReplay && pcard->location & LOCATION_HAND)
pcard->is_public = public_status[pcard];
mainGame->dField.MoveCard(pcard, 5);
pcard->is_highlighting = false;
}
mainGame->WaitFrameSignal(5, lock);
}
if(!skip_panel && panel_confirm.size()) {
std::sort(panel_confirm.begin(), panel_confirm.end(), ClientCard::client_card_sort);
if(field_confirm.empty() && mainGame->dField.limbo_temp.empty()) {
std::swap(panel_confirm, mainGame->dField.queued_panel_confirm_cards);
return true;
}
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
std::swap(mainGame->dField.selectable_cards, panel_confirm);
mainGame->wCardSelect->setText(epro::sprintf(gDataManager->GetSysString(208), mainGame->dField.selectable_cards.size()).data());
mainGame->dField.ShowSelectCard(true);
mainGame->actionSignal.Wait(lock);
}
return true;
}
case MSG_SHUFFLE_DECK: {
Play(SoundManager::SFX::SHUFFLE);
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
if(mainGame->dField.deck[player].size() < 2)
return true;
bool rev = mainGame->dField.deck_reversed;
auto lock = LockIf();
if(!mainGame->dInfo.isCatchingUp) {
mainGame->dField.deck_reversed = false;
if(rev) {
for(const auto& pcard : mainGame->dField.deck[player])
mainGame->dField.MoveCard(pcard, 10);
mainGame->WaitFrameSignal(10, lock);
}
}
for(const auto& pcard : mainGame->dField.deck[player]) {
pcard->code = 0;
pcard->is_reversed = false;
}
if(!mainGame->dInfo.isCatchingUp) {
for(int i = 0; i < 5; ++i) {
for(const auto& pcard : mainGame->dField.deck[player]) {
constexpr float milliseconds = 3.0f * 1000.0f / 60.0f;
pcard->dPos.set((rand() * 1.2f / RAND_MAX - 0.2f) / milliseconds, 0, 0);
pcard->dRot.set(0, 0, 0);
pcard->is_moving = true;
pcard->aniFrame = milliseconds;
}
mainGame->WaitFrameSignal(3, lock);
for(const auto& pcard : mainGame->dField.deck[player])
mainGame->dField.MoveCard(pcard, 3);
mainGame->WaitFrameSignal(3, lock);
}
mainGame->dField.deck_reversed = rev;
if(rev) {
for(const auto& pcard : mainGame->dField.deck[player])
mainGame->dField.MoveCard(pcard, 10);
mainGame->WaitFrameSignal(10, lock);
}
}
return true;
}
case MSG_SHUFFLE_HAND: {
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
/*const auto count = */CompatRead<uint8_t, uint32_t>(pbuf);
auto lock = LockIf();
if(!mainGame->dInfo.isCatchingUp) {
mainGame->WaitFrameSignal(5, lock);
if(player == 1 && !mainGame->dInfo.isReplay && !mainGame->dInfo.isSingleMode) {
bool flip = false;
for(const auto& pcard : mainGame->dField.hand[player])
if(pcard->code) {
constexpr float milliseconds = 5.0f * 1000.0f / 60.0f;
pcard->dPos.set(0, 0, 0);
pcard->dRot.set(1.322f / milliseconds, irr::core::PI / milliseconds, 0);
pcard->is_moving = true;
pcard->is_hovered = false;
pcard->aniFrame = milliseconds;
flip = true;
}
if(flip)
mainGame->WaitFrameSignal(5, lock);
}
for(const auto& pcard : mainGame->dField.hand[player]) {
constexpr float milliseconds = 5.0f * 1000.0f / 60.0f;
pcard->dPos.set((3.9f - pcard->curPos.X) / milliseconds, 0, 0);
pcard->dRot.set(0, 0, 0);
pcard->is_moving = true;
pcard->is_hovered = false;
pcard->aniFrame = milliseconds;
}
mainGame->WaitFrameSignal(11, lock);
}
for(const auto& pcard : mainGame->dField.hand[player]) {
pcard->SetCode(BufferIO::Read<uint32_t>(pbuf));
pcard->desc_hints.clear();
if(!mainGame->dInfo.isCatchingUp) {
pcard->is_hovered = false;
mainGame->dField.MoveCard(pcard, 5);
}
}
if(!mainGame->dInfo.isCatchingUp)
mainGame->WaitFrameSignal(5, lock);
return true;
}
case MSG_SHUFFLE_EXTRA: {
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto count = CompatRead<uint8_t, uint32_t>(pbuf);
if((mainGame->dField.extra[player].size() - mainGame->dField.extra_p_count[player]) < 2)
return true;
auto lock = LockIf();
if(!mainGame->dInfo.isCatchingUp) {
if(count > 1)
Play(SoundManager::SFX::SHUFFLE);
for(int i = 0; i < 5; ++i) {
for(const auto& pcard : mainGame->dField.extra[player]) {
if(!(pcard->position & POS_FACEUP)) {
constexpr float milliseconds = 3.0f * 1000.0f / 60.0f;
pcard->dPos.set((rand() * 1.2f / RAND_MAX - 0.2f) / milliseconds, 0, 0);
pcard->dRot.set(0, 0, 0);
pcard->is_moving = true;
pcard->aniFrame = milliseconds;
}
}
mainGame->WaitFrameSignal(3, lock);
for(const auto& pcard : mainGame->dField.extra[player])
if(!(pcard->position & POS_FACEUP))
mainGame->dField.MoveCard(pcard, 3);
mainGame->WaitFrameSignal(3, lock);
}
}
for (const auto& pcard : mainGame->dField.extra[player])
if(!(pcard->position & POS_FACEUP))
pcard->SetCode(BufferIO::Read<uint32_t>(pbuf));
return true;
}
case MSG_REFRESH_DECK: {
/*const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));*/
return true;
}
case MSG_SWAP_GRAVE_DECK: {
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
ProgressiveBuffer buff;
if(!mainGame->dInfo.compat_mode) {
/*const auto mainsize = */BufferIO::Read<uint32_t>(pbuf);
const auto extrabuffersize = BufferIO::Read<uint32_t>(pbuf);
buff.data.resize(extrabuffersize);
BufferIO::Read(pbuf, buff.data.data(), extrabuffersize);
}
auto checkextra = [&buff, compat = mainGame->dInfo.compat_mode] (int idx, ClientCard* pcard) -> bool {
if(compat)
return pcard->type & (TYPE_FUSION | TYPE_SYNCHRO | TYPE_XYZ | TYPE_LINK);
return buff.bitGet(idx);
};
auto lock = LockIf();
mainGame->dField.grave[player].swap(mainGame->dField.deck[player]);
for(const auto& pcard : mainGame->dField.grave[player]) {
pcard->location = LOCATION_GRAVE;
if(!mainGame->dInfo.isCatchingUp)
mainGame->dField.MoveCard(pcard, 10);
}
int m = 0;
int i = 0;
for(auto cit = mainGame->dField.deck[player].begin(); cit != mainGame->dField.deck[player].end(); i++) {
ClientCard* pcard = *cit;
if(checkextra(i, pcard)) {
pcard->position = POS_FACEDOWN;
mainGame->dField.AddCard(pcard, player, LOCATION_EXTRA, 0);
cit = mainGame->dField.deck[player].erase(cit);
} else {
pcard->location = LOCATION_DECK;
pcard->sequence = m++;
++cit;
}
if(!mainGame->dInfo.isCatchingUp)
mainGame->dField.MoveCard(pcard, 10);
}
if(!mainGame->dInfo.isCatchingUp)
mainGame->WaitFrameSignal(11, lock);
return true;
}
case MSG_REVERSE_DECK: {
mainGame->dField.deck_reversed = !mainGame->dField.deck_reversed;
if(!mainGame->dInfo.isCatchingUp) {
for(auto& pcard : mainGame->dField.deck[0])
mainGame->dField.MoveCard(pcard, 10);
for(auto& pcard : mainGame->dField.deck[1])
mainGame->dField.MoveCard(pcard, 10);
}
return true;
}
case MSG_DECK_TOP: {
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto seq = CompatRead<uint8_t, uint32_t>(pbuf);
ClientCard* pcard = mainGame->dField.GetCard(player, LOCATION_DECK, mainGame->dField.deck[player].size() - 1 - seq);
const auto code = BufferIO::Read<uint32_t>(pbuf);
bool rev;
if(!mainGame->dInfo.compat_mode) {
rev = (BufferIO::Read<uint32_t>(pbuf) & POS_FACEUP_DEFENSE) != 0;
pcard->SetCode(code);
} else {
rev = (code & 0x80000000) != 0;
pcard->SetCode(code & 0x7fffffff);
}
if(pcard->is_reversed != rev) {
pcard->is_reversed = rev;
mainGame->dField.MoveCard(pcard, 5);
}
return true;
}
case MSG_SHUFFLE_SET_CARD: {
std::vector<ClientCard*>* lst = 0;
const auto loc = BufferIO::Read<uint8_t>(pbuf);
const auto count = BufferIO::Read<uint8_t>(pbuf);
if(loc == LOCATION_MZONE)
lst = mainGame->dField.mzone;
else
lst = mainGame->dField.szone;
ClientCard* mc[7];
auto lock = LockIf();
for (int i = 0; i < count; ++i) {
CoreUtils::loc_info previous = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
previous.controler = mainGame->LocalPlayer(previous.controler);
mc[i] = lst[previous.controler][previous.sequence];
mc[i]->SetCode(0);
if(!mainGame->dInfo.isCatchingUp) {
constexpr float milliseconds = 10.0f * 1000.0f / 60.0f;
mc[i]->dPos.set((3.95f - mc[i]->curPos.X) / milliseconds, 0, 0.5f / milliseconds);
mc[i]->dRot.set(0, 0, 0);
mc[i]->is_moving = true;
mc[i]->aniFrame = milliseconds;
}
}
if(!mainGame->dInfo.isCatchingUp)
mainGame->WaitFrameSignal(20, lock);
for (int i = 0; i < count; ++i) {
CoreUtils::loc_info current = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
current.controler = mainGame->LocalPlayer(current.controler);
if (current.location > 0) {
uint8_t prev_seq = mc[i]->sequence;
auto tmp = lst[current.controler][current.sequence];
lst[current.controler][prev_seq] = tmp;
lst[current.controler][current.sequence] = mc[i];
mc[i]->sequence = current.sequence;
tmp->sequence = prev_seq;
}
}
if(!mainGame->dInfo.isCatchingUp) {
for (int i = 0; i < count; ++i) {
mainGame->dField.MoveCard(mc[i], 10);
for (auto pcard : mc[i]->overlayed)
mainGame->dField.MoveCard(pcard, 10);
}
mainGame->WaitFrameSignal(11, lock);
}
return true;
}
case MSG_NEW_TURN: {
Play(SoundManager::SFX::NEXT_TURN);
/*const auto player = */mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
auto lock = LockIf();
mainGame->dInfo.turn++;
if(!mainGame->dInfo.isReplay && !mainGame->dInfo.isSingleMode && mainGame->dInfo.player_type < (mainGame->dInfo.team1 + mainGame->dInfo.team2)) {
mainGame->btnLeaveGame->setText(gDataManager->GetSysString(1351).data());
mainGame->btnLeaveGame->setVisible(true);
}
if(!mainGame->dInfo.isReplay && mainGame->dInfo.player_type < 7) {
if(!mainGame->tabSettings.chkHideChainButtons->isChecked()) {
mainGame->btnChainIgnore->setVisible(true);
mainGame->btnChainAlways->setVisible(true);
mainGame->btnChainWhenAvail->setVisible(true);
//mainGame->dField.UpdateChainButtons();
} else {
mainGame->btnChainIgnore->setVisible(false);
mainGame->btnChainAlways->setVisible(false);
mainGame->btnChainWhenAvail->setVisible(false);
mainGame->btnCancelOrFinish->setVisible(false);
}
}
if(!mainGame->dInfo.isCatchingUp) {
mainGame->showcardcode = 10;
mainGame->showcarddif = 30;
mainGame->showcardp = 0;
mainGame->showcard = 101;
mainGame->WaitFrameSignal(40, lock);
mainGame->showcard = 0;
}
return true;
}
case MSG_NEW_PHASE: {
Play(SoundManager::SFX::PHASE);
const auto phase = BufferIO::Read<uint16_t>(pbuf);
auto lock = LockIf();
mainGame->btnDP->setVisible(false);
mainGame->btnDP->setSubElement(false);
mainGame->btnSP->setVisible(false);
mainGame->btnSP->setSubElement(false);
mainGame->btnM1->setVisible(false);
mainGame->btnM1->setSubElement(false);
mainGame->btnBP->setVisible(false);
mainGame->btnBP->setSubElement(false);
mainGame->btnM2->setVisible(false);
mainGame->btnM2->setSubElement(false);
mainGame->btnEP->setVisible(false);
mainGame->btnEP->setSubElement(false);
if(gGameConfig->alternative_phase_layout) {
mainGame->btnDP->setVisible(true);
mainGame->btnSP->setVisible(true);
mainGame->btnM1->setVisible(true);
mainGame->btnBP->setVisible(true);
mainGame->btnBP->setPressed(false);
mainGame->btnBP->setEnabled(false);
mainGame->btnM2->setVisible(true);
mainGame->btnM2->setPressed(false);
mainGame->btnM2->setEnabled(false);
mainGame->btnEP->setVisible(true);
mainGame->btnEP->setPressed(false);
mainGame->btnEP->setEnabled(false);
}
mainGame->btnShuffle->setVisible(false);
mainGame->showcarddif = 30;
mainGame->showcardp = 0;
switch (phase) {
case PHASE_DRAW:
event_string = gDataManager->GetSysString(20).data();
mainGame->btnDP->setVisible(true);
mainGame->btnDP->setSubElement(true);
mainGame->showcardcode = 4;
break;
case PHASE_STANDBY:
event_string = gDataManager->GetSysString(21).data();
mainGame->btnSP->setVisible(true);
mainGame->btnSP->setSubElement(true);
mainGame->showcardcode = 5;
break;
case PHASE_MAIN1:
event_string = gDataManager->GetSysString(22).data();
mainGame->btnM1->setVisible(true);
mainGame->btnM1->setSubElement(true);
mainGame->showcardcode = 6;
break;
case PHASE_BATTLE_START:
event_string = gDataManager->GetSysString(24).data();
mainGame->btnBP->setVisible(true);
mainGame->btnBP->setSubElement(true);
mainGame->btnBP->setPressed(true);
mainGame->btnBP->setEnabled(false);
mainGame->showcardcode = 7;
break;
case PHASE_MAIN2:
event_string = gDataManager->GetSysString(22).data();
mainGame->btnM2->setVisible(true);
mainGame->btnM2->setSubElement(true);
mainGame->btnM2->setPressed(true);
mainGame->btnM2->setEnabled(false);
mainGame->showcardcode = 8;
break;
case PHASE_END:
event_string = gDataManager->GetSysString(26).data();
mainGame->btnEP->setVisible(true);
mainGame->btnEP->setSubElement(true);
mainGame->btnEP->setPressed(true);
mainGame->btnEP->setEnabled(false);
mainGame->showcardcode = 9;
break;
}
if(!mainGame->dInfo.isCatchingUp) {
mainGame->showcard = 101;
mainGame->WaitFrameSignal(40, lock);
mainGame->showcard = 0;
}
return true;
}
case MSG_MOVE: {
const auto code = BufferIO::Read<uint32_t>(pbuf);
CoreUtils::loc_info previous = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
previous.controler = mainGame->LocalPlayer(previous.controler);
CoreUtils::loc_info current = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
current.controler = mainGame->LocalPlayer(current.controler);
const auto reason = BufferIO::Read<uint32_t>(pbuf);
if (previous.location != current.location) {
if (reason & REASON_DESTROY)
Play(SoundManager::SFX::DESTROYED);
else if (current.location & LOCATION_REMOVED)
Play(SoundManager::SFX::BANISHED);
}
auto lock = LockIf();
if (previous.location == 0) {
ClientCard* pcard = new ClientCard{};
pcard->position = current.position;
pcard->SetCode(code);
mainGame->dField.AddCard(pcard, current.controler, current.location, current.sequence);
if(!mainGame->dInfo.isCatchingUp) {
pcard->UpdateDrawCoordinates(true);
pcard->curAlpha = 5;
int frames = 20;
if(gGameConfig->quick_animation)
frames = 12;
mainGame->dField.FadeCard(pcard, 255, frames);
mainGame->WaitFrameSignal(frames, lock);
}
} else if (current.location == 0) {
ClientCard* pcard = nullptr;
if(previous.location & LOCATION_OVERLAY) {
auto olcard = mainGame->dField.GetCard(previous.controler, (previous.location & (~LOCATION_OVERLAY)) & 0xff, previous.sequence);
pcard = olcard->overlayed[previous.position];
} else
pcard = mainGame->dField.GetCard(previous.controler, previous.location, previous.sequence);
if (code != 0 && pcard->code != code)
pcard->SetCode(code);
pcard->ClearTarget();
for(const auto& eqcard : pcard->equipped)
eqcard->equipTarget = nullptr;
if(!mainGame->dInfo.isCatchingUp) {
int frames = 20;
if(gGameConfig->quick_animation)
frames = 12;
mainGame->dField.FadeCard(pcard, 5, frames);
mainGame->WaitFrameSignal(frames, lock);
}
if(pcard->location & LOCATION_OVERLAY) {
pcard->overlayTarget->overlayed.erase(pcard->overlayTarget->overlayed.begin() + pcard->sequence);
pcard->overlayTarget = 0;
mainGame->dField.overlay_cards.erase(pcard);
} else
mainGame->dField.RemoveCard(previous.controler, previous.location, previous.sequence);
if(!mainGame->dInfo.isCatchingUp && pcard == mainGame->dField.hovered_card)
mainGame->dField.hovered_card = nullptr;
delete pcard;
} else {
if (!(previous.location & LOCATION_OVERLAY) && !(current.location & LOCATION_OVERLAY)) {
ClientCard* pcard = mainGame->dField.GetCard(previous.controler, previous.location, previous.sequence);
if (pcard->code != code && (code != 0 || current.location == LOCATION_EXTRA))
pcard->SetCode(code);
pcard->cHint = 0;
pcard->chValue = 0;
if((previous.location & LOCATION_ONFIELD) && (current.location != previous.location))
pcard->counters.clear();
if(current.location != previous.location) {
pcard->ClearTarget();
if(pcard->equipTarget) {
pcard->equipTarget->is_showequip = false;
pcard->equipTarget->equipped.erase(pcard);
pcard->equipTarget = 0;
}
}
pcard->is_hovered = false;
pcard->is_showequip = false;
pcard->is_showtarget = false;
pcard->is_showchaintarget = false;
mainGame->dField.RemoveCard(previous.controler, previous.location, previous.sequence);
pcard->position = current.position;
mainGame->dField.AddCard(pcard, current.controler, current.location, current.sequence);
if(!mainGame->dInfo.isCatchingUp) {
if (previous.location == current.location && previous.controler == current.controler && (current.location & (LOCATION_DECK | LOCATION_GRAVE | LOCATION_REMOVED | LOCATION_EXTRA))) {
constexpr float milliseconds = 5.0f * 1000.0f / 60.0f;
pcard->dPos.set(-1.5f / milliseconds, 0, 0);
pcard->dRot.set(0, 0, 0);
if (previous.controler == 1) pcard->dPos.X *= -0.1f;
pcard->is_moving = true;
pcard->aniFrame = milliseconds;
mainGame->WaitFrameSignal(5, lock);
mainGame->dField.MoveCard(pcard, 5);
mainGame->WaitFrameSignal(5, lock);
} else {
if (current.location == LOCATION_MZONE && pcard->overlayed.size() > 0) {
for (const auto& ocard : pcard->overlayed)
mainGame->dField.MoveCard(ocard, 10);
mainGame->WaitFrameSignal(10, lock);
}
if (current.location == LOCATION_HAND) {
for (const auto& hcard : mainGame->dField.hand[current.controler])
mainGame->dField.MoveCard(hcard, 10);
} else
mainGame->dField.MoveCard(pcard, 10);
if(previous.location == LOCATION_HAND) {
for(const auto& hcard : mainGame->dField.hand[previous.controler])
mainGame->dField.MoveCard(hcard, 10);
}
mainGame->WaitFrameSignal(5, lock);
}
}
} else if (!(previous.location & LOCATION_OVERLAY)) {
ClientCard* pcard = mainGame->dField.GetCard(previous.controler, previous.location, previous.sequence);
if (code != 0 && pcard->code != code)
pcard->SetCode(code);
pcard->counters.clear();
pcard->ClearTarget();
pcard->is_showtarget = false;
pcard->is_showchaintarget = false;
mainGame->dField.RemoveCard(previous.controler, previous.location, previous.sequence);
ClientCard* olcard = mainGame->dField.GetCard(current.controler, current.location & (~LOCATION_OVERLAY) & 0xff, current.sequence);
olcard->overlayed.push_back(pcard);
mainGame->dField.overlay_cards.insert(pcard);
pcard->overlayTarget = olcard;
pcard->location = LOCATION_OVERLAY;
pcard->sequence = static_cast<uint32_t>(olcard->overlayed.size() - 1);
if(!mainGame->dInfo.isCatchingUp && (olcard->location & LOCATION_ONFIELD)) {
mainGame->dField.MoveCard(pcard, 10);
if(previous.location == LOCATION_HAND)
for(const auto& hcard : mainGame->dField.hand[previous.controler])
mainGame->dField.MoveCard(hcard, 10);
mainGame->WaitFrameSignal(5, lock);
}
if(!mainGame->dInfo.isCatchingUp)
mainGame->WaitFrameSignal(5, lock);
} else if (!(current.location & LOCATION_OVERLAY)) {
ClientCard* olcard = mainGame->dField.GetCard(previous.controler, previous.location & (~LOCATION_OVERLAY) & 0xff, previous.sequence);
ClientCard* pcard = olcard->overlayed[previous.position];
olcard->overlayed.erase(olcard->overlayed.begin() + pcard->sequence);
pcard->overlayTarget = 0;
pcard->position = current.position;
mainGame->dField.AddCard(pcard, current.controler, current.location, current.sequence);
mainGame->dField.overlay_cards.erase(pcard);
for(size_t i = 0; i < olcard->overlayed.size(); ++i) {
olcard->overlayed[i]->sequence = static_cast<uint32_t>(i);
if(!mainGame->dInfo.isCatchingUp)
mainGame->dField.MoveCard(olcard->overlayed[i], 2);
}
if(!mainGame->dInfo.isCatchingUp) {
mainGame->WaitFrameSignal(5, lock);
mainGame->dField.MoveCard(pcard, 10);
mainGame->WaitFrameSignal(5, lock);
}
} else {
ClientCard* olcard1 = mainGame->dField.GetCard(previous.controler, previous.location & (~LOCATION_OVERLAY) & 0xff, previous.sequence);
ClientCard* pcard = olcard1->overlayed[previous.position];
ClientCard* olcard2 = mainGame->dField.GetCard(current.controler, current.location & (~LOCATION_OVERLAY) & 0xff, current.sequence);
olcard1->overlayed.erase(olcard1->overlayed.begin() + pcard->sequence);
olcard2->overlayed.push_back(pcard);
pcard->sequence = static_cast<uint32_t>(olcard2->overlayed.size() - 1);
pcard->location = LOCATION_OVERLAY;
pcard->overlayTarget = olcard2;
for(size_t i = 0; i < olcard1->overlayed.size(); ++i) {
olcard1->overlayed[i]->sequence = static_cast<uint32_t>(i);
if(!mainGame->dInfo.isCatchingUp)
mainGame->dField.MoveCard(olcard1->overlayed[i], 2);
}
if(!mainGame->dInfo.isCatchingUp) {
mainGame->dField.MoveCard(pcard, 10);
mainGame->WaitFrameSignal(5, lock);
}
}
}
return true;
}
case MSG_POS_CHANGE: {
const auto code = BufferIO::Read<uint32_t>(pbuf);
const auto cc = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto cl = BufferIO::Read<uint8_t>(pbuf);
const auto cs = BufferIO::Read<uint8_t>(pbuf);
const auto pp = BufferIO::Read<uint8_t>(pbuf);
const auto cp = BufferIO::Read<uint8_t>(pbuf);
auto lock = LockIf();
ClientCard* pcard = mainGame->dField.GetCard(cc, cl, cs);
if((pp & POS_FACEUP) && (cp & POS_FACEDOWN)) {
pcard->counters.clear();
pcard->ClearTarget();
}
if (code != 0 && pcard->code != code)
pcard->SetCode(code);
pcard->position = cp;
if(!mainGame->dInfo.isCatchingUp) {
event_string = gDataManager->GetSysString(1600).data();
mainGame->dField.MoveCard(pcard, 10);
mainGame->WaitFrameSignal(11, lock);
}
return true;
}
case MSG_SET: {
Play(SoundManager::SFX::SET);
/*const auto code = BufferIO::Read<uint32_t>(pbuf);*/
/*CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);*/
event_string = gDataManager->GetSysString(1601).data();
return true;
}
case MSG_SWAP: {
/*const auto code1 = */BufferIO::Read<uint32_t>(pbuf);
CoreUtils::loc_info info1 = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info1.controler = mainGame->LocalPlayer(info1.controler);
/*const auto code2 = */BufferIO::Read<uint32_t>(pbuf);
CoreUtils::loc_info info2 = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info2.controler = mainGame->LocalPlayer(info2.controler);
event_string = gDataManager->GetSysString(1602).data();
ClientCard* pc1 = mainGame->dField.GetCard(info1.controler, info1.location, info1.sequence);
ClientCard* pc2 = mainGame->dField.GetCard(info2.controler, info2.location, info2.sequence);
auto lock = LockIf();
mainGame->dField.RemoveCard(info1.controler, info1.location, info1.sequence);
mainGame->dField.RemoveCard(info2.controler, info2.location, info2.sequence);
mainGame->dField.AddCard(pc1, info2.controler, info2.location, info2.sequence);
mainGame->dField.AddCard(pc2, info1.controler, info1.location, info1.sequence);
if(!mainGame->dInfo.isCatchingUp) {
mainGame->dField.MoveCard(pc1, 10);
mainGame->dField.MoveCard(pc2, 10);
for(size_t i = 0; i < pc1->overlayed.size(); ++i)
mainGame->dField.MoveCard(pc1->overlayed[i], 10);
for(size_t i = 0; i < pc2->overlayed.size(); ++i)
mainGame->dField.MoveCard(pc2->overlayed[i], 10);
mainGame->WaitFrameSignal(11, lock);
}
return true;
}
case MSG_FIELD_DISABLED: {
auto disabled = BufferIO::Read<uint32_t>(pbuf);
if (!mainGame->dInfo.isFirst)
disabled = (disabled >> 16) | (disabled << 16);
mainGame->dField.disabled_field = disabled;
return true;
}
case MSG_SUMMONING: {
const auto code = BufferIO::Read<uint32_t>(pbuf);
/*CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);*/
if(!PlayChant(SoundManager::CHANT::SUMMON, code))
Play(SoundManager::SFX::SUMMON);
if(!mainGame->dInfo.isCatchingUp) {
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
event_string = epro::sprintf(gDataManager->GetSysString(1603), gDataManager->GetName(code));
mainGame->showcardcode = code;
mainGame->showcarddif = 0;
mainGame->showcardp = 0;
mainGame->showcard = 7;
mainGame->WaitFrameSignal(30, lock);
mainGame->showcard = 0;
mainGame->WaitFrameSignal(11, lock);
}
return true;
}
case MSG_SUMMONED: {
event_string = gDataManager->GetSysString(1604).data();
return true;
}
case MSG_SPSUMMONING: {
const auto code = BufferIO::Read<uint32_t>(pbuf);
/*CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);*/
if(!code || !PlayChant(SoundManager::CHANT::SUMMON, code))
Play(SoundManager::SFX::SPECIAL_SUMMON);
if(!mainGame->dInfo.isCatchingUp) {
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
event_string = epro::sprintf(gDataManager->GetSysString(1605), gDataManager->GetName(code));
if(code) {
mainGame->showcardcode = code;
mainGame->showcarddif = 1;
mainGame->showcard = 5;
mainGame->WaitFrameSignal(30, lock);
mainGame->showcard = 0;
mainGame->WaitFrameSignal(11, lock);
}
}
return true;
}
case MSG_SPSUMMONED: {
event_string = gDataManager->GetSysString(1606).data();
return true;
}
case MSG_FLIPSUMMONING: {
const auto code = BufferIO::Read<uint32_t>(pbuf);
CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info.controler = mainGame->LocalPlayer(info.controler);
if(!PlayChant(SoundManager::CHANT::SUMMON, code))
Play(SoundManager::SFX::FLIP);
ClientCard* pcard = mainGame->dField.GetCard(info.controler, info.location, info.sequence);
pcard->SetCode(code);
pcard->position = info.position;
if(!mainGame->dInfo.isCatchingUp) {
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
event_string = epro::sprintf(gDataManager->GetSysString(1607), gDataManager->GetName(code));
mainGame->dField.MoveCard(pcard, 10);
mainGame->WaitFrameSignal(11, lock);
mainGame->showcardcode = code;
mainGame->showcarddif = 0;
mainGame->showcardp = 0;
mainGame->showcard = 7;
mainGame->WaitFrameSignal(30, lock);
mainGame->showcard = 0;
mainGame->WaitFrameSignal(11, lock);
}
return true;
}
case MSG_FLIPSUMMONED: {
event_string = gDataManager->GetSysString(1608).data();
return true;
}
case MSG_CHAINING: {
const auto code = BufferIO::Read<uint32_t>(pbuf);
if (!PlayChant(SoundManager::CHANT::ACTIVATE, code))
Play(SoundManager::SFX::ACTIVATE);
CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
const auto cc = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto cl = BufferIO::Read<uint8_t>(pbuf);
const auto cs = CompatRead<uint8_t, uint32_t>(pbuf);
const auto desc = CompatRead<uint32_t, uint64_t>(pbuf);
/*const auto ct = */CompatRead<uint8_t, uint32_t>(pbuf);
ClientCard* pcard = mainGame->dField.GetCard(mainGame->LocalPlayer(info.controler), info.location, info.sequence, info.position);
auto lock = LockIf();
if(pcard->code != code || (!pcard->is_public && !mainGame->dInfo.compat_mode)) {
pcard->is_public = mainGame->dInfo.compat_mode;
pcard->code = code;
if(!mainGame->dInfo.isCatchingUp)
mainGame->dField.MoveCard(pcard, 10);
}
if(!mainGame->dInfo.isCatchingUp) {
mainGame->showcardcode = code;
mainGame->showcarddif = 0;
mainGame->showcard = 1;
pcard->is_highlighting = true;
if(pcard->location & 0x30) {
constexpr float milliseconds = 5.0f * 1000.0f / 60.0f;
float shift = -0.75f / milliseconds;
if(info.controler == 1) shift *= -1.0f;
pcard->dPos.set(shift, 0, 0);
pcard->dRot.set(0, 0, 0);
pcard->is_moving = true;
pcard->aniFrame = milliseconds;
mainGame->WaitFrameSignal(30, lock);
mainGame->dField.MoveCard(pcard, 5);
} else
mainGame->WaitFrameSignal(30, lock);
pcard->is_highlighting = false;
}
mainGame->dField.current_chain.chain_card = pcard;
mainGame->dField.current_chain.code = code;
mainGame->dField.current_chain.desc = desc;
mainGame->dField.current_chain.controler = cc;
mainGame->dField.current_chain.location = cl;
mainGame->dField.current_chain.sequence = cs;
mainGame->dField.current_chain.UpdateDrawCoordinates();
mainGame->dField.current_chain.solved = false;
mainGame->dField.current_chain.target.clear();
if(cl == LOCATION_HAND)
mainGame->dField.current_chain.chain_pos.X += 0.35f;
else {
int chc = 0;
for(const auto& chain : mainGame->dField.chains) {
if(cl == LOCATION_GRAVE || cl == LOCATION_REMOVED) {
if(chain.controler == cc && chain.location == cl)
chc++;
} else {
if(chain.controler == cc && chain.location == cl && chain.sequence == cs)
chc++;
}
}
mainGame->dField.current_chain.chain_pos.Y += chc * 0.25f;
}
return true;
}
case MSG_CHAINED: {
const auto ct = BufferIO::Read<uint8_t>(pbuf);
auto lock = LockIf();
event_string = epro::sprintf(gDataManager->GetSysString(1609), gDataManager->GetName(mainGame->dField.current_chain.code));
mainGame->dField.chains.push_back(mainGame->dField.current_chain);
if (ct > 1 && !mainGame->dInfo.isCatchingUp)
mainGame->WaitFrameSignal(20, lock);
mainGame->dField.last_chain = true;
return true;
}
case MSG_CHAIN_SOLVING: {
const auto ct = BufferIO::Read<uint8_t>(pbuf);
auto lock = LockIf();
if(!mainGame->dInfo.isCatchingUp) {
if(mainGame->dField.last_chain)
mainGame->WaitFrameSignal(11, lock);
for(int i = 0; i < 5; ++i) {
mainGame->dField.chains[ct - 1].solved = false;
mainGame->WaitFrameSignal(3, lock);
mainGame->dField.chains[ct - 1].solved = true;
mainGame->WaitFrameSignal(3, lock);
}
} else {
mainGame->dField.chains[ct - 1].solved = true;
}
mainGame->dField.last_chain = false;
return true;
}
case MSG_CHAIN_SOLVED: {
/*const auto ct = BufferIO::Read<uint8_t>(pbuf);*/
return true;
}
case MSG_CHAIN_END: {
for(const auto& chain : mainGame->dField.chains) {
for(const auto& target : chain.target)
target->is_showchaintarget = false;
chain.chain_card->is_showchaintarget = false;
}
mainGame->dField.chains.clear();
return true;
}
case MSG_CHAIN_NEGATED:
case MSG_CHAIN_DISABLED: {
const auto ct = BufferIO::Read<uint8_t>(pbuf);
if(!mainGame->dInfo.isCatchingUp) {
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->showcardcode = mainGame->dField.chains[ct - 1].code;
mainGame->showcarddif = 0;
mainGame->showcard = 3;
mainGame->WaitFrameSignal(30, lock);
mainGame->showcard = 0;
}
return true;
}
case MSG_RANDOM_SELECTED: {
/*const auto player = */BufferIO::Read<uint8_t>(pbuf);
const auto count = CompatRead<uint8_t, uint32_t>(pbuf);
if(mainGame->dInfo.isCatchingUp) {
return true;
}
std::vector<ClientCard*> pcards;
pcards.resize(count);
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
for (auto& pcard : pcards) {
CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info.controler = mainGame->LocalPlayer(info.controler);
if ((info.location & LOCATION_OVERLAY) > 0)
pcard = mainGame->dField.GetCard(info.controler, info.location & (~LOCATION_OVERLAY) & 0xff, info.sequence)->overlayed[info.position];
else
pcard = mainGame->dField.GetCard(info.controler, info.location, info.sequence);
pcard->is_highlighting = true;
}
mainGame->WaitFrameSignal(30, lock);
for(auto& pcard : pcards)
pcard->is_highlighting = false;
return true;
}
case MSG_CARD_SELECTED:
case MSG_BECOME_TARGET: {
if(mainGame->dInfo.isCatchingUp)
return true;
const auto count = CompatRead<uint8_t, uint32_t>(pbuf);
for(uint32_t i = 0; i < count; ++i) {
CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
ClientCard* pcard = mainGame->dField.GetCard(mainGame->LocalPlayer(info.controler), info.location, info.sequence);
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
pcard->is_highlighting = true;
if(mainGame->dInfo.curMsg == MSG_BECOME_TARGET)
mainGame->dField.current_chain.target.insert(pcard);
if(pcard->location & LOCATION_ONFIELD) {
for (int j = 0; j < 3; ++j) {
mainGame->dField.FadeCard(pcard, 5, 5);
mainGame->WaitFrameSignal(5, lock);
mainGame->dField.FadeCard(pcard, 255, 5);
mainGame->WaitFrameSignal(5, lock);
}
} else if(pcard->location & 0x30) {
constexpr float milliseconds = 5.0f * 1000.0f / 60.0f;
float shift = -0.75f / milliseconds;
if(info.controler == 1) shift *= -1.0f;
pcard->dPos.set(shift, 0, 0);
pcard->dRot.set(0, 0, 0);
pcard->is_moving = true;
pcard->aniFrame = milliseconds;
mainGame->WaitFrameSignal(30, lock);
mainGame->dField.MoveCard(pcard, 5);
} else
mainGame->WaitFrameSignal(30, lock);
mainGame->AddLog(epro::sprintf(gDataManager->GetSysString((mainGame->dInfo.curMsg == MSG_BECOME_TARGET) ? 1610 : 1680), gDataManager->GetName(pcard->code), gDataManager->FormatLocation(info.location, info.sequence), info.sequence + 1), pcard->code);
pcard->is_highlighting = false;
}
return true;
}
case MSG_DRAW: {
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto count = CompatRead<uint8_t, uint32_t>(pbuf);
auto lock = LockIf();
auto& deck = mainGame->dField.deck[player];
for (auto it = deck.crbegin(), end = it + count; it != end; ++it) {
auto pcard = *it;
const auto code = BufferIO::Read<uint32_t>(pbuf);
if(!mainGame->dInfo.compat_mode) {
/*uint32_t position =*/BufferIO::Read<uint32_t>(pbuf);
if(!mainGame->dField.deck_reversed || code)
pcard->SetCode(code);
} else if(!mainGame->dField.deck_reversed || code) {
pcard->SetCode(code & 0x7fffffff);
}
}
for(uint32_t i = 0; i < count; ++i) {
Play(SoundManager::SFX::DRAW);
const auto pcard = deck.back();
deck.pop_back();
mainGame->dField.AddCard(pcard, player, LOCATION_HAND, 0);
if(!mainGame->dInfo.isCatchingUp) {
for(auto& hand_pcard : mainGame->dField.hand[player])
mainGame->dField.MoveCard(hand_pcard, 10);
mainGame->WaitFrameSignal(5, lock);
}
}
event_string = epro::sprintf(gDataManager->GetSysString(1611 + player), count);
return true;
}
case MSG_DAMAGE: {
Play(SoundManager::SFX::DAMAGE);
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto val = BufferIO::Read<uint32_t>(pbuf);
int final = mainGame->dInfo.lp[player] - val;
if (final < 0)
final = 0;
auto lock = LockIf();
if(!mainGame->dInfo.isCatchingUp) {
mainGame->lpd = (mainGame->dInfo.lp[player] - final) / 10;
event_string = epro::sprintf(gDataManager->GetSysString(1613 + player), val);
mainGame->lpccolor = 0xff0000;
mainGame->lpcalpha = 0xff;
mainGame->lpplayer = player;
mainGame->lpcstring = epro::format(L"-{}", val);
mainGame->WaitFrameSignal(30, lock);
mainGame->lpframe = 10;
mainGame->WaitFrameSignal(11, lock);
mainGame->lpcstring = L"";
}
mainGame->dInfo.lp[player] = final;
mainGame->dInfo.strLP[player] = epro::to_wstring(mainGame->dInfo.lp[player]);
return true;
}
case MSG_RECOVER: {
Play(SoundManager::SFX::RECOVER);
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto val = BufferIO::Read<uint32_t>(pbuf);
const int final = mainGame->dInfo.lp[player] + val;
auto lock = LockIf();
if(!mainGame->dInfo.isCatchingUp) {
mainGame->lpd = (mainGame->dInfo.lp[player] - final) / 10;
event_string = epro::sprintf(gDataManager->GetSysString(1615 + player), val);
mainGame->lpccolor = 0x00ff00;
mainGame->lpcalpha = 0xff;
mainGame->lpplayer = player;
mainGame->lpcstring = epro::format(L"+{}", val);
mainGame->WaitFrameSignal(30, lock);
mainGame->lpframe = 10;
mainGame->WaitFrameSignal(11, lock);
mainGame->lpcstring = L"";
}
mainGame->dInfo.lp[player] = final;
mainGame->dInfo.strLP[player] = epro::to_wstring(mainGame->dInfo.lp[player]);
return true;
}
case MSG_EQUIP: {
Play(SoundManager::SFX::EQUIP);
CoreUtils::loc_info info1 = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
CoreUtils::loc_info info2 = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
ClientCard* pc1 = mainGame->dField.GetCard(mainGame->LocalPlayer(info1.controler), info1.location, info1.sequence);
ClientCard* pc2 = mainGame->dField.GetCard(mainGame->LocalPlayer(info2.controler), info2.location, info2.sequence);
auto lock = LockIf();
if(pc1->equipTarget)
pc1->equipTarget->equipped.erase(pc1);
pc1->equipTarget = pc2;
pc2->equipped.insert(pc1);
if(!mainGame->dInfo.isCatchingUp) {
if(pc1->equipTarget) {
pc1->is_showequip = false;
pc1->equipTarget->is_showequip = false;
}
if(mainGame->dField.hovered_card == pc1)
pc2->is_showequip = true;
else if(mainGame->dField.hovered_card == pc2)
pc1->is_showequip = true;
}
return true;
}
case MSG_LPUPDATE: {
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto val = BufferIO::Read<uint32_t>(pbuf);
auto lock = LockIf();
if(!mainGame->dInfo.isCatchingUp) {
mainGame->lpd = (mainGame->dInfo.lp[player] - val) / 10;
mainGame->lpplayer = player;
mainGame->lpframe = 10;
mainGame->WaitFrameSignal(11, lock);
}
mainGame->dInfo.lp[player] = val;
mainGame->dInfo.strLP[player] = epro::to_wstring(mainGame->dInfo.lp[player]);
return true;
}
case MSG_UNEQUIP: {
CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
ClientCard* pc = mainGame->dField.GetCard(mainGame->LocalPlayer(info.controler), info.location, info.sequence);
auto lock = LockIf();
if(!mainGame->dInfo.isCatchingUp) {
if(mainGame->dField.hovered_card == pc)
pc->equipTarget->is_showequip = false;
else if(mainGame->dField.hovered_card == pc->equipTarget)
pc->is_showequip = false;
}
pc->equipTarget->equipped.erase(pc);
pc->equipTarget = 0;
return true;
}
case MSG_CARD_TARGET: {
CoreUtils::loc_info info1 = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
CoreUtils::loc_info info2 = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
ClientCard* pc1 = mainGame->dField.GetCard(mainGame->LocalPlayer(info1.controler), info1.location, info1.sequence);
ClientCard* pc2 = mainGame->dField.GetCard(mainGame->LocalPlayer(info2.controler), info2.location, info2.sequence);
auto lock = LockIf();
pc1->cardTarget.insert(pc2);
pc2->ownerTarget.insert(pc1);
if(!mainGame->dInfo.isCatchingUp) {
if(mainGame->dField.hovered_card == pc1)
pc2->is_showtarget = true;
else if(mainGame->dField.hovered_card == pc2)
pc1->is_showtarget = true;
}
break;
}
case MSG_CANCEL_TARGET: {
CoreUtils::loc_info info1 = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
CoreUtils::loc_info info2 = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
ClientCard* pc1 = mainGame->dField.GetCard(mainGame->LocalPlayer(info1.controler), info1.location, info1.sequence);
ClientCard* pc2 = mainGame->dField.GetCard(mainGame->LocalPlayer(info2.controler), info2.location, info2.sequence);
auto lock = LockIf();
pc1->cardTarget.erase(pc2);
pc2->ownerTarget.erase(pc1);
if(!mainGame->dInfo.isCatchingUp) {
if(mainGame->dField.hovered_card == pc1)
pc2->is_showtarget = false;
else if(mainGame->dField.hovered_card == pc2)
pc1->is_showtarget = false;
}
break;
}
case MSG_PAY_LPCOST: {
Play(SoundManager::SFX::DAMAGE);
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto cost = BufferIO::Read<uint32_t>(pbuf);
int final = mainGame->dInfo.lp[player] - cost;
if (final < 0)
final = 0;
auto lock = LockIf();
if(!mainGame->dInfo.isCatchingUp) {
mainGame->lpd = (mainGame->dInfo.lp[player] - final) / 10;
mainGame->lpccolor = 0x0000ff;
mainGame->lpcalpha = 0xff;
mainGame->lpplayer = player;
mainGame->lpcstring = epro::format(L"-{}", cost);
mainGame->WaitFrameSignal(30, lock);
mainGame->lpframe = 10;
mainGame->WaitFrameSignal(11, lock);
mainGame->lpcstring = L"";
}
mainGame->dInfo.lp[player] = final;
mainGame->dInfo.strLP[player] = epro::to_wstring(mainGame->dInfo.lp[player]);
return true;
}
case MSG_ADD_COUNTER: {
Play(SoundManager::SFX::COUNTER_ADD);
const auto type = BufferIO::Read<uint16_t>(pbuf);
const auto c = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto l = BufferIO::Read<uint8_t>(pbuf);
const auto s = BufferIO::Read<uint8_t>(pbuf);
const auto count = BufferIO::Read<uint16_t>(pbuf);
ClientCard* pc = mainGame->dField.GetCard(c, l, s);
if (pc->counters.count(type))
pc->counters[type] += count;
else pc->counters[type] = count;
if(mainGame->dInfo.isCatchingUp)
return true;
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
pc->is_highlighting = true;
mainGame->stACMessage->setText(epro::format(gDataManager->GetSysString(1617), gDataManager->GetName(pc->code), gDataManager->GetCounterName(type), count).data());
mainGame->PopupElement(mainGame->wACMessage, 20);
mainGame->WaitFrameSignal(40, lock);
pc->is_highlighting = false;
return true;
}
case MSG_REMOVE_COUNTER: {
Play(SoundManager::SFX::COUNTER_REMOVE);
const auto type = BufferIO::Read<uint16_t>(pbuf);
const auto c = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto l = BufferIO::Read<uint8_t>(pbuf);
const auto s = BufferIO::Read<uint8_t>(pbuf);
const auto count = BufferIO::Read<uint16_t>(pbuf);
ClientCard* pc = mainGame->dField.GetCard(c, l, s);
auto lock = LockIf();
pc->counters[type] -= count;
if (pc->counters[type] <= 0)
pc->counters.erase(type);
if(mainGame->dInfo.isCatchingUp)
return true;
pc->is_highlighting = true;
mainGame->stACMessage->setText(epro::format(gDataManager->GetSysString(1618), gDataManager->GetName(pc->code), gDataManager->GetCounterName(type), count).data());
mainGame->PopupElement(mainGame->wACMessage, 20);
mainGame->WaitFrameSignal(40, lock);
pc->is_highlighting = false;
return true;
}
case MSG_ATTACK: {
CoreUtils::loc_info info1 = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info1.controler = mainGame->LocalPlayer(info1.controler);
mainGame->dField.attacker = mainGame->dField.GetCard(info1.controler, info1.location, info1.sequence);
if(!PlayChant(SoundManager::CHANT::ATTACK, mainGame->dField.attacker->code))
Play(SoundManager::SFX::ATTACK);
if(mainGame->dInfo.isCatchingUp)
return true;
CoreUtils::loc_info info2 = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
const bool is_direct = info2.location == 0;
info2.controler = mainGame->LocalPlayer(info2.controler);
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
float sy;
float xa = mainGame->dField.attacker->curPos.X;
float ya = mainGame->dField.attacker->curPos.Y;
float xd, yd;
if (!is_direct) {
mainGame->dField.attack_target = mainGame->dField.GetCard(info2.controler, info2.location, info2.sequence);
event_string = epro::format(gDataManager->GetSysString(1619), gDataManager->GetName(mainGame->dField.attacker->code),
gDataManager->GetName(mainGame->dField.attack_target->code));
xd = mainGame->dField.attack_target->curPos.X;
yd = mainGame->dField.attack_target->curPos.Y;
} else {
event_string = epro::format(gDataManager->GetSysString(1620), gDataManager->GetName(mainGame->dField.attacker->code));
xd = 3.95f;
yd = (info1.controler == 0) ? -3.5f : 3.5f;
}
sy = std::sqrt((xa - xd) * (xa - xd) + (ya - yd) * (ya - yd)) / 2.0f;
mainGame->atk_t.set((xa + xd) / 2, (ya + yd) / 2, 0);
mainGame->atk_r.set(0, 0, -std::atan((xd - xa) / (yd - ya)));
if(ya <= yd)
mainGame->atk_r.Z += irr::core::PI;
matManager.GenArrow(sy);
mainGame->attack_sv = 0.0f;
mainGame->is_attacking = true;
mainGame->WaitFrameSignal(40, lock);
mainGame->is_attacking = false;
return true;
}
case MSG_BATTLE: {
if(mainGame->dInfo.isCatchingUp)
return true;
CoreUtils::loc_info info1 = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info1.controler = mainGame->LocalPlayer(info1.controler);
const auto aatk = static_cast<int32_t>(BufferIO::Read<uint32_t>(pbuf));
const auto adef = static_cast<int32_t>(BufferIO::Read<uint32_t>(pbuf));
/*const auto da = */BufferIO::Read<uint8_t>(pbuf);
CoreUtils::loc_info info2 = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info2.controler = mainGame->LocalPlayer(info2.controler);
const auto datk = static_cast<int32_t>(BufferIO::Read<uint32_t>(pbuf));
const auto ddef = static_cast<int32_t>(BufferIO::Read<uint32_t>(pbuf));
/*const auto dd = */BufferIO::Read<uint8_t>(pbuf);
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
ClientCard* pcard = mainGame->dField.GetCard(info1.controler, info1.location, info1.sequence);
if(aatk != pcard->attack) {
pcard->attack = aatk;
pcard->atkstring = epro::to_wstring(aatk);
}
if(adef != pcard->defense) {
pcard->defense = adef;
pcard->defstring = epro::to_wstring(adef);
}
if(info2.location) {
pcard = mainGame->dField.GetCard(info2.controler, info2.location, info2.sequence);
if(datk != pcard->attack) {
pcard->attack = datk;
pcard->atkstring = epro::to_wstring(datk);
}
if(ddef != pcard->defense) {
pcard->defense = ddef;
pcard->defstring = epro::to_wstring(ddef);
}
}
return true;
}
case MSG_ATTACK_DISABLED: {
event_string = epro::sprintf(gDataManager->GetSysString(1621), gDataManager->GetName(mainGame->dField.attacker->code));
return true;
}
case MSG_DAMAGE_STEP_START: {
return true;
}
case MSG_DAMAGE_STEP_END: {
return true;
}
case MSG_MISSED_EFFECT: {
if(mainGame->dInfo.isCatchingUp)
return true;
CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
uint32_t code = BufferIO::Read<uint32_t>(pbuf);
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->AddLog(epro::sprintf(gDataManager->GetSysString(1622), gDataManager->GetName(code)), code);
return true;
}
case MSG_TOSS_COIN: {
if(mainGame->dInfo.isCatchingUp)
return true;
Play(SoundManager::SFX::COIN);
/*const auto player = */mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto count = BufferIO::Read<uint8_t>(pbuf);
std::wstring text(gDataManager->GetSysString(1623));
for (int i = 0; i < count; ++i) {
bool res = !!BufferIO::Read<uint8_t>(pbuf);
text += epro::format(L"[{}]", gDataManager->GetSysString(res ? 60 : 61));
}
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->AddLog(text);
mainGame->stACMessage->setText(text.data());
mainGame->PopupElement(mainGame->wACMessage, 20);
mainGame->WaitFrameSignal(40, lock);
return true;
}
case MSG_TOSS_DICE: {
if(mainGame->dInfo.isCatchingUp)
return true;
Play(SoundManager::SFX::DICE);
/*const auto player = */mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto count = BufferIO::Read<uint8_t>(pbuf);
std::wstring text(gDataManager->GetSysString(1624));
for (int i = 0; i < count; ++i) {
uint8_t res = BufferIO::Read<uint8_t>(pbuf);
text += epro::format(L"[{}]", res);
}
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->AddLog(text);
mainGame->stACMessage->setText(text.data());
mainGame->PopupElement(mainGame->wACMessage, 20);
mainGame->WaitFrameSignal(40, lock);
return true;
}
case MSG_ROCK_PAPER_SCISSORS: {
if(mainGame->dInfo.isCatchingUp)
return true;
/*const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));*/
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->wHand->setVisible(true);
return false;
}
case MSG_HAND_RES: {
if(mainGame->dInfo.isCatchingUp)
return true;
const auto res = BufferIO::Read<uint8_t>(pbuf);
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->stHintMsg->setVisible(false);
uint8_t res1 = (res & 0x3) - 1;
uint8_t res2 = ((res >> 2) & 0x3) - 1;
if(mainGame->dInfo.isFirst)
mainGame->showcardcode = res1 + (res2 << 16);
else
mainGame->showcardcode = res2 + (res1 << 16);
mainGame->showcarddif = 50;
mainGame->showcardp = 0;
mainGame->showcard = 100;
mainGame->WaitFrameSignal(60, lock);
return false;
}
case MSG_ANNOUNCE_RACE: {
/*const auto player = */mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
mainGame->dField.announce_count = BufferIO::Read<uint8_t>(pbuf);
const auto available = CompatRead<uint32_t, uint64_t>(pbuf);
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->dField.ShowSelectRace(available);
mainGame->wANRace->setText(gDataManager->GetDesc(select_hint ? select_hint : 563, mainGame->dInfo.compat_mode).data());
mainGame->PopupElement(mainGame->wANRace);
select_hint = 0;
return false;
}
case MSG_ANNOUNCE_ATTRIB: {
/*const auto player = */mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
mainGame->dField.announce_count = BufferIO::Read<uint8_t>(pbuf);
const auto available = BufferIO::Read<uint32_t>(pbuf);
uint32_t filter = 0x1;
for(auto i = 0u; i < sizeofarr(mainGame->chkAttribute); ++i, filter <<= 1) {
mainGame->chkAttribute[i]->setChecked(false);
mainGame->chkAttribute[i]->setVisible((filter& available) != 0);
}
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->wANAttribute->setText(gDataManager->GetDesc(select_hint ? select_hint : 562, mainGame->dInfo.compat_mode).data());
mainGame->PopupElement(mainGame->wANAttribute);
select_hint = 0;
return false;
}
case MSG_ANNOUNCE_CARD: {
/*const auto player = */mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto count = BufferIO::Read<uint8_t>(pbuf);
mainGame->dField.declare_opcodes.clear();
for (int i = 0; i < count; ++i)
mainGame->dField.declare_opcodes.push_back(CompatRead<uint32_t, uint64_t>(pbuf));
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->ebANCard->setText(L"");
mainGame->wANCard->setText(gDataManager->GetDesc(select_hint ? select_hint : 564, mainGame->dInfo.compat_mode).data());
if(mainGame->dField.UpdateDeclarableList() == 0) {
DuelClient::SetResponseI(0);
return true;
}
mainGame->PopupElement(mainGame->wANCard);
select_hint = 0;
return false;
}
case MSG_ANNOUNCE_NUMBER: {
/*const auto player = */mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto count = BufferIO::Read<uint8_t>(pbuf);
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->cbANNumber->clear();
for (int i = 0; i < count; ++i) {
uint32_t value = (uint32_t)((CompatRead<uint32_t, uint64_t>(pbuf)) & 0xffffffff);
mainGame->cbANNumber->addItem(epro::format(L" {}", value).data(), value);
}
mainGame->cbANNumber->setSelected(0);
mainGame->wANNumber->setText(gDataManager->GetDesc(select_hint ? select_hint : 565, mainGame->dInfo.compat_mode).data());
mainGame->PopupElement(mainGame->wANNumber);
select_hint = 0;
return false;
}
case MSG_CARD_HINT: {
CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
const auto chtype = BufferIO::Read<uint8_t>(pbuf);
const auto value = CompatRead<uint32_t, uint64_t>(pbuf);
ClientCard* pcard = mainGame->dField.GetCard(mainGame->LocalPlayer(info.controler), info.location, info.sequence);
if(!pcard)
return true;
if(chtype == CHINT_DESC_ADD) {
pcard->desc_hints[value]++;
} else if(chtype == CHINT_DESC_REMOVE) {
pcard->desc_hints[value]--;
if(pcard->desc_hints[value] <= 0)
pcard->desc_hints.erase(value);
} else {
pcard->cHint = chtype;
pcard->chValue = value;
if(chtype == CHINT_TURN) {
if(value == 0)
return true;
if(mainGame->dInfo.isCatchingUp)
return true;
if(pcard->location & LOCATION_ONFIELD)
pcard->is_highlighting = true;
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->showcardcode = pcard->code;
mainGame->showcarddif = 0;
mainGame->showcardp = (value & 0xffff) - 1;
mainGame->showcard = 6;
mainGame->WaitFrameSignal(30, lock);
pcard->is_highlighting = false;
mainGame->showcard = 0;
}
}
return true;
}
case MSG_PLAYER_HINT: {
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto chtype = BufferIO::Read<uint8_t>(pbuf);
const auto value = CompatRead<uint32_t, uint64_t>(pbuf);
auto& player_desc_hints = mainGame->dField.player_desc_hints[player];
if(chtype == PHINT_DESC_ADD) {
player_desc_hints[value]++;
} else if(chtype == PHINT_DESC_REMOVE) {
player_desc_hints[value]--;
if(player_desc_hints[value] <= 0)
player_desc_hints.erase(value);
}
return true;
}
case MSG_MATCH_KILL: {
match_kill = BufferIO::Read<uint32_t>(pbuf);
return true;
}
case MSG_REMOVE_CARDS: {
const auto count = BufferIO::Read<uint32_t>(pbuf);
if(count > 0) {
std::vector<ClientCard*> cards;
cards.resize(count);
for(auto& pcard : cards) {
CoreUtils::loc_info loc = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
loc.controler = mainGame->LocalPlayer(loc.controler);
if(loc.location & LOCATION_OVERLAY) {
auto olcard = mainGame->dField.GetCard(loc.controler, (loc.location & (~LOCATION_OVERLAY)) & 0xff, loc.sequence);
pcard = olcard->overlayed[loc.position];
} else
pcard = mainGame->dField.GetCard(loc.controler, loc.location, loc.sequence);
}
auto lock = LockIf();
if(!mainGame->dInfo.isCatchingUp) {
for(auto& pcard : cards)
mainGame->dField.FadeCard(pcard, 5, 5);
mainGame->WaitFrameSignal(5, lock);
}
for(auto& pcard : cards) {
if(pcard == mainGame->dField.hovered_card)
mainGame->dField.hovered_card = 0;
if(pcard->location & LOCATION_OVERLAY) {
pcard->overlayTarget->overlayed.erase(pcard->overlayTarget->overlayed.begin() + pcard->sequence);
mainGame->dField.overlay_cards.erase(pcard);
for(size_t j = 0; j < pcard->overlayTarget->overlayed.size(); ++j)
pcard->overlayTarget->overlayed[j]->sequence = static_cast<uint32_t>(j);
pcard->overlayTarget = 0;
} else
mainGame->dField.RemoveCard(pcard->controler, pcard->location, pcard->sequence);
delete pcard;
}
}
return true;
}
case MSG_TAG_SWAP: {
const auto player = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
const auto mcount = CompatRead<uint8_t, uint32_t>(pbuf);
const auto ecount = CompatRead<uint8_t, uint32_t>(pbuf);
const auto pcount = CompatRead<uint8_t, uint32_t>(pbuf);
const auto hcount = CompatRead<uint8_t, uint32_t>(pbuf);
const auto topcode = BufferIO::Read<uint32_t>(pbuf);
auto MatchPile = [player,&pcount](auto& pile, uint32_t count, uint8_t location) {
if(pile.size() > count) {
for(auto cit = pile.begin() + count; cit != pile.end(); cit++)
delete *cit;
pile.resize(count);
} else {
while(pile.size() < count) {
ClientCard* ccard = new ClientCard{};
ccard->controler = player;
ccard->location = location;
ccard->sequence = static_cast<uint32_t>(pile.size());
ccard->position = POS_FACEDOWN;
pile.push_back(ccard);
}
}
if(location == LOCATION_EXTRA && pcount) {
auto i = pcount;
for(auto it = pile.rbegin(); i > 0 && it != pile.rend(); it++, i--) {
(*it)->position = POS_FACEUP;
}
}
};
auto lock = LockIf();
if(!mainGame->dInfo.isCatchingUp) {
constexpr float milliseconds = 5.0f * 1000.0f / 60.0f;
for(auto* list : { &mainGame->dField.deck[player] , &mainGame->dField.hand[player] , &mainGame->dField.extra[player] }) {
for(const auto& pcard : *list) {
if(player == 0) pcard->dPos.Y = 2.0f / milliseconds;
else pcard->dPos.Y = -3.0f / milliseconds;
pcard->dRot.set(0, 0, 0);
pcard->is_moving = true;
pcard->aniFrame = milliseconds;
}
}
mainGame->WaitFrameSignal(5, lock);
}
MatchPile(mainGame->dField.deck[player], mcount, LOCATION_DECK);
MatchPile(mainGame->dField.hand[player], hcount, LOCATION_HAND);
MatchPile(mainGame->dField.extra[player], ecount, LOCATION_EXTRA);
mainGame->dField.extra_p_count[player] = pcount;
//
if(!mainGame->dInfo.isCatchingUp) {
for (const auto& pcard : mainGame->dField.deck[player]) {
pcard->UpdateDrawCoordinates();
if(player == 0) pcard->curPos.Y += 2.0f;
else pcard->curPos.Y -= 3.0f;
mainGame->dField.MoveCard(pcard, 5);
}
if(mainGame->dField.deck[player].size())
mainGame->dField.deck[player].back()->code = topcode;
for(auto* list : { &mainGame->dField.hand[player] , &mainGame->dField.extra[player] }) {
for(const auto& pcard : *list) {
if(!mainGame->dInfo.compat_mode) {
pcard->code = BufferIO::Read<uint32_t>(pbuf);
pcard->position = BufferIO::Read<uint32_t>(pbuf);
} else {
const auto flag = BufferIO::Read<uint32_t>(pbuf);
pcard->code = flag & 0x7fffffff;
pcard->position = flag & 0x80000000 ? POS_FACEUP : POS_FACEDOWN;
}
pcard->UpdateDrawCoordinates();
if(player == 0) pcard->curPos.Y += 2.0f;
else pcard->curPos.Y -= 3.0f;
mainGame->dField.MoveCard(pcard, 5);
}
}
mainGame->WaitFrameSignal(5, lock);
}
auto& curplayer = mainGame->dInfo.current_player[player];
++curplayer;
if((player == 0 && mainGame->dInfo.isTeam1) || (player == 1 && !mainGame->dInfo.isTeam1))
curplayer %= mainGame->dInfo.team1;
else
curplayer %= mainGame->dInfo.team2;
break;
}
case MSG_RELOAD_FIELD: {
auto lock = LockIf();
mainGame->dField.Clear();
if(mainGame->dInfo.compat_mode) {
uint8_t field = BufferIO::Read<uint8_t>(pbuf) & 0xf;
mainGame->dInfo.duel_field = field;
switch(field) {
case 1: mainGame->dInfo.duel_params = DUEL_MODE_MR1; break;
case 2: mainGame->dInfo.duel_params = DUEL_MODE_MR2; break;
case 3: mainGame->dInfo.duel_params = DUEL_MODE_MR3; break;
case 4: mainGame->dInfo.duel_params = DUEL_MODE_MR4; break;
default:
case 5: mainGame->dInfo.duel_params = DUEL_MODE_MR5; break;
}
} else {
uint32_t opts = BufferIO::Read<uint32_t>(pbuf);
mainGame->dInfo.duel_field = mainGame->GetMasterRule(opts);
mainGame->dInfo.duel_params = opts;
}
matManager.SetActiveVertices(mainGame->dInfo.HasFieldFlag(DUEL_3_COLUMNS_FIELD),
!mainGame->dInfo.HasFieldFlag(DUEL_SEPARATE_PZONE));
mainGame->SetPhaseButtons();
for(int i = 0; i < 2; ++i) {
int p = mainGame->LocalPlayer(i);
mainGame->dInfo.lp[p] = BufferIO::Read<uint32_t>(pbuf);
mainGame->dInfo.strLP[p] = epro::to_wstring(mainGame->dInfo.lp[p]);
for(int seq = 0; seq < 7; ++seq) {
const auto is_zone_used = !!BufferIO::Read<uint8_t>(pbuf);
if(!is_zone_used)
continue;
ClientCard* ccard = new ClientCard{};
mainGame->dField.AddCard(ccard, p, LOCATION_MZONE, seq);
ccard->position = BufferIO::Read<uint8_t>(pbuf);
const auto xyz_mat_count = CompatRead<uint8_t, uint32_t>(pbuf);
for(uint32_t xyz = 0; xyz < xyz_mat_count; ++xyz) {
ClientCard* xcard = new ClientCard{};
ccard->overlayed.push_back(xcard);
mainGame->dField.overlay_cards.insert(xcard);
xcard->overlayTarget = ccard;
xcard->location = LOCATION_OVERLAY;
xcard->sequence = static_cast<uint32_t>(ccard->overlayed.size() - 1);
xcard->owner = p;
xcard->controler = p;
}
}
for(int seq = 0; seq < 8; ++seq) {
const auto is_zone_used = !!BufferIO::Read<uint8_t>(pbuf);
if(!is_zone_used)
continue;
ClientCard* ccard = new ClientCard{};
mainGame->dField.AddCard(ccard, p, LOCATION_SZONE, seq);
ccard->position = BufferIO::Read<uint8_t>(pbuf);
if(mainGame->dInfo.compat_mode)
continue;
const auto xyz_mat_count = BufferIO::Read<uint32_t>(pbuf);
for(uint32_t xyz = 0; xyz < xyz_mat_count; ++xyz) {
ClientCard* xcard = new ClientCard{};
ccard->overlayed.push_back(xcard);
mainGame->dField.overlay_cards.insert(xcard);
xcard->overlayTarget = ccard;
xcard->location = LOCATION_OVERLAY;
xcard->sequence = static_cast<uint32_t>(ccard->overlayed.size() - 1);
xcard->owner = p;
xcard->controler = p;
}
}
auto push_range = [&pbuf,p](uint8_t location) {
const auto val = CompatRead<uint8_t, uint32_t>(pbuf);
for(uint32_t seq = 0; seq < val; ++seq)
mainGame->dField.AddCard(new ClientCard{}, p, location, seq);
};
push_range(LOCATION_DECK);
push_range(LOCATION_HAND);
push_range(LOCATION_GRAVE);
push_range(LOCATION_REMOVED);
push_range(LOCATION_EXTRA);
mainGame->dField.extra_p_count[p] = CompatRead<uint8_t, uint32_t>(pbuf);
}
mainGame->dInfo.startlp = std::max(mainGame->dInfo.lp[0], mainGame->dInfo.lp[1]);
mainGame->dField.RefreshAllCards();
const auto solving_chains = CompatRead<uint8_t, uint32_t>(pbuf);
if(solving_chains > 0) {
for(uint32_t i = 0; i < solving_chains; ++i) {
uint32_t code = BufferIO::Read<uint32_t>(pbuf);
CoreUtils::loc_info info = CoreUtils::ReadLocInfo(pbuf, mainGame->dInfo.compat_mode);
info.controler = mainGame->LocalPlayer(info.controler);
uint8_t cc = mainGame->LocalPlayer(BufferIO::Read<uint8_t>(pbuf));
uint8_t cl = BufferIO::Read<uint8_t>(pbuf);
uint32_t cs = BufferIO::Read<uint32_t>(pbuf);
uint64_t desc = CompatRead<uint32_t, uint64_t>(pbuf);
ClientCard* pcard = mainGame->dField.GetCard(info.controler, info.location, info.sequence, info.position);
mainGame->dField.current_chain.chain_card = pcard;
mainGame->dField.current_chain.code = code;
mainGame->dField.current_chain.desc = desc;
mainGame->dField.current_chain.controler = cc;
mainGame->dField.current_chain.location = cl;
mainGame->dField.current_chain.sequence = cs;
mainGame->dField.current_chain.UpdateDrawCoordinates();
mainGame->dField.current_chain.solved = false;
int chc = 0;
for(const auto& chain : mainGame->dField.chains) {
if(cl == LOCATION_GRAVE || cl == LOCATION_REMOVED) {
if(chain.controler == cc && chain.location == cl)
chc++;
} else {
if(chain.controler == cc && chain.location == cl && chain.sequence == cs)
chc++;
}
}
if(cl == LOCATION_HAND)
mainGame->dField.current_chain.chain_pos.X += 0.35f;
else
mainGame->dField.current_chain.chain_pos.Y += chc * 0.25f;
mainGame->dField.chains.push_back(mainGame->dField.current_chain);
}
event_string = epro::sprintf(gDataManager->GetSysString(1609), gDataManager->GetName(mainGame->dField.current_chain.code));
mainGame->dField.last_chain = true;
}
break;
}
}
return true;
}
void DuelClient::SwapField() {
if(!analyzeMutex.try_lock())
is_swapping = !is_swapping;
else {
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
mainGame->dField.ReplaySwap();
analyzeMutex.unlock();
}
}
void DuelClient::SendResponse() {
if(answered.exchange(true))
return;
auto& msg = mainGame->dInfo.curMsg;
switch(msg) {
case MSG_SELECT_BATTLECMD:
case MSG_SELECT_IDLECMD: {
for(auto& pcard : mainGame->dField.limbo_temp)
delete pcard;
mainGame->dField.limbo_temp.clear();
mainGame->dField.ClearCommandFlag();
if(gGameConfig->alternative_phase_layout) {
mainGame->btnBP->setEnabled(false);
mainGame->btnM2->setEnabled(false);
mainGame->btnEP->setEnabled(false);
} else {
if(msg == MSG_SELECT_BATTLECMD) {
mainGame->btnM2->setVisible(false);
} else {
mainGame->btnBP->setVisible(false);
}
mainGame->btnEP->setVisible(false);
}
mainGame->btnShuffle->setVisible(false);
break;
}
case MSG_SELECT_CARD:
case MSG_SELECT_UNSELECT_CARD:
case MSG_SELECT_CHAIN:
case MSG_CONFIRM_CARDS: {
for(auto& pcard : mainGame->dField.limbo_temp)
delete pcard;
mainGame->dField.limbo_temp.clear();
if(msg == MSG_SELECT_CHAIN)
mainGame->dField.ClearChainSelect();
if(msg != MSG_SELECT_CARD && msg != MSG_SELECT_UNSELECT_CARD)
break;
}
[[fallthrough]];
case MSG_SELECT_TRIBUTE:
case MSG_SELECT_COUNTER: {
mainGame->dField.ClearSelect();
break;
}
case MSG_SELECT_SUM: {
for(auto& pcard : mainGame->dField.must_select_cards)
pcard->is_selected = false;
for(auto& pcard : mainGame->dField.selectsum_all) {
pcard->is_selectable = false;
pcard->is_selected = false;
}
mainGame->dField.must_select_cards.clear();
mainGame->dField.selectsum_all.clear();
break;
}
}
if(mainGame->dInfo.isSingleMode) {
SingleMode::SetResponse(response_buf.data(), response_buf.size());
SingleMode::singleSignal.Set();
} else if (!mainGame->dInfo.isReplay) {
if(replay_stream.size())
replay_stream.pop_back();
/*if(mainGame->dInfo.time_player == 0)
SendPacketToServer(CTOS_TIME_CONFIRM);*/
mainGame->dInfo.time_player = 2;
SendBufferToServer(CTOS_RESPONSE, response_buf.data(), response_buf.size());
}
}
static std::vector<epro::Address> getAddresses() {
#if EDOPRO_ANDROID
return porting::getLocalIP();
#else
std::vector<epro::Address> addresses;
#if EDOPRO_WINDOWS
char hname[256];
gethostname(hname, 256);
evutil_addrinfo hints;
evutil_addrinfo* res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if(evutil_getaddrinfo(hname, nullptr, &hints, &res) != 0)
return {};
for(auto* ptr = res; ptr != nullptr && addresses.size() < 8; ptr = ptr->ai_next) {
if(ptr->ai_family == AF_INET) {
auto addr_in = reinterpret_cast<sockaddr_in*>(ptr->ai_addr);
if(addr_in->sin_addr.s_addr != 0)
addresses.emplace_back(&addr_in->sin_addr.s_addr, epro::Address::INET);
} else if(ptr->ai_family == AF_INET6) {
auto addr_in6 = reinterpret_cast<sockaddr_in6*>(ptr->ai_addr);
addresses.emplace_back(addr_in6->sin6_addr.s6_addr, epro::Address::INET6);
}
}
evutil_freeaddrinfo(res);
#elif EDOPRO_LINUX || EDOPRO_APPLE
ifaddrs* allInterfaces;
if(getifaddrs(&allInterfaces) != 0)
return {};
for(auto* interface = allInterfaces; interface != nullptr && addresses.size() < 8; interface = interface->ifa_next) {
auto flags = interface->ifa_flags;
sockaddr* addr = interface->ifa_addr;
if((flags & (IFF_UP | IFF_RUNNING | IFF_LOOPBACK)) != (IFF_UP | IFF_RUNNING))
continue;
if(addr->sa_family == AF_INET) {
auto addr_in = reinterpret_cast<sockaddr_in*>(addr);
if(addr_in->sin_addr.s_addr != 0)
addresses.emplace_back(&addr_in->sin_addr.s_addr, epro::Address::INET);
} else if(addr->sa_family == AF_INET6) {
auto addr_in6 = reinterpret_cast<sockaddr_in6*>(addr);
addresses.emplace_back(addr_in6->sin6_addr.s6_addr, epro::Address::INET6);
}
}
freeifaddrs(allInterfaces);
#endif
return addresses;
#endif
}
void DuelClient::BeginRefreshHost() {
if(is_refreshing)
return;
is_refreshing = true;
mainGame->btnLanRefresh->setEnabled(false);
mainGame->lstHostList->clear();
hosts.clear();
const auto addresses = getAddresses();
if(addresses.empty()) {
mainGame->btnLanRefresh->setEnabled(true);
is_refreshing = false;
return;
}
event_base* broadev = event_base_new();
if(!broadev)
return;
bool hasIpv6 = false;
auto createAndBindIpv6Socket = [&hasIpv6]() -> evutil_socket_t {
#ifdef IPV6_V6ONLY
evutil_socket_t reply = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if(reply == EVUTIL_INVALID_SOCKET)
return EVUTIL_INVALID_SOCKET;
int off = 0;
if(setsockopt(reply, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, (ev_socklen_t)sizeof(off)) != 0) {
evutil_closesocket(reply);
return EVUTIL_INVALID_SOCKET;
}
sockaddr_in6 reply_addr_v6;
memset(&reply_addr_v6, 0, sizeof(reply_addr_v6));
reply_addr_v6.sin6_family = AF_INET6;
reply_addr_v6.sin6_port = htons(7921);
evutil_inet_pton(AF_INET6, "::", &reply_addr_v6.sin6_addr);
if(bind(reply, reinterpret_cast<sockaddr*>(&reply_addr_v6), sizeof(reply_addr_v6)) == -1) {
evutil_closesocket(reply);
return EVUTIL_INVALID_SOCKET;
}
hasIpv6 = true;
return reply;
#else
return EVUTIL_INVALID_SOCKET;
#endif
};
auto createAndBindIpv4Socket = []() -> evutil_socket_t {
evutil_socket_t reply = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(reply == EVUTIL_INVALID_SOCKET)
return EVUTIL_INVALID_SOCKET;
sockaddr_in reply_addr;
memset(&reply_addr, 0, sizeof(reply_addr));
reply_addr.sin_family = AF_INET;
reply_addr.sin_port = htons(7921);
reply_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(reply, reinterpret_cast<sockaddr*>(&reply_addr), sizeof(reply_addr)) == -1) {
evutil_closesocket(reply);
return EVUTIL_INVALID_SOCKET;
}
return reply;
};
evutil_socket_t reply = createAndBindIpv6Socket();
if(reply == EVUTIL_INVALID_SOCKET && (reply = createAndBindIpv4Socket()) == EVUTIL_INVALID_SOCKET) {
event_base_free(broadev);
mainGame->btnLanRefresh->setEnabled(true);
is_refreshing = false;
return;
}
timeval timeout = { 3, 0 };
resp_event = event_new(broadev, reply, EV_TIMEOUT | EV_READ | EV_PERSIST, BroadcastReply, broadev);
event_add(resp_event, &timeout);
epro::thread(RefreshThread, broadev).detach();
//send request
sockaddr_in localv4;
memset(&localv4, 0, sizeof(localv4));
localv4.sin_family = AF_INET;
sockaddr_in6 localv6;
memset(&localv6, 0, sizeof(localv6));
localv6.sin6_family = AF_INET6;
sockaddr_in sockTov4;
memset(&sockTov4, 0, sizeof(sockTov4));
sockTov4.sin_addr.s_addr = htonl(INADDR_BROADCAST);
sockTov4.sin_family = AF_INET;
sockTov4.sin_port = htons(7920);
sockaddr_in6 sockTov6;
memset(&sockTov6, 0, sizeof(sockTov6));
//multicast address
evutil_inet_pton(AF_INET6, "FF02::1", &sockTov6.sin6_addr);
sockTov6.sin6_family = AF_INET6;
sockTov6.sin6_port = htons(7920);
HostRequest hReq;
hReq.identifier = NETWORK_CLIENT_ID;
for(const auto& address : addresses) {
if(address.family == epro::Address::INET6) {
if(!hasIpv6)
continue;
address.toIn6Addr(localv6.sin6_addr);
evutil_socket_t sSend = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if(sSend == EVUTIL_INVALID_SOCKET)
continue;
if(bind(sSend, reinterpret_cast<sockaddr*>(&localv6), sizeof(localv6)) == -1) {
evutil_closesocket(sSend);
continue;
}
sendto(sSend, reinterpret_cast<const char*>(&hReq), sizeof(HostRequest), 0,
reinterpret_cast<sockaddr*>(&sockTov6), sizeof(sockTov6));
evutil_closesocket(sSend);
} else {
address.toInAddr(localv4.sin_addr);
evutil_socket_t sSend = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sSend == EVUTIL_INVALID_SOCKET)
continue;
int on = 1;
setsockopt(sSend, SOL_SOCKET, SO_BROADCAST, (const char*)&on,
(ev_socklen_t)sizeof(on));
if(bind(sSend, reinterpret_cast<sockaddr*>(&localv4), sizeof(localv4)) == -1) {
evutil_closesocket(sSend);
continue;
}
sendto(sSend, reinterpret_cast<const char*>(&hReq), sizeof(HostRequest), 0,
reinterpret_cast<sockaddr*>(&sockTov4), sizeof(sockTov4));
evutil_closesocket(sSend);
}
}
}
int DuelClient::RefreshThread(event_base* broadev) {
Utils::SetThreadName("RefreshThread");
event_base_dispatch(broadev);
evutil_socket_t fd;
event_get_assignment(resp_event, 0, &fd, 0, 0, 0);
evutil_closesocket(fd);
event_free(resp_event);
event_base_free(broadev);
is_refreshing = false;
return 0;
}
void DuelClient::BroadcastReply(evutil_socket_t fd, short events, void* arg) {
if(events & EV_TIMEOUT) {
event_base_loopbreak((event_base*)arg);
if(!is_closing)
mainGame->btnLanRefresh->setEnabled(true);
} else if(events & EV_READ) {
sockaddr_storage bc_addr;
ev_socklen_t sz = sizeof(sockaddr_storage);
HostPacket packet{};
int ret = recvfrom(fd, reinterpret_cast<char*>(&packet), sizeof(packet), 0, (sockaddr*)&bc_addr, &sz);
if(ret < static_cast<int>(sizeof(HostPacket)))
return;
epro::Address ipaddr;
if(bc_addr.ss_family == AF_INET) {
ipaddr = epro::Address{ &reinterpret_cast<sockaddr_in&>(bc_addr).sin_addr.s_addr, epro::Address::INET };
} else if(bc_addr.ss_family == AF_INET6) {
auto IsV6AddressV4Mapped = [](const in6_addr& addr) {
static constexpr in6_addr ipv6Mapped = {
{
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFF,
}
};
return memcmp(ipv6Mapped.s6_addr, addr.s6_addr, 12) == 0;
};
const auto& v6addr = reinterpret_cast<sockaddr_in6&>(bc_addr).sin6_addr;
if(IsV6AddressV4Mapped(v6addr))
ipaddr = epro::Address{ v6addr.s6_addr + 12, epro::Address::INET };
else
ipaddr = epro::Address{ v6addr.s6_addr, epro::Address::INET6 };
} else
return;
if(is_closing)
return;
if(packet.identifier != NETWORK_SERVER_ID)
return;
const auto remote = epro::Host{ ipaddr, packet.port };
if(std::find(hosts.begin(), hosts.end(), remote) == hosts.end()) {
std::lock_guard<epro::mutex> lock(mainGame->gMutex);
hosts.push_back(remote);
const auto is_compact_mode = packet.host.handshake != SERVER_HANDSHAKE;
int rule = packet.host.duel_rule;
auto GetRuleString = [&]()-> std::wstring {
if(!is_compact_mode) {
auto duel_flag = (((uint64_t)packet.host.duel_flag_low) | ((uint64_t)packet.host.duel_flag_high) << 32);
mainGame->GetMasterRule(duel_flag & ~(DUEL_RELAY | DUEL_TCG_SEGOC_NONPUBLIC | DUEL_PSEUDO_SHUFFLE), packet.host.forbiddentypes, &rule);
if(rule == 6) {
if(duel_flag == DUEL_MODE_GOAT)
return L"GOAT";
if(duel_flag == DUEL_MODE_RUSH)
return L"Rush";
if(duel_flag == DUEL_MODE_SPEED)
return L"Speed";
return L"Custom MR";
}
}
return epro::format(L"MR {}", (rule == 0) ? 3 : rule);
};
auto GetIsCustom = [&packet,&rule, is_compact_mode] {
static constexpr DeckSizes normal_sizes{ {40,60}, {0,15}, {0,15} };
if(packet.host.draw_count == 1 && packet.host.start_hand == 5 && packet.host.start_lp == 8000
&& !packet.host.no_check_deck_content && !packet.host.no_shuffle_deck
&& (packet.host.duel_flag_low & DUEL_PSEUDO_SHUFFLE) == 0
&& rule == DEFAULT_DUEL_RULE && packet.host.extra_rules == 0
&& (is_compact_mode || (packet.host.version.client.major < 40) || packet.host.sizes == normal_sizes))
return gDataManager->GetSysString(1280);
return gDataManager->GetSysString(1281);
};
auto FormatVersion = [&packet, is_compact_mode] {
if(is_compact_mode)
return epro::format(L"Fluo: {:X}.0{:X}.{:X}", packet.version >> 12, (packet.version >> 4) & 0xff, packet.version & 0xf);
const auto& version = packet.host.version;
return epro::format(L"{}.{}", version.client.major, version.client.minor);
};
wchar_t gamename[20];
BufferIO::DecodeUTF16(packet.name, gamename, 20);
auto hoststr = epro::format(L"[{}][{}][{}][{}][{}][{}]{}",
gdeckManager->GetLFListName(packet.host.lflist),
gDataManager->GetSysString(packet.host.rule + 1900),
gDataManager->GetSysString(packet.host.mode + 1244),
FormatVersion(),
GetRuleString(),
GetIsCustom(),
gamename);
mainGame->lstHostList->addItem(hoststr.data());
}
}
}
void DuelClient::ReplayPrompt(bool local_stream) {
if(local_stream) {
auto replay_header = ExtendedReplayHeader::CreateDefaultHeader(REPLAY_YRPX, static_cast<uint32_t>(time(0)));
if(mainGame->dInfo.compat_mode)
replay_header.base.flag &= ~REPLAY_LUA64;
last_replay.BeginRecord(false);
last_replay.WriteHeader(replay_header);
last_replay.Write<uint32_t>(static_cast<uint32_t>(mainGame->dInfo.selfnames.size()), false);
for(auto& name : mainGame->dInfo.selfnames) {
last_replay.WriteData(name.data(), 40, false);
}
last_replay.Write<uint32_t>(static_cast<uint32_t>(mainGame->dInfo.opponames.size()), false);
for(auto& name : mainGame->dInfo.opponames) {
last_replay.WriteData(name.data(), 40, false);
}
last_replay.Write<uint64_t>(mainGame->dInfo.duel_params);
last_replay.WriteStream(replay_stream);
last_replay.EndRecord();
}
replay_stream.clear();
std::unique_lock<epro::mutex> lock(mainGame->gMutex);
mainGame->wPhase->setVisible(false);
if(mainGame->dInfo.player_type < 7)
mainGame->btnLeaveGame->setVisible(false);
mainGame->btnChainIgnore->setVisible(false);
mainGame->btnChainAlways->setVisible(false);
mainGame->btnChainWhenAvail->setVisible(false);
mainGame->btnCancelOrFinish->setVisible(false);
auto now = std::time(nullptr);
mainGame->PopupSaveWindow(gDataManager->GetSysString(1340), epro::format(L"{:%Y-%m-%d %H-%M-%S}", epro::localtime(now)), gDataManager->GetSysString(1342));
mainGame->replaySignal.Wait(lock);
if(mainGame->saveReplay || !is_local_host) {
if(mainGame->saveReplay)
last_replay.SaveReplay(Utils::ToPathString(mainGame->ebFileSaveName->getText()));
else last_replay.SaveReplay(EPRO_TEXT("_LastReplay"));
}
}
}