diff --git a/gframe/data_manager.cpp b/gframe/data_manager.cpp index 9407164c4..8005c0249 100644 --- a/gframe/data_manager.cpp +++ b/gframe/data_manager.cpp @@ -9,6 +9,9 @@ #include "common.h" #include "file_stream.h" #include "fmt.h" +#include "game.h" +#include "deck_manager.h" +#include "deck_con.h" #if !defined(SQLITE_NOTICE) #define SQLITE_NOTICE 27 @@ -660,5 +663,22 @@ bool DataManager::deck_sort_name(const CardDataC* p1, const CardDataC* p2) { return res < 0; return check_codes(p1, p2); } +bool DataManager::deck_sort_genesys(const CardDataC* p1, const CardDataC* p2) { + if (check_either_skills(p1->type, p2->type)) + return check_skills(p1, p2); + int32_t p1_points = 0; + int32_t p2_points = 0; + if (mainGame->deckBuilder.filterList) { + auto it1 = mainGame->deckBuilder.filterList->GetLimitationIterator(p1); + if (it1 != mainGame->deckBuilder.filterList->content.end()) + p1_points = it1->second; + auto it2 = mainGame->deckBuilder.filterList->GetLimitationIterator(p2); + if (it2 != mainGame->deckBuilder.filterList->content.end()) + p2_points = it2->second; + } + if (p1_points != p2_points) + return p1_points < p2_points; + return deck_sort_lv(p1, p2); +} } diff --git a/gframe/data_manager.h b/gframe/data_manager.h index 24bbfa9fc..a19c030f7 100644 --- a/gframe/data_manager.h +++ b/gframe/data_manager.h @@ -184,6 +184,7 @@ class DataManager { static bool deck_sort_atk(const CardDataC* l1, const CardDataC* l2); static bool deck_sort_def(const CardDataC* l1, const CardDataC* l2); static bool deck_sort_name(const CardDataC* l1, const CardDataC* l2); + static bool deck_sort_genesys(const CardDataC* l1, const CardDataC* l2); private: std::unique_ptr irrvfs; template diff --git a/gframe/deck_con.cpp b/gframe/deck_con.cpp index 1c061043b..59c44a778 100644 --- a/gframe/deck_con.cpp +++ b/gframe/deck_con.cpp @@ -88,6 +88,7 @@ void DeckBuilder::Initialize(bool refresh) { is_draging = false; prev_deck = mainGame->cbDBDecks->getSelected(); prev_operation = 0; + RefreshLimitationStatus(); mainGame->SetMessageWindow(); mainGame->device->setEventReceiver(this); } @@ -439,7 +440,18 @@ bool DeckBuilder::OnEvent(const irr::SEvent& event) { break; } case BUTTON_MARKS_FILTER: { - mainGame->PopupElement(mainGame->wLinkMarks); + if (filterList && filterList->genesys) + mainGame->PopupElement(mainGame->wGenesys); + else + mainGame->PopupElement(mainGame->wLinkMarks); + break; + } + case BUTTON_GENESYS_OK: { + filter_genesys_op = mainGame->cbGenesysOp->getSelected(); + filter_genesys_val1 = _wtoi(mainGame->ebGenesys1->getText()); + filter_genesys_val2 = _wtoi(mainGame->ebGenesys2->getText()); + mainGame->HideElement(mainGame->wGenesys); + StartFilter(true); break; } case BUTTON_MARKERS_OK: { @@ -511,9 +523,19 @@ bool DeckBuilder::OnEvent(const irr::SEvent& event) { case COMBOBOX_DBLFLIST: { filterList = &gdeckManager->_lfList[mainGame->cbDBLFList->getSelected()]; mainGame->ReloadCBLimit(); + RefreshLimitationStatus(); StartFilter(true); break; } + case COMBOBOX_GENESYS_OP: { + if (mainGame->cbGenesysOp->getSelected() == 3) { // Between + mainGame->ebGenesys2->setVisible(true); + } else { + mainGame->ebGenesys2->setVisible(false); + mainGame->ebGenesys2->setText(L""); + } + break; + } case COMBOBOX_DBDECKS: { int sel = mainGame->cbDBDecks->getSelected(); if(sel >= 0) @@ -1033,6 +1055,9 @@ bool DeckBuilder::FiltersChanged() { CHECK_AND_SET(filter_scl); CHECK_AND_SET(filter_marks); CHECK_AND_SET(filter_lm); + CHECK_AND_SET(filter_genesys_op); + CHECK_AND_SET(filter_genesys_val1); + CHECK_AND_SET(filter_genesys_val2); return res; } #undef CHECK_AND_SET @@ -1170,7 +1195,9 @@ void DeckBuilder::FilterCards(bool force_refresh) { mainGame->scrFilter->setPos(0); } bool DeckBuilder::CheckCardProperties(const CardDataM& data) { - if(data._data.type & TYPE_TOKEN || data._data.ot & SCOPE_HIDDEN || ((data._data.ot & SCOPE_OFFICIAL) != data._data.ot && (!mainGame->chkAnime->isChecked() && !filterList->whitelist))) + if(data._data.type & TYPE_TOKEN || data._data.ot & SCOPE_HIDDEN || ((data._data.ot & SCOPE_OFFICIAL) != data._data.ot && (!mainGame->chkAnime->isChecked() && !filterList->whitelist && !filterList->genesys))) + return false; + if (filterList->genesys && (!(data._data.ot & SCOPE_TCG) || (data._data.type & (TYPE_PENDULUM | TYPE_LINK)))) return false; switch(filter_type) { case 1: { @@ -1232,7 +1259,25 @@ bool DeckBuilder::CheckCardProperties(const CardDataM& data) { return false; if(filter_marks && (data._data.link_marker & filter_marks) != filter_marks) return false; - if((filter_lm != LIMITATION_FILTER_NONE || filterList->whitelist) && filter_lm != LIMITATION_FILTER_ALL) { + if (filterList->genesys && filter_genesys_op != 0xFFFFFFFF) { + auto it = filterList->GetLimitationIterator(&data._data); + int points = (it == filterList->content.end()) ? 0 : it->second; + switch (filter_genesys_op) { + case 0: // >= + if (points < filter_genesys_val1) return false; + break; + case 1: // <= + if (points > filter_genesys_val1) return false; + break; + case 2: // == + if (points != filter_genesys_val1) return false; + break; + case 3: // Between + if (points < filter_genesys_val1 || points > filter_genesys_val2) return false; + break; + } + } + if((filter_lm != LIMITATION_FILTER_NONE || filterList->whitelist || filterList->genesys) && filter_lm != LIMITATION_FILTER_ALL) { auto flit = filterList->GetLimitationIterator(&data._data); int count = 3; if(flit == filterList->content.end()) { @@ -1240,17 +1285,39 @@ bool DeckBuilder::CheckCardProperties(const CardDataM& data) { count = -1; } else count = flit->second; - switch(filter_lm) { - case LIMITATION_FILTER_BANNED: - case LIMITATION_FILTER_LIMITED: - case LIMITATION_FILTER_SEMI_LIMITED: - if(count != filter_lm - 1) - return false; - break; - case LIMITATION_FILTER_UNLIMITED: - if(count < 3) - return false; - break; + if (filterList->genesys) { + switch (filter_lm) { + case LIMITATION_FILTER_UNLIMITED: + if (count < 3 && flit != filterList->content.end()) + return false; + break; + case LIMITATION_FILTER_BANNED: + if (count != 1) + return false; + break; + case LIMITATION_FILTER_LIMITED: + if (count != 2) + return false; + break; + case LIMITATION_FILTER_SEMI_LIMITED: + if (count != 3) + return false; + break; + default: + break; + } + } else { + switch (filter_lm) { + case LIMITATION_FILTER_BANNED: + case LIMITATION_FILTER_LIMITED: + case LIMITATION_FILTER_SEMI_LIMITED: + if (count != filter_lm - 1) + return false; + break; + case LIMITATION_FILTER_UNLIMITED: + if (count < 3) + return false; + break; case LIMITATION_FILTER_OCG: if(data._data.ot != SCOPE_OCG) return false; @@ -1297,6 +1364,7 @@ bool DeckBuilder::CheckCardProperties(const CardDataM& data) { break; default: break; + } } if(filterList->whitelist && count < 0) return false; @@ -1372,6 +1440,13 @@ void DeckBuilder::ClearFilter() { filter_marks = 0; for(int i = 0; i < 8; i++) mainGame->btnMark[i]->setPressed(false); + filter_genesys_op = 0xFFFFFFFF; + filter_genesys_val1 = 0; + filter_genesys_val2 = 0; + mainGame->cbGenesysOp->setSelected(0); + mainGame->ebGenesys1->setText(L""); + mainGame->ebGenesys2->setText(L""); + mainGame->ebGenesys2->setVisible(false); } void DeckBuilder::SortList() { auto last = [&] { @@ -1401,6 +1476,9 @@ void DeckBuilder::SortList() { case 3: sort(DataManager::deck_sort_name); break; + case 4: + sort(DataManager::deck_sort_genesys); + break; } } void DeckBuilder::ClearDeck() { @@ -1425,6 +1503,7 @@ void DeckBuilder::ClearDeck() { side_monster_count = 0; side_spell_count = 0; side_trap_count = 0; + genesys_points = 0; } void DeckBuilder::RefreshLimitationStatus() { main_and_extra_legend_count_monster = DeckManager::CountLegends(current_deck.main, TYPE_MONSTER) + DeckManager::CountLegends(current_deck.extra, TYPE_MONSTER); @@ -1444,6 +1523,28 @@ void DeckBuilder::RefreshLimitationStatus() { side_monster_count = DeckManager::TypeCount(current_deck.side, TYPE_MONSTER); side_spell_count = DeckManager::TypeCount(current_deck.side, TYPE_SPELL); side_trap_count = DeckManager::TypeCount(current_deck.side, TYPE_TRAP); + genesys_points = 0; + if (filterList->genesys) { + for (auto* card : current_deck.main) { + auto it = filterList->GetLimitationIterator(card); + if (it != filterList->content.end()) + genesys_points += it->second; + } + for (auto* card : current_deck.extra) { + auto it = filterList->GetLimitationIterator(card); + if (it != filterList->content.end()) + genesys_points += it->second; + } + for (auto* card : current_deck.side) { + auto it = filterList->GetLimitationIterator(card); + if (it != filterList->content.end()) + genesys_points += it->second; + } + } + if (filterList->genesys) + mainGame->btnMarksFilter->setText(gDataManager->GetSysString(12504).data()); + else + mainGame->btnMarksFilter->setText(gDataManager->GetSysString(1374).data()); } void DeckBuilder::RefreshLimitationStatusOnRemoved(const CardDataC* card, DeckType location) { switch(location) { @@ -1466,6 +1567,11 @@ void DeckBuilder::RefreshLimitationStatusOnRemoved(const CardDataC* card, DeckTy } if(card->type & TYPE_SKILL) --main_skill_count; + if (filterList->genesys) { + auto it = filterList->GetLimitationIterator(card); + if (it != filterList->content.end()) + genesys_points -= it->second; + } break; } case DeckType::EXTRA: @@ -1482,6 +1588,11 @@ void DeckBuilder::RefreshLimitationStatusOnRemoved(const CardDataC* card, DeckTy --extra_link_count; if(card->type & TYPE_RITUAL) --extra_rush_ritual_count; + if (filterList->genesys) { + auto it = filterList->GetLimitationIterator(card); + if (it != filterList->content.end()) + genesys_points -= it->second; + } break; } case DeckType::SIDE: @@ -1492,6 +1603,11 @@ void DeckBuilder::RefreshLimitationStatusOnRemoved(const CardDataC* card, DeckTy --side_spell_count; if(card->type & TYPE_TRAP) --side_trap_count; + if (filterList->genesys) { + auto it = filterList->GetLimitationIterator(card); + if (it != filterList->content.end()) + genesys_points -= it->second; + } break; } } @@ -1517,6 +1633,11 @@ void DeckBuilder::RefreshLimitationStatusOnAdded(const CardDataC* card, DeckType } if(card->type & TYPE_SKILL) ++main_skill_count; + if (filterList->genesys) { + auto it = filterList->GetLimitationIterator(card); + if (it != filterList->content.end()) + genesys_points += it->second; + } break; } case DeckType::EXTRA: @@ -1533,6 +1654,11 @@ void DeckBuilder::RefreshLimitationStatusOnAdded(const CardDataC* card, DeckType ++extra_link_count; if(card->type & TYPE_RITUAL) ++extra_rush_ritual_count; + if (filterList->genesys) { + auto it = filterList->GetLimitationIterator(card); + if (it != filterList->content.end()) + genesys_points += it->second; + } break; } case DeckType::SIDE: @@ -1543,6 +1669,11 @@ void DeckBuilder::RefreshLimitationStatusOnAdded(const CardDataC* card, DeckType ++side_spell_count; if(card->type & TYPE_TRAP) ++side_trap_count; + if (filterList->genesys) { + auto it = filterList->GetLimitationIterator(card); + if (it != filterList->content.end()) + genesys_points += it->second; + } break; } } @@ -1647,9 +1778,11 @@ bool DeckBuilder::check_limit(const CardDataC* pointer) { uint32_t limitcode = pointer->alias ? pointer->alias : pointer->code; int found = 0; int limit = filterList->whitelist ? 0 : 3; + if (filterList->genesys) + limit = 3; auto endit = filterList->content.end(); auto it = filterList->GetLimitationIterator(pointer); - if(it != endit) + if(it != endit && !filterList->genesys) limit = it->second; if(limit == 0) return false; @@ -1657,10 +1790,12 @@ bool DeckBuilder::check_limit(const CardDataC* pointer) { for(auto* plist : { &deck.main, &deck.extra, &deck.side }) { for(auto& pcard : *plist) { if(pcard->code == limitcode || pcard->alias == limitcode) { - if((it = filterList->content.find(pcard->code)) != endit) - limit = std::min(limit, it->second); - else if((it = filterList->content.find(pcard->alias)) != endit) - limit = std::min(limit, it->second); + if(!filterList->genesys) { + if((it = filterList->content.find(pcard->code)) != endit) + limit = std::min(limit, it->second); + else if((it = filterList->content.find(pcard->alias)) != endit) + limit = std::min(limit, it->second); + } found++; } if(limit <= found) diff --git a/gframe/deck_con.h b/gframe/deck_con.h index 9268ae8fd..8f687baee 100644 --- a/gframe/deck_con.h +++ b/gframe/deck_con.h @@ -106,6 +106,9 @@ class DeckBuilder final : public irr::IEventReceiver { DECLARE_WITH_CACHE(uint32_t, filter_scl) DECLARE_WITH_CACHE(uint32_t, filter_marks) DECLARE_WITH_CACHE(limitation_search_filters, filter_lm) + DECLARE_WITH_CACHE(uint32_t, filter_genesys_op) + DECLARE_WITH_CACHE(int32_t, filter_genesys_val1) + DECLARE_WITH_CACHE(int32_t, filter_genesys_val2) #undef DECLARE_WITH_CACHE irr::core::vector2di mouse_pos; @@ -142,6 +145,7 @@ class DeckBuilder final : public irr::IEventReceiver { uint16_t side_monster_count; uint16_t side_spell_count; uint16_t side_trap_count; + int32_t genesys_points; LFList* filterList; std::map, std::less<>> searched_terms; std::vector results; diff --git a/gframe/deck_manager.cpp b/gframe/deck_manager.cpp index 09245d1ca..9d65fb9f1 100644 --- a/gframe/deck_manager.cpp +++ b/gframe/deck_manager.cpp @@ -34,6 +34,7 @@ void DeckManager::ClearDummies() { } bool DeckManager::LoadLFListSingle(const epro::path_string& path) { static constexpr auto key = "$whitelist"sv; + static constexpr auto genesys_key = "$genesys"sv; FileStream infile{ path, FileStream::in }; if(infile.fail()) return false; @@ -56,6 +57,7 @@ bool DeckManager::LoadLFListSingle(const epro::path_string& path) { lflist.content.clear(); lflist.hash = 0x7dfcee6a; lflist.whitelist = false; + lflist.genesys = false; loaded = true; continue; } @@ -63,6 +65,10 @@ bool DeckManager::LoadLFListSingle(const epro::path_string& path) { lflist.whitelist = true; continue; } + if(str.rfind(genesys_key.data(), 0, genesys_key.size()) == 0) { + lflist.genesys = true; + continue; + } if(!lflist.hash) continue; auto p = str.find(' '); @@ -103,6 +109,7 @@ void DeckManager::LoadLFList() { nolimit.hash = 0; nolimit.content.clear(); nolimit.whitelist = false; + nolimit.genesys = false; _lfList.push_back(nolimit); null_lflist_index = _lfList.size() - 1; } @@ -157,6 +164,7 @@ int DeckManager::CountLegends(const Deck::Vector& cards, uint32_t type) { static DeckError CheckCards(const Deck::Vector& cards, LFList const* curlist, DuelAllowedCards allowedCards, banlist_content_t& ccount, + uint16_t& total_points, std::function additionalCheck = nullptr) { DeckError ret{ DeckError::NONE }; for (const auto cit : cards) { @@ -194,14 +202,20 @@ static DeckError CheckCards(const Deck::Vector& cards, LFList const* curlist, int dc = ccount[code]; if (dc > 3) return ret.type = DeckError::CARDCOUNT, ret; - auto it = curlist->GetLimitationIterator(cit); - auto is_end = it == curlist->content.end(); - if ((!is_end && dc > it->second) || (curlist->whitelist && is_end)) - return ret.type = DeckError::LFLIST, ret; + if (curlist->genesys) { + auto it = curlist->GetLimitationIterator(cit); + if (it != curlist->content.end()) + total_points += it->second; + } else { + auto it = curlist->GetLimitationIterator(cit); + auto is_end = it == curlist->content.end(); + if ((!is_end && dc > it->second) || (curlist->whitelist && is_end)) + return ret.type = DeckError::LFLIST, ret; + } } return { DeckError::NONE }; } -DeckError DeckManager::CheckDeckContent(const Deck& deck, LFList const* lflist, DuelAllowedCards allowedCards, uint32_t forbiddentypes, bool rituals_in_extra) { +DeckError DeckManager::CheckDeckContent(const Deck& deck, LFList const* lflist, DuelAllowedCards allowedCards, uint32_t forbiddentypes, bool rituals_in_extra, uint16_t points_limit) { DeckError ret{ DeckError::NONE }; if(TypeCount(deck.main, forbiddentypes) > 0 || TypeCount(deck.extra, forbiddentypes) > 0 || TypeCount(deck.side, forbiddentypes) > 0) return ret.type = DeckError::FORBTYPE, ret; @@ -214,9 +228,10 @@ DeckError DeckManager::CheckDeckContent(const Deck& deck, LFList const* lflist, if(TypeCount(deck.main, TYPE_SKILL) > 1) return ret.type = DeckError::TOOMANYSKILLS, ret; banlist_content_t ccount; + uint16_t total_points = 0; if(!lflist) return ret; - ret = CheckCards(deck.main, lflist, allowedCards, ccount, [&](const CardDataC* cit)->DeckError { + ret = CheckCards(deck.main, lflist, allowedCards, ccount, total_points, [&](const CardDataC* cit)->DeckError { if ((cit->type & (TYPE_FUSION | TYPE_SYNCHRO | TYPE_XYZ)) || (cit->type & TYPE_LINK && cit->type & TYPE_MONSTER)) return { DeckError::EXTRACOUNT }; if(cit->isRitualMonster() && rituals_in_extra) @@ -224,7 +239,7 @@ DeckError DeckManager::CheckDeckContent(const Deck& deck, LFList const* lflist, return { DeckError::NONE }; }); if (ret.type) return ret; - ret = CheckCards(deck.extra, lflist, allowedCards, ccount, [&](const CardDataC* cit)->DeckError { + ret = CheckCards(deck.extra, lflist, allowedCards, ccount, total_points, [&](const CardDataC* cit)->DeckError { if(cit->isRitualMonster()) { if(!rituals_in_extra) return { DeckError::EXTRACOUNT }; @@ -233,7 +248,15 @@ DeckError DeckManager::CheckDeckContent(const Deck& deck, LFList const* lflist, return { DeckError::NONE }; }); if (ret.type) return ret; - return CheckCards(deck.side, lflist, allowedCards, ccount); + ret = CheckCards(deck.side, lflist, allowedCards, ccount, total_points); + if (ret.type) return ret; + if (lflist->genesys && total_points > points_limit) { + ret.type = DeckError::POINTS; + ret.count.current = total_points; + ret.count.maximum = points_limit; + return ret; + } + return ret; } DeckError DeckManager::CheckDeckSize(const Deck& deck, const DeckSizes& sizes) { DeckError ret{ DeckError::NONE }; diff --git a/gframe/deck_manager.h b/gframe/deck_manager.h index 49bd0daed..7380adaa2 100644 --- a/gframe/deck_manager.h +++ b/gframe/deck_manager.h @@ -20,6 +20,7 @@ struct LFList { std::wstring listName; banlist_content_t content; bool whitelist; + bool genesys; auto GetLimitationIterator(const CardDataC* pcard) const { auto flit = content.find(pcard->code); if(flit == content.end() && pcard->alias) { @@ -70,7 +71,7 @@ class DeckManager { LFList const* GetLFList(uint32_t lfhash) const; epro::wstringview GetLFListName(uint32_t lfhash) const; static void RefreshDeck(Deck& deck); - static DeckError CheckDeckContent(const Deck& deck, LFList const* lflist, DuelAllowedCards allowedCards, uint32_t forbiddentypes, bool rituals_in_extra); + static DeckError CheckDeckContent(const Deck& deck, LFList const* lflist, DuelAllowedCards allowedCards, uint32_t forbiddentypes, bool rituals_in_extra, uint16_t points_limit = 0); static DeckError CheckDeckSize(const Deck& deck, const DeckSizes& sizes); static int TypeCount(const Deck::Vector& cards, uint32_t type); static int CountLegends(const Deck::Vector& cards, uint32_t type); diff --git a/gframe/drawing.cpp b/gframe/drawing.cpp index 918406c7a..fde20f7d2 100644 --- a/gframe/drawing.cpp +++ b/gframe/drawing.cpp @@ -1183,7 +1183,13 @@ void Game::DrawThumb(const CardDataC* cp, irr::core::vector2di pos, LFList* lfli auto code = cp->code; auto flit = lflist->GetLimitationIterator(cp); int count = 3; - if(flit == lflist->content.end()) { + int genesys_p = 0; + if (lflist->genesys) { + if (!(cp->ot & SCOPE_TCG) || (cp->type & (TYPE_PENDULUM | TYPE_LINK))) + count = 0; + else if (flit != lflist->content.end()) + genesys_p = flit->second; + } else if(flit == lflist->content.end()) { if(lflist->whitelist) count = -1; } else @@ -1202,22 +1208,37 @@ void Game::DrawThumb(const CardDataC* cp, irr::core::vector2di pos, LFList* lfli } driver->draw2DImage(img, dragloc, irr::core::recti(0, 0, size.Width, size.Height), cliprect); if(!is_siding) { - switch(count) { - case -1: - case 0: - imageManager.draw2DImageFilterScaled(imageManager.tLim, limitloc, irr::core::recti(0, 0, 64, 64), cliprect, 0, true); - break; - case 1: - imageManager.draw2DImageFilterScaled(imageManager.tLim, limitloc, irr::core::recti(64, 0, 128, 64), cliprect, 0, true); - break; - case 2: - imageManager.draw2DImageFilterScaled(imageManager.tLim, limitloc, irr::core::recti(0, 64, 64, 128), cliprect, 0, true); - break; - default: - if(cp->ot & SCOPE_LEGEND) { - imageManager.draw2DImageFilterScaled(imageManager.tLim, limitloc, irr::core::recti(64, 64, 128, 128), cliprect, 0, true); + if (lflist->genesys && genesys_p > 0) { + if (imageManager.tGenesysLim) { + const auto tex_size = imageManager.tGenesysLim->getOriginalSize(); + imageManager.draw2DImageFilterScaled(imageManager.tGenesysLim, limitloc, irr::core::recti(0, 0, tex_size.Width, tex_size.Height), cliprect, 0, true); + } + const auto p_str = epro::to_wstring(genesys_p); + const auto text_loc = limitloc - Scale(0, 1); + for (int x = -1; x <= 1; ++x) { + for (int y = -1; y <= 1; ++y) { + textFont->drawustring(p_str, text_loc + Scale(x, y), 0xff000000, true, true, cliprect); } - break; + } + textFont->drawustring(p_str, text_loc, 0xffffff00, true, true, cliprect); + } else { + switch(count) { + case -1: + case 0: + imageManager.draw2DImageFilterScaled(imageManager.tLim, limitloc, irr::core::recti(0, 0, 64, 64), cliprect, 0, true); + break; + case 1: + imageManager.draw2DImageFilterScaled(imageManager.tLim, limitloc, irr::core::recti(64, 0, 128, 64), cliprect, 0, true); + break; + case 2: + imageManager.draw2DImageFilterScaled(imageManager.tLim, limitloc, irr::core::recti(0, 64, 64, 128), cliprect, 0, true); + break; + default: + if(cp->ot & SCOPE_LEGEND) { + imageManager.draw2DImageFilterScaled(imageManager.tLim, limitloc, irr::core::recti(64, 64, 128, 128), cliprect, 0, true); + } + break; + } } #define IDX(scope,idx) case SCOPE_##scope:\ index = idx;\ @@ -1266,6 +1287,12 @@ void Game::DrawDeckBd() { const auto main_deck_size_str = GetDeckSizeStr(current_deck.main, gdeckManager->pre_deck.main); DrawShadowText(numFont, main_deck_size_str, Resize(379, 137, 439, 157), Resize(1, 1, 1, 1), 0xffffffff, 0xff000000, false, true); + if (deckBuilder.filterList->genesys) { + const auto points_str = epro::format(L"{}: {}", gDataManager->GetSysString(12508), deckBuilder.genesys_points); + uint32_t color = deckBuilder.genesys_points > 100 ? 0xffffff00 : 0xffffffff; + DrawShadowText(textFont, points_str, Resize(444, 136, 544, 156), Resize(1, 1, 1, 1), color, 0xff000000, false, true); + } + const auto main_types_count_str = epro::format(L"{} {} {} {} {} {}", gDataManager->GetSysString(1312), deckBuilder.main_monster_count, gDataManager->GetSysString(1313), deckBuilder.main_spell_count, diff --git a/gframe/duelclient.cpp b/gframe/duelclient.cpp index 6a946d21d..66ae77083 100644 --- a/gframe/duelclient.cpp +++ b/gframe/duelclient.cpp @@ -231,6 +231,7 @@ catch(...) { what = def; } 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); + TOI(cscg.info.points_limit, mainGame->ebPointsLimit->getText(), 100); 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; @@ -466,7 +467,8 @@ void DuelClient::HandleSTOCPacketLanAsync(const std::vector& data) { event_base_loopbreak(client_base); break; } - case ERROR_TYPE::DECKERROR: { + case ERROR_TYPE::DECKERROR: + case ERROR_TYPE::SIDEERROR: { auto pkt = BufferIO::getStruct(pdata, len); std::lock_guard lock(mainGame->gMutex); int mainmin = 40, mainmax = 60, extramax = 15, sidemax = 15; @@ -540,6 +542,10 @@ void DuelClient::HandleSTOCPacketLanAsync(const std::vector& data) { text = gDataManager->GetSysString(1427).data(); break; } + case DeckError::POINTS: { + text = epro::sprintf(gDataManager->GetSysString(12507), curcount, mainmax); + break; + } default: { text = gDataManager->GetSysString(1406).data(); break; @@ -551,11 +557,6 @@ void DuelClient::HandleSTOCPacketLanAsync(const std::vector& data) { mainGame->btnHostPrepDuelist->setEnabled(true); break; } - case ERROR_TYPE::SIDEERROR: { - std::lock_guard 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)) { @@ -572,7 +573,8 @@ void DuelClient::HandleSTOCPacketLanAsync(const std::vector& data) { 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)); + auto code = _pkt.code; + mainGame->PopupMessage(epro::sprintf(gDataManager->GetSysString(1411), code >> 12, (code >> 4) & 0xff, code & 0xf)); } if(mainGame->isHostingOnline) { #define HIDE_AND_CHECK(obj) if(obj->isVisible()) mainGame->HideElement(obj); @@ -721,6 +723,7 @@ void DuelClient::HandleSTOCPacketLanAsync(const std::vector& data) { #undef CHK } uint64_t params = (pkt.info.duel_flag_low | ((uint64_t)pkt.info.duel_flag_high) << 32); + uint64_t mode = params & ~(DUEL_TEST_MODE | DUEL_ATTACK_FIRST_TURN | DUEL_PSEUDO_SHUFFLE | DUEL_SIMPLE_AI | DUEL_RELAY | DUEL_TCG_SEGOC_NONPUBLIC | DUEL_TCG_SEGOC_FIRSTTRIGGER); mainGame->dInfo.duel_params = params; mainGame->dInfo.isRelay = params & DUEL_RELAY; params &= ~DUEL_RELAY; @@ -745,7 +748,7 @@ void DuelClient::HandleSTOCPacketLanAsync(const std::vector& data) { 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); + mainGame->dInfo.duel_field = mainGame->GetMasterRule(mode, pkt.info.forbiddentypes, &rule); if(mainGame->dInfo.compat_mode) rule = pkt.info.duel_rule; if (rule >= 6) { @@ -755,6 +758,10 @@ void DuelClient::HandleSTOCPacketLanAsync(const std::vector& data) { str.append(epro::format(L"*{}\n", gDataManager->GetSysString(1259))); } else if(params == DUEL_MODE_GOAT) { str.append(epro::format(L"*{}\n", gDataManager->GetSysString(1248))); + } + if ((params & ~DUEL_TCG_SEGOC_NONPUBLIC) == DUEL_MODE_GENESYS) { + str.append(epro::format(L"*{}\n", gDataManager->GetSysString(12506))); + str.append(epro::format(L"{}: {}\n", gDataManager->GetSysString(12508), pkt.info.points_limit)); } 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) { @@ -809,6 +816,8 @@ void DuelClient::HandleSTOCPacketLanAsync(const std::vector& data) { break; if(params == DUEL_MODE_SPEED && pkt.info.sizes == speed_deck_sizes) break; + if(params == DUEL_MODE_GENESYS && pkt.info.sizes == ocg_deck_sizes) + break; } str.append(epro::format(L"*{}\n", gDataManager->GetSysString(12112))); } while(0); @@ -879,6 +888,10 @@ void DuelClient::HandleSTOCPacketLanAsync(const std::vector& data) { mainGame->deckBuilder.filterList = &gdeckManager->_lfList[0]; watching = 0; mainGame->stHostPrepOB->setText(epro::format(L"{} {}", gDataManager->GetSysString(1253), watching).data()); + if ((params & ~DUEL_TCG_SEGOC_NONPUBLIC) == DUEL_MODE_GENESYS) { + strL.clear(); + strR.clear(); + } mainGame->stHostPrepRule->setText(str.data()); mainGame->stHostPrepRuleR->setText(strR.data()); mainGame->stHostPrepRuleL->setText(strL.data()); diff --git a/gframe/game.cpp b/gframe/game.cpp index 3c6e24c6a..c8470161f 100644 --- a/gframe/game.cpp +++ b/gframe/game.cpp @@ -743,6 +743,24 @@ void Game::Initialize() { btnMark[7] = env->addButton(Scale(80, 80, 110, 110), wLinkMarks, -1, L"\u2198"); for(int i=0;i<8;i++) btnMark[i]->setIsPushButton(true); + wGenesys = env->addWindow(Scale(700, 30, 920, 150), false, gDataManager->GetSysString(12504).data()); + defaultStrings.emplace_back(wGenesys, 12504); + wGenesys->getCloseButton()->setVisible(false); + wGenesys->setDrawTitlebar(false); + wGenesys->setDraggable(false); + wGenesys->setVisible(false); + cbGenesysOp = env->addComboBox(Scale(10, 10, 210, 35), wGenesys, COMBOBOX_GENESYS_OP); + for (int i = 0; i < 4; i++) { + cbGenesysOp->addItem(gDataManager->GetSysString(12500 + i).data()); + defaultStrings.emplace_back(cbGenesysOp, 12500 + i); + } + ebGenesys1 = env->addEditBox(L"", Scale(10, 45, 105, 70), true, wGenesys, EDITBOX_GENESYS_VAL1); + ebGenesys1->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER); + ebGenesys2 = env->addEditBox(L"", Scale(115, 45, 210, 70), true, wGenesys, EDITBOX_GENESYS_VAL2); + ebGenesys2->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER); + ebGenesys2->setVisible(false); + btnGenesysOK = env->addButton(Scale(60, 80, 160, 110), wGenesys, BUTTON_GENESYS_OK, gDataManager->GetSysString(1211).data()); + defaultStrings.emplace_back(btnGenesysOK, 1211); //replay window wReplay = env->addWindow(Scale(220, 100, 800, 520), false, gDataManager->GetSysString(1202).data()); defaultStrings.emplace_back(wReplay, 1202); @@ -1204,6 +1222,12 @@ void Game::PopulateGameHostWindows() { defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1237).data(), Scale(20, 100, 320, 120), false, false, tDuelSettings), 1237); ebTimeLimit = env->addEditBox(WStr(gGameConfig->timeLimit), Scale(140, 95, 220, 120), true, tDuelSettings, EDITBOX_NUMERIC); ebTimeLimit->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER); + stPointsLimit = env->addStaticText(gDataManager->GetSysString(12508).data(), Scale(230, 100, 320, 120), false, false, tDuelSettings); + stPointsLimit->setVisible(false); + ebPointsLimit = env->addEditBox(WStr(gGameConfig->pointsLimit), Scale(290, 95, 370, 120), true, tDuelSettings, EDITBOX_NUMERIC); + ebPointsLimit->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER); + ebPointsLimit->setVisible(false); + defaultStrings.emplace_back(stPointsLimit, 12508); defaultStrings.emplace_back(env->addStaticText(gDataManager->GetSysString(1236).data(), Scale(20, 130, 220, 150), false, false, tDuelSettings), 1236); cbDuelRule = AddComboBox(env, Scale(140, 125, 300, 150), tDuelSettings, COMBOBOX_DUEL_RULE); @@ -2489,6 +2513,9 @@ void Game::RefreshLFLists() { } deckBuilder.filterList = &gdeckManager->_lfList[cbDBLFList->getSelected()]; cbFilterBanlist->setSelected(prevFilter); + bool is_genesys = gdeckManager->_lfList[cbHostLFList->getSelected()].genesys; + stPointsLimit->setVisible(is_genesys); + ebPointsLimit->setVisible(is_genesys); } void Game::RefreshAiDecks() { gBot.bots.clear(); @@ -2565,6 +2592,7 @@ void Game::SaveConfig() { gGameConfig->lastExtraRules = extra_rules; gGameConfig->lastDuelForbidden = forbiddentypes; TrySaveInt(gGameConfig->timeLimit, ebTimeLimit); + TrySaveInt(gGameConfig->pointsLimit, ebPointsLimit); TrySaveInt(gGameConfig->team1count, ebTeam1); TrySaveInt(gGameConfig->team2count, ebTeam2); TrySaveInt(gGameConfig->bestOf, ebBestOf); @@ -3126,54 +3154,47 @@ void Game::UpdateDuelParam() { flag2 |= limits[i]; } } - switch (flag) { + switch(flag & ~DUEL_TCG_SEGOC_NONPUBLIC) { case DUEL_MODE_SPEED: { cbDuelRule->setSelected(5); - if(flag2 == DUEL_MODE_MR5_FORB) { - cbDuelRule->removeItem(8); - break; - } + break; } - [[fallthrough]]; case DUEL_MODE_RUSH: { cbDuelRule->setSelected(6); - if(flag2 == DUEL_MODE_MR5_FORB) { - cbDuelRule->removeItem(8); - break; - } + break; } - [[fallthrough]]; case DUEL_MODE_GOAT: { cbDuelRule->setSelected(7); - if(flag2 == DUEL_MODE_MR1_FORB) { - cbDuelRule->removeItem(8); - break; - } + break; + } + case DUEL_MODE_GENESYS: { + cbDuelRule->setSelected(8); + break; } - [[fallthrough]]; - default: + default: { switch(flag & ~DUEL_TCG_SEGOC_NONPUBLIC) { - #define CHECK(MR) \ +#define CHECK(MR) \ case DUEL_MODE_MR##MR:{ \ cbDuelRule->setSelected(MR - 1); \ - if (flag2 == DUEL_MODE_MR##MR##_FORB) { \ - cbDuelRule->removeItem(8); break; } \ - } \ - [[fallthrough]]; + if (flag2 == DUEL_MODE_MR##MR##_FORB) \ + return; \ + break; \ + } CHECK(1) CHECK(2) CHECK(3) CHECK(4) CHECK(5) - #undef CHECK +#undef CHECK default: { cbDuelRule->addItem(gDataManager->GetSysString(1630).data()); - cbDuelRule->setSelected(8); + cbDuelRule->setSelected(9); break; } } break; } + } duel_param = flag; forbiddentypes = flag2; } @@ -3386,6 +3407,7 @@ void Game::ReloadCBSortType() { cbSortType->clear(); for (int i = 1370; i <= 1373; i++) cbSortType->addItem(gDataManager->GetSysString(i).data()); + cbSortType->addItem(gDataManager->GetSysString(12505).data()); } void Game::ReloadCBCardType() { cbCardType->clear(); @@ -3512,6 +3534,7 @@ void Game::ReloadCBDuelRule(irr::gui::IGUIComboBox* cb) { cb->addItem(gDataManager->GetSysString(1258).data()); cb->addItem(gDataManager->GetSysString(1259).data()); cb->addItem(gDataManager->GetSysString(1248).data()); + cb->addItem(gDataManager->GetSysString(12506).data()); } void Game::ReloadCBRule() { cbRule->clear(); diff --git a/gframe/game.h b/gframe/game.h index eab91f8a1..7938201a7 100644 --- a/gframe/game.h +++ b/gframe/game.h @@ -207,6 +207,8 @@ struct host_creation_panel_elements { irr::gui::IGUIComboBox* cbMatchMode; irr::gui::IGUIComboBox* cbRule; irr::gui::IGUIEditBox* ebTimeLimit; + irr::gui::IGUIEditBox* ebPointsLimit; + irr::gui::IGUIStaticText* stPointsLimit; irr::gui::IGUIEditBox* ebTeam1; irr::gui::IGUIEditBox* ebTeam2; irr::gui::IGUIEditBox* ebBestOf; @@ -366,6 +368,11 @@ struct deck_edit_page_elements { irr::gui::IGUIWindow* wLinkMarks; irr::gui::IGUIButton* btnMark[8]; irr::gui::IGUIButton* btnMarksOK; + irr::gui::IGUIWindow* wGenesys; + irr::gui::IGUIComboBox* cbGenesysOp; + irr::gui::IGUIEditBox* ebGenesys1; + irr::gui::IGUIEditBox* ebGenesys2; + irr::gui::IGUIButton* btnGenesysOK; irr::gui::IGUICheckBox* chkAnime; //sort type irr::gui::IGUIStaticText* wSort; diff --git a/gframe/game_config.inl b/gframe/game_config.inl index 5ea99b946..b53b2d4ae 100644 --- a/gframe/game_config.inl +++ b/gframe/game_config.inl @@ -23,6 +23,7 @@ OPTION(uint64_t, lastDuelParam, 0x2E800) //#define DUEL_MODE_MR5 OPTION(uint32_t, lastExtraRules, 0) OPTION(uint32_t, lastDuelForbidden, 0) //#define DUEL_MODE_MR5_FORB OPTION(uint32_t, timeLimit, 180) +OPTION(uint16_t, pointsLimit, 100) OPTION(uint32_t, team1count, 1) OPTION(uint32_t, team2count, 1) OPTION(uint32_t, bestOf, 1) diff --git a/gframe/generic_duel.cpp b/gframe/generic_duel.cpp index 32b91f057..1c9e81842 100644 --- a/gframe/generic_duel.cpp +++ b/gframe/generic_duel.cpp @@ -378,7 +378,7 @@ void GenericDuel::PlayerReady(DuelPlayer* dp, bool is_ready) { } else { bool rituals_in_extra = host_info.duel_flag_high & (DUEL_EXTRA_DECK_RITUAL >> 32); deck_error = DeckManager::CheckDeckContent(dueler.pdeck, gdeckManager->GetLFList(host_info.lflist), - static_cast(host_info.rule), host_info.forbiddentypes, rituals_in_extra); + static_cast(host_info.rule), host_info.forbiddentypes, rituals_in_extra, host_info.points_limit); } } if(deck_error.type != DeckError::NONE) { diff --git a/gframe/image_manager.cpp b/gframe/image_manager.cpp index aed71e8c0..272d23739 100644 --- a/gframe/image_manager.cpp +++ b/gframe/image_manager.cpp @@ -146,7 +146,10 @@ bool ImageManager::Initial() { tLim = loadTextureAnySize(EPRO_TEXT("lim"sv)); ASSERT_TEXTURE_LOADED(tLim, "lim"); ASSIGN_DEFAULT(tLim); - + tGenesysLim = loadTextureAnySize(EPRO_TEXT("genesys_lim"sv)); + if (tGenesysLim) { + ASSIGN_DEFAULT(tGenesysLim); + } tOT = loadTextureAnySize(EPRO_TEXT("ot"sv)); ASSERT_TEXTURE_LOADED(tOT, "ot"); ASSIGN_DEFAULT(tOT); @@ -297,6 +300,7 @@ void ImageManager::ChangeTextures(epro::path_stringview _path) { REPLACE_TEXTURE_ANY_SIZE(tTarget, "target"); REPLACE_TEXTURE_ANY_SIZE(tChainTarget, "chaintarget"); REPLACE_TEXTURE_ANY_SIZE(tLim, "lim"); + REPLACE_TEXTURE_ANY_SIZE(tGenesysLim, "genesys_lim"); REPLACE_TEXTURE_ANY_SIZE(tOT, "ot"); REPLACE_TEXTURE_WITH_FIXED_SIZE(tHand[0], "f1", 89, 128); REPLACE_TEXTURE_WITH_FIXED_SIZE(tHand[1], "f2", 89, 128); diff --git a/gframe/image_manager.h b/gframe/image_manager.h index fad467ae2..59927a0d9 100644 --- a/gframe/image_manager.h +++ b/gframe/image_manager.h @@ -127,6 +127,7 @@ class ImageManager { A(tTarget) A(tChainTarget) A(tLim) + A(tGenesysLim) A(tOT) A(tHand[3]) A(tBackGround) diff --git a/gframe/menu_handler.cpp b/gframe/menu_handler.cpp index 7b4e5d500..094ce963c 100644 --- a/gframe/menu_handler.cpp +++ b/gframe/menu_handler.cpp @@ -933,6 +933,11 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) { mainGame->forbiddentypes = DUEL_MODE_MR1_FORB; break; } + case 8: { + mainGame->duel_param = DUEL_MODE_GENESYS; + mainGame->forbiddentypes = DUEL_MODE_GENESYS_FORB; + break; + } } #undef CHECK mainGame->duel_param |= tcg; @@ -963,6 +968,11 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) { } case irr::gui::EGET_COMBO_BOX_CHANGED: { switch (id) { + case COMBOBOX_HOST_LFLIST: { + gGameConfig->lastlflist = mainGame->cbHostLFList->getItemData(mainGame->cbHostLFList->getSelected()); + mainGame->RefreshLFLists(); + break; + } case COMBOBOX_DUEL_RULE: { auto setDeckSizes = [&](const DeckSizes& size) { mainGame->ebMainMin->setText(epro::to_wstring(size.main.min).data()); @@ -1008,9 +1018,21 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) { mainGame->ebStartHand->setText(L"5"); goto remove; } + case 8: { + mainGame->duel_param = DUEL_MODE_GENESYS; + setDeckSizes(ocg_deck_sizes); + mainGame->forbiddentypes = DUEL_MODE_GENESYS_FORB; + mainGame->chkTcgRulings->setChecked(true); + mainGame->ebStartHand->setText(L"5"); + auto selected = mainGame->cbHostLFList->getSelected(); + bool is_genesys = gdeckManager->_lfList[selected].genesys; + mainGame->stPointsLimit->setVisible(is_genesys); + mainGame->ebPointsLimit->setVisible(is_genesys); + goto remove; + } default: break; remove: - combobox->removeItem(8); + combobox->removeItem(9); mainGame->UpdateExtraRules(); } #undef CHECK diff --git a/gframe/menu_handler.h b/gframe/menu_handler.h index 553686467..5139cc708 100644 --- a/gframe/menu_handler.h +++ b/gframe/menu_handler.h @@ -212,6 +212,10 @@ enum GUI { BUTTON_MARKS_FILTER, BUTTON_MARKERS_OK, + BUTTON_GENESYS_OK, + COMBOBOX_GENESYS_OP, + EDITBOX_GENESYS_VAL1, + EDITBOX_GENESYS_VAL2, CHECKBOX_OBSOLETE, CHECKBOX_DRAW, diff --git a/gframe/network.h b/gframe/network.h index 3961eaa7a..467c4b242 100644 --- a/gframe/network.h +++ b/gframe/network.h @@ -57,6 +57,8 @@ struct HostInfo { uint32_t duel_flag_low; uint32_t forbiddentypes; uint16_t extra_rules; + uint16_t points_limit; + char : 0; DeckSizes sizes; }; struct HostPacket { @@ -121,7 +123,8 @@ struct DeckError { UNOFFICIALCARD, INVALIDSIZE, TOOMANYLEGENDS, - TOOMANYSKILLS + TOOMANYSKILLS, + POINTS }; DERR_TYPE type = DERR_TYPE::NONE; struct { diff --git a/gframe/ocgapi_constants.h b/gframe/ocgapi_constants.h index cdc0f4c03..05d6783c7 100644 --- a/gframe/ocgapi_constants.h +++ b/gframe/ocgapi_constants.h @@ -421,11 +421,13 @@ #define DUEL_MODE_MR3 (DUEL_PZONE | DUEL_SEPARATE_PZONE | DUEL_SPSUMMON_ONCE_OLD_NEGATE | DUEL_RETURN_TO_DECK_TRIGGERS | DUEL_CANNOT_SUMMON_OATH_OLD) #define DUEL_MODE_MR4 (DUEL_PZONE | DUEL_EMZONE | DUEL_SPSUMMON_ONCE_OLD_NEGATE | DUEL_RETURN_TO_DECK_TRIGGERS | DUEL_CANNOT_SUMMON_OATH_OLD) #define DUEL_MODE_MR5 (DUEL_PZONE | DUEL_EMZONE | DUEL_FSX_MMZONE | DUEL_TRAP_MONSTERS_NOT_USE_ZONE | DUEL_TRIGGER_ONLY_IN_LOCATION) +#define DUEL_MODE_GENESYS (DUEL_FSX_MMZONE | DUEL_TRAP_MONSTERS_NOT_USE_ZONE | DUEL_TRIGGER_ONLY_IN_LOCATION) #define DUEL_MODE_MR1_FORB (TYPE_XYZ | TYPE_PENDULUM | TYPE_LINK) #define DUEL_MODE_MR2_FORB (TYPE_PENDULUM | TYPE_LINK) #define DUEL_MODE_MR3_FORB TYPE_LINK #define DUEL_MODE_MR4_FORB 0 #define DUEL_MODE_MR5_FORB 0 +#define DUEL_MODE_GENESYS_FORB (TYPE_PENDULUM | TYPE_LINK) #endif /* OCGAPI_CONSTANTS_H */