mirror of
https://github.com/edo9300/edopro
synced 2026-08-23 22:26:05 -04:00
Implement a stateful Duel object wrapping the udnerlying core api automatically managing its lifetime and simplifying calling the various OCG_ functions
300 lines
10 KiB
C++
300 lines
10 KiB
C++
#include "replay_mode.h"
|
|
#include "RNG/mt19937.h"
|
|
#include "duelclient.h"
|
|
#include "game.h"
|
|
#include "single_mode.h"
|
|
#include "fmt.h"
|
|
|
|
namespace ygo {
|
|
bool ReplayMode::ReadReplayResponse() {
|
|
ReplayResponse* res;
|
|
bool result = cur_yrp->GetNextResponse(res);
|
|
if (result)
|
|
pduel->DuelSetResponse(res->response.data(), res->length);
|
|
return result;
|
|
}
|
|
int ReplayMode::OldReplayThread() {
|
|
Utils::SetThreadName("OldReplay");
|
|
mainGame->dInfo.isReplay = true;
|
|
mainGame->dInfo.isOldReplay = true;
|
|
if(!cur_yrp) {
|
|
EndDuel();
|
|
return 0;
|
|
}
|
|
const auto& replay_header = cur_yrp->pheader;
|
|
mainGame->dInfo.isFirst = true;
|
|
mainGame->dInfo.isTeam1 = true;
|
|
mainGame->dInfo.isRelay = !!(cur_yrp->params.duel_flags & DUEL_RELAY);
|
|
mainGame->dInfo.isSingleMode = !!(replay_header.base.flag & REPLAY_SINGLE_MODE);
|
|
mainGame->dInfo.isHandTest = !!(replay_header.base.flag & REPLAY_HAND_TEST);
|
|
mainGame->dInfo.compat_mode = false;
|
|
mainGame->dInfo.legacy_race_size = false;
|
|
mainGame->dInfo.current_player[0] = 0;
|
|
mainGame->dInfo.current_player[1] = 0;
|
|
mainGame->dInfo.opponames.clear();
|
|
mainGame->dInfo.selfnames.clear();
|
|
mainGame->dInfo.team1 = ReplayMode::cur_yrp->GetPlayersCount(0);
|
|
mainGame->dInfo.team2 = ReplayMode::cur_yrp->GetPlayersCount(1);
|
|
if(!mainGame->dInfo.isRelay)
|
|
mainGame->dInfo.current_player[1] = mainGame->dInfo.team2 - 1;
|
|
if (!StartDuel()) {
|
|
EndDuel();
|
|
return 0;
|
|
}
|
|
mainGame->dInfo.isInDuel = true;
|
|
mainGame->dInfo.isStarted = true;
|
|
mainGame->dInfo.isOldReplay = true;
|
|
mainGame->dInfo.checkRematch = false;
|
|
mainGame->SetMessageWindow();
|
|
mainGame->dInfo.isCatchingUp = (skip_turn > 0);
|
|
is_continuing = true;
|
|
skip_step = 0;
|
|
if (mainGame->dInfo.isSingleMode) {
|
|
for(const auto& message : CoreUtils::ParseMessages(pduel.get()))
|
|
is_continuing = OldReplayAnalyze(message) && is_continuing;
|
|
} else {
|
|
ReplayRefresh(0, LOCATION_DECK, 0x2181fff);
|
|
ReplayRefresh(1, LOCATION_DECK, 0x2181fff);
|
|
ReplayRefresh(0, LOCATION_EXTRA, 0x2181fff);
|
|
ReplayRefresh(1, LOCATION_EXTRA, 0x2181fff);
|
|
}
|
|
exit_pending = false;
|
|
current_step = 0;
|
|
if (mainGame->dInfo.isCatchingUp)
|
|
mainGame->gMutex.lock();
|
|
while(is_continuing && !exit_pending) {
|
|
/*int engFlag = */pduel->DuelProcess();
|
|
for(const auto& message : CoreUtils::ParseMessages(pduel.get())) {
|
|
if(is_restarting || !is_continuing)
|
|
break;
|
|
is_continuing = OldReplayAnalyze(message) && is_continuing;
|
|
}
|
|
if(is_restarting) {
|
|
mainGame->gMutex.lock();
|
|
is_restarting = false;
|
|
int step = current_step - 1;
|
|
if(step < 0)
|
|
step = 0;
|
|
if(mainGame->dInfo.isSingleMode) {
|
|
is_continuing = true;
|
|
skip_step = 0;
|
|
for(const auto& message : CoreUtils::ParseMessages(pduel.get())) {
|
|
if(is_restarting || !is_continuing)
|
|
break;
|
|
is_continuing = OldReplayAnalyze(message) && is_continuing;
|
|
}
|
|
}
|
|
if(step == 0) {
|
|
Pause(true, false);
|
|
mainGame->dInfo.isInDuel = true;
|
|
mainGame->dInfo.isStarted = true;
|
|
mainGame->dInfo.checkRematch = false;
|
|
mainGame->dInfo.isCatchingUp = false;
|
|
mainGame->dField.RefreshAllCards();
|
|
mainGame->gMutex.unlock();
|
|
}
|
|
skip_step = step;
|
|
current_step = 0;
|
|
}
|
|
}
|
|
if (mainGame->dInfo.isCatchingUp) {
|
|
mainGame->dInfo.isCatchingUp = false;
|
|
mainGame->dField.RefreshAllCards();
|
|
mainGame->gMutex.unlock();
|
|
}
|
|
EndDuel();
|
|
return 0;
|
|
}
|
|
bool ReplayMode::StartDuel() {
|
|
const auto& replay_header = cur_yrp->pheader;
|
|
const auto& seed = replay_header.seed;
|
|
const auto& names = cur_yrp->GetPlayerNames();
|
|
const auto first_oppo_player = names.begin() + cur_yrp->GetPlayersCount(0);
|
|
mainGame->dInfo.selfnames.assign(names.begin(), first_oppo_player);
|
|
mainGame->dInfo.opponames.assign(first_oppo_player, names.end());
|
|
uint32_t start_lp = cur_yrp->params.start_lp;
|
|
uint32_t start_hand = cur_yrp->params.start_hand;
|
|
uint32_t draw_count = cur_yrp->params.draw_count;
|
|
OCG_Player team = { start_lp, start_hand, draw_count };
|
|
pduel = mainGame->SetupDuel({ { seed[0], seed[1], seed[2], seed[3] }, cur_yrp->params.duel_flags, team, team });
|
|
mainGame->dInfo.duel_params = cur_yrp->params.duel_flags;
|
|
mainGame->dInfo.duel_field = mainGame->GetMasterRule(mainGame->dInfo.duel_params);
|
|
matManager.SetActiveVertices(mainGame->dInfo.HasFieldFlag(DUEL_3_COLUMNS_FIELD),
|
|
!mainGame->dInfo.HasFieldFlag(DUEL_SEPARATE_PZONE));
|
|
mainGame->SetPhaseButtons();
|
|
mainGame->dInfo.lp[0] = start_lp;
|
|
mainGame->dInfo.lp[1] = start_lp;
|
|
mainGame->dInfo.startlp = start_lp;
|
|
mainGame->dInfo.strLP[0] = epro::to_wstring(start_lp);
|
|
mainGame->dInfo.strLP[1] = mainGame->dInfo.strLP[0];
|
|
mainGame->dInfo.turn = 0;
|
|
if (!mainGame->dInfo.isSingleMode || (replay_header.base.flag & REPLAY_HAND_TEST)) {
|
|
const auto& rule_cards = cur_yrp->GetRuleCards();
|
|
OCG_NewCardInfo card_info = { 0, 0, 0, 0, 0, 0, POS_FACEDOWN_DEFENSE };
|
|
for(auto card : rule_cards) {
|
|
card_info.code = card;
|
|
pduel->DuelNewCard(&card_info);
|
|
}
|
|
const auto& decks = cur_yrp->GetPlayerDecks();
|
|
for(int i = 0; i < mainGame->dInfo.team1; i++) {
|
|
card_info.duelist = i;
|
|
card_info.loc = LOCATION_DECK;
|
|
for(auto card : decks[i].main_deck) {
|
|
card_info.code = card;
|
|
pduel->DuelNewCard(&card_info);
|
|
}
|
|
card_info.loc = LOCATION_EXTRA;
|
|
for(auto card : decks[i].extra_deck) {
|
|
card_info.code = card;
|
|
pduel->DuelNewCard(&card_info);
|
|
}
|
|
}
|
|
card_info.team = 1;
|
|
card_info.con = 1;
|
|
for(int i = 0; i < mainGame->dInfo.team2; i++) {
|
|
card_info.duelist = i;
|
|
card_info.loc = LOCATION_DECK;
|
|
for(auto card : decks[i + mainGame->dInfo.team1].main_deck) {
|
|
card_info.code = card;
|
|
pduel->DuelNewCard(&card_info);
|
|
}
|
|
card_info.loc = LOCATION_EXTRA;
|
|
for(auto card : decks[i + mainGame->dInfo.team1].extra_deck) {
|
|
card_info.code = card;
|
|
pduel->DuelNewCard(&card_info);
|
|
}
|
|
}
|
|
if(replay_header.base.flag & REPLAY_HAND_TEST) {
|
|
const char cmd[] = "Debug.ReloadFieldEnd()";
|
|
pduel->LoadScript(cmd, sizeof(cmd) - 1, " ");
|
|
} else {
|
|
mainGame->dField.Initial(0, static_cast<uint32_t>(decks[0].main_deck.size()), static_cast<uint32_t>(decks[0].extra_deck.size()));
|
|
mainGame->dField.Initial(1, static_cast<uint32_t>(decks[mainGame->dInfo.team1].main_deck.size()), static_cast<uint32_t>(decks[mainGame->dInfo.team1].extra_deck.size()));
|
|
}
|
|
} else {
|
|
if(!mainGame->LoadScript(pduel.get(), cur_yrp->scriptname))
|
|
return false;
|
|
}
|
|
pduel->StartDuel();
|
|
return true;
|
|
}
|
|
|
|
bool ReplayMode::OldReplayAnalyze(const CoreUtils::Packet& packet) {
|
|
if(packet.message == MSG_SELECT_BATTLECMD || packet.message == MSG_SELECT_IDLECMD || packet.message == MSG_SELECT_CHAIN)
|
|
ReplayRefresh();
|
|
switch(packet.message) {
|
|
case MSG_SELECT_BATTLECMD:
|
|
case MSG_SELECT_IDLECMD:
|
|
case MSG_SELECT_EFFECTYN:
|
|
case MSG_SELECT_YESNO:
|
|
case MSG_SELECT_OPTION:
|
|
case MSG_SELECT_CARD:
|
|
case MSG_SELECT_TRIBUTE:
|
|
case MSG_SELECT_UNSELECT_CARD:
|
|
case MSG_SELECT_CHAIN:
|
|
case MSG_SELECT_PLACE:
|
|
case MSG_SELECT_DISFIELD:
|
|
case MSG_SELECT_POSITION:
|
|
case MSG_SELECT_COUNTER:
|
|
case MSG_SELECT_SUM:
|
|
case MSG_SORT_CARD:
|
|
case MSG_SORT_CHAIN:
|
|
case MSG_ROCK_PAPER_SCISSORS:
|
|
case MSG_ANNOUNCE_RACE:
|
|
case MSG_ANNOUNCE_ATTRIB:
|
|
case MSG_ANNOUNCE_CARD:
|
|
case MSG_ANNOUNCE_NUMBER: {
|
|
return ReadReplayResponse();
|
|
}
|
|
}
|
|
if(!ReplayAnalyze(packet))
|
|
return false;
|
|
const auto* pbuf = packet.data();
|
|
int player;
|
|
switch(mainGame->dInfo.curMsg) {
|
|
case MSG_SHUFFLE_DECK: {
|
|
player = BufferIO::Read<uint8_t>(pbuf);
|
|
ReplayRefresh(player, LOCATION_DECK, 0x2181fff);
|
|
break;
|
|
}
|
|
case MSG_SWAP_GRAVE_DECK: {
|
|
player = BufferIO::Read<uint8_t>(pbuf);
|
|
ReplayRefresh(player, LOCATION_GRAVE, 0x2181fff);
|
|
break;
|
|
}
|
|
case MSG_REVERSE_DECK: {
|
|
ReplayRefresh(0, LOCATION_DECK, 0x2181fff);
|
|
ReplayRefresh(1, LOCATION_DECK, 0x2181fff);
|
|
break;
|
|
}
|
|
case MSG_MOVE: {
|
|
pbuf += 4;
|
|
auto previous = CoreUtils::ReadLocInfo(pbuf, false);
|
|
auto current = CoreUtils::ReadLocInfo(pbuf, false);
|
|
if(previous.location && !(current.location & 0x80) && (previous.location != current.location || previous.controler != current.controler))
|
|
ReplayRefreshSingle(current.controler, current.location, current.sequence);
|
|
break;
|
|
}
|
|
case MSG_TAG_SWAP: {
|
|
player = BufferIO::Read<uint8_t>(pbuf);
|
|
ReplayRefresh(player, LOCATION_DECK, 0x2181fff);
|
|
ReplayRefresh(player, LOCATION_EXTRA, 0x2181fff);
|
|
break;
|
|
}
|
|
case MSG_NEW_PHASE:
|
|
case MSG_SUMMONED:
|
|
case MSG_SPSUMMONED:
|
|
case MSG_FLIPSUMMONED:
|
|
case MSG_CHAINED:
|
|
case MSG_CHAIN_SOLVED:
|
|
case MSG_DAMAGE_STEP_START:
|
|
case MSG_DAMAGE_STEP_END: {
|
|
ReplayRefresh();
|
|
break;
|
|
}
|
|
case MSG_CHAIN_END: {
|
|
ReplayRefresh();
|
|
ReplayRefresh(0, LOCATION_DECK);
|
|
ReplayRefresh(1, LOCATION_DECK);
|
|
break;
|
|
}
|
|
case MSG_RELOAD_FIELD: {
|
|
ReplayReload();
|
|
if(!mainGame->dInfo.isCatchingUp)
|
|
mainGame->gMutex.lock();
|
|
mainGame->dField.RefreshAllCards();
|
|
if(!mainGame->dInfo.isCatchingUp)
|
|
mainGame->gMutex.unlock();
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
void ReplayMode::ReplayRefresh(uint8_t player, uint8_t location, uint32_t flag) {
|
|
uint32_t len = 0;
|
|
OCG_QueryInfo info{ flag, player, location };
|
|
auto buff = static_cast<uint8_t*>(pduel->DuelQueryLocation(&len, &info));
|
|
if(len == 0)
|
|
return;
|
|
mainGame->dField.UpdateFieldCard(mainGame->LocalPlayer(player), location, buff);
|
|
}
|
|
void ReplayMode::ReplayRefresh(uint32_t flag) {
|
|
for(int p = 0; p < 2; p++)
|
|
for(int loc = LOCATION_HAND; loc != LOCATION_GRAVE; loc *= 2)
|
|
ReplayRefresh(p, loc, flag);
|
|
}
|
|
void ReplayMode::ReplayRefreshSingle(uint8_t player, uint8_t location, uint32_t sequence, uint32_t flag) {
|
|
uint32_t len = 0;
|
|
OCG_QueryInfo info{ flag, player, location, sequence };
|
|
auto buff = static_cast<uint8_t*>(pduel->DuelQuery(&len, &info));
|
|
if(buff == nullptr)
|
|
return;
|
|
mainGame->dField.UpdateCard(mainGame->LocalPlayer(player), location, sequence, buff);
|
|
}
|
|
void ReplayMode::ReplayReload() {
|
|
for(int p = 0; p < 2; p++)
|
|
for(int loc = LOCATION_DECK; loc != LOCATION_OVERLAY; loc <<= 1)
|
|
ReplayRefresh(p, loc, 0x2ffdfff);
|
|
}
|
|
|
|
}
|