Minor: New Game Yu-Gi-Oh! Bandai (#22)

This commit is contained in:
Sebastian Dine
2026-07-30 14:51:53 +02:00
committed by GitHub
parent 7cf25d671f
commit 2eb7c59f78
65 changed files with 4142 additions and 75 deletions
@@ -0,0 +1,77 @@
#pragma once
// YuGiOhBandaiCardPreviewSource: Yugipedia pageimages + SMW ask for Bandai
// Carddass previews and auto-detect (by English name or Bandai number).
#include "ccm/ports/ICardPreviewSource.hpp"
#include "ccm/ports/IHttpClient.hpp"
#include <string>
#include <string_view>
#include <vector>
namespace ccm {
class YuGiOhBandaiCardPreviewSource final : public ICardPreviewSource {
public:
explicit YuGiOhBandaiCardPreviewSource(IHttpClient& http);
[[nodiscard]] bool supportsAutoDetectPrint() const noexcept override { return true; }
Result<std::string, PreviewLookupError>
fetchImageUrl(std::string_view name,
std::string_view setId,
std::string_view setNo) override;
Result<AutoDetectedPrint> detectFirstPrint(std::string_view name,
std::string_view setId) override;
Result<std::vector<AutoDetectedPrint>> detectPrintVariants(std::string_view name,
std::string_view setId) override;
Result<AutoDetectedPrint> detectBySetNo(std::string_view setNo) override;
Result<std::vector<AutoDetectedPrint>> detectVariantsBySetNo(
std::string_view setNo) override;
// Prefer "<Name> (Bandai)" / English / Sealdass page depending on setId.
static std::string preferredPageTitle(std::string_view name,
std::string_view setId,
std::string_view setNo);
static std::string buildPageImagesUrl(std::string_view pageTitle);
static std::string buildAskByNameUrl(std::string_view englishName);
static std::string buildAskByNumberUrl(std::string_view setNo);
// True for Jump/Toei promo codes (J1, TA2, …). Yugipedia's SMW
// `Bandai number` property is numeric-only, so these must use the
// promotional gallery instead of `action=ask`.
[[nodiscard]] static bool isAlphanumericPromoNumber(std::string_view setNo);
static Result<std::vector<AutoDetectedPrint>>
parsePromoGalleryResponse(const std::string& body,
std::string_view wantedSetNo);
static Result<std::string, PreviewLookupError>
parsePageImagesResponse(const std::string& body);
static Result<std::vector<AutoDetectedPrint>>
parseAskResponse(const std::string& body, std::string_view preferredSetId);
static AutoDetectedPrint enrichPrint(AutoDetectedPrint print,
std::string_view pageTitle);
private:
Result<std::string, PreviewLookupError> fetchPageImage(std::string_view pageTitle);
Result<std::vector<AutoDetectedPrint>> askByName(std::string_view name,
std::string_view setId);
Result<std::vector<AutoDetectedPrint>> askByNumber(std::string_view setNo);
IHttpClient& http_;
};
} // namespace ccm
@@ -0,0 +1,29 @@
#pragma once
// YuGiOhBandaiGameModule: Bandai Carddass via Yugipedia.
#include "ccm/games/IGameModule.hpp"
#include "ccm/games/yugiohbandai/YuGiOhBandaiCardPreviewSource.hpp"
#include "ccm/games/yugiohbandai/YuGiOhBandaiSetSource.hpp"
namespace ccm {
class YuGiOhBandaiGameModule final : public IGameModule {
public:
explicit YuGiOhBandaiGameModule(IHttpClient& http);
[[nodiscard]] Game id() const noexcept override { return Game::YuGiOhBandai; }
[[nodiscard]] std::string dirName() const override { return "yugiohbandai"; }
[[nodiscard]] std::string displayName() const override { return "Yu-Gi-Oh! (Bandai)"; }
ISetSource& setSource() override { return setSource_; }
ICardPreviewSource* cardPreviewSource() noexcept override { return &previewSource_; }
YuGiOhBandaiSetSource& bandaiSetSource() noexcept { return setSource_; }
private:
YuGiOhBandaiSetSource setSource_;
YuGiOhBandaiCardPreviewSource previewSource_;
};
} // namespace ccm
@@ -0,0 +1,67 @@
#pragma once
// YuGiOhBandaiSetSource: hardcoded Bandai set manifest + Yugipedia gallery
// wikitext catalogs for set completion.
#include "ccm/domain/Set.hpp"
#include "ccm/domain/YuGiOhBandaiSetCatalog.hpp"
#include "ccm/games/IGameModule.hpp"
#include "ccm/ports/IHttpClient.hpp"
#include <string>
#include <string_view>
#include <vector>
namespace ccm {
class YuGiOhBandaiSetSource final : public ISetSource {
public:
struct FetchWithCatalog {
std::vector<Set> sets;
YuGiOhBandaiSetCatalog catalog;
};
struct SetManifestEntry {
const char* id;
const char* name;
const char* releaseDate; // YYYY/MM/DD
const char* galleryPage; // Yugipedia page title (may be shared)
// For the shared promo gallery: keep cards whose setNo starts with
// this prefix (empty = keep all from that page into this pack).
const char* setNoPrefix;
};
explicit YuGiOhBandaiSetSource(IHttpClient& http);
Result<std::vector<Set>> fetchAll() override;
Result<FetchWithCatalog> fetchAllWithCatalog();
[[nodiscard]] static const std::vector<SetManifestEntry>& setManifest();
static Result<std::vector<Set>> parseResponse(const std::string& /*unused*/);
// Parse one gallery wikitext body into checklist cards.
static Result<std::vector<YuGiOhBandaiCatalogCard>>
parseGalleryWikitext(const std::string& wikitext);
// Map a Bandai number string to a set id (ban1/ban2/ban3/promos/sealdass).
static std::string setIdForNumber(std::string_view setNo);
static std::string setNameForId(std::string_view setId);
// Normalize printed numbers: strip leading zeros on pure-decimal values;
// uppercase letter prefixes (j1 → J1). Sealdass stays unpadded decimal.
static std::string normalizeCardNumber(std::string_view setNo);
static std::string expandRarityCode(std::string_view code);
static std::string buildGalleryParseUrl(std::string_view pageTitle);
static std::string englishNameFromGalleryTitle(std::string_view pageTitle);
private:
IHttpClient& http_;
};
} // namespace ccm