minor: New Game Digimon Digi-Battle (#17)

* digimon digi battle added to supported games

* sonarqube update

* readme update

---------

Co-authored-by: sdine <sdine@sdine.com>
This commit is contained in:
Sebastian Dine
2026-07-19 12:06:57 +02:00
committed by GitHub
parent 42926f2fb5
commit e5c830e945
53 changed files with 2170 additions and 49 deletions
+3 -3
View File
@@ -5,11 +5,11 @@ The `ccm` executable — composition root only. The single place where concrete
## File pointers
- `main.cpp` — the entire app. Defines `CcmApp : public wxApp`, builds the dependency graph in `OnInit()`, then hands an `AppContext` to `MainFrame`.
- `CMakeLists.txt` — declares the `ccm` target. Sets `WIN32_EXECUTABLE TRUE` on Windows so no console window appears. Links `ccm_core`, `ccm_ui_wx`, `ccm_warnings`. **`POST_BUILD`**: creates `$<TARGET_FILE_DIR:ccm>/assets/` and copies `ui_wx/assets/ygo_card_back.png` there so Yu-Gi-Oh! preview fallbacks work offline (see `BaseSelectedCardPanel` / `docs/assets-and-info-apis.md`).
- `CMakeLists.txt` — declares the `ccm` target. Sets `WIN32_EXECUTABLE TRUE` on Windows so no console window appears. Links `ccm_core`, `ccm_ui_wx`, `ccm_warnings`. **`POST_BUILD`**: creates `$<TARGET_FILE_DIR:ccm>/assets/` and copies `ui_wx/assets/ygo_card_back.png` and `ui_wx/assets/digibattle99_card_back.png` there so Yu-Gi-Oh! / Digi-Battle preview fallbacks work offline (see `BaseSelectedCardPanel` / `docs/assets-and-info-apis.md`).
## Conventions
1. **Composition root is the only place** that names concrete adapters: `StdFileSystem`, `CprHttpClient`, `JsonCollectionRepository<MagicCard>`, `JsonCollectionRepository<PokemonCard>`, `JsonCollectionRepository<YuGiOhCard>`, `JsonSetRepository`, `LocalImageStore`, `LocalPreviewByteCache`, `MagicGameModule`, `PokemonGameModule`, `YuGiOhGameModule`, `MagicGameView`, `PokemonGameView`, `YuGiOhGameView`, etc. If a concrete adapter type appears anywhere else in the codebase, move the wiring here.
1. **Composition root is the only place** that names concrete adapters: `StdFileSystem`, `CprHttpClient`, `JsonCollectionRepository<MagicCard>`, `JsonCollectionRepository<PokemonCard>`, `JsonCollectionRepository<YuGiOhCard>`, `JsonCollectionRepository<DigiBattle99Card>`, `JsonSetRepository`, `LocalImageStore`, `LocalPreviewByteCache`, `MagicGameModule`, `PokemonGameModule`, `YuGiOhGameModule`, `DigiBattle99GameModule`, `MagicGameView`, `PokemonGameView`, `YuGiOhGameView`, `DigiBattle99GameView`, etc. If a concrete adapter type appears anywhere else in the codebase, move the wiring here.
2. **Member declaration order in `CcmApp` matters** — destruction is reverse, so a member that depends on another (e.g. `magicCollSvc_` depends on `magicRepo_` and `imgStore_`; `previewSvc_` depends on `http_` and is consumed by `ctx_`; `magicView_` depends on the typed `magicCollSvc_` and the shared services) must be declared **after** its deps. Do not reorder casually.
3. **Use `std::unique_ptr` for everything owned** by `CcmApp`. The `AppContext` then holds plain references into those owned objects, plus a vector of `IGameView*` raw pointers (the `unique_ptr<>`s for the views are the actual owners; the vector just describes the active set).
4. **Game-to-directory mapping** lives in `dirNameForGame(Game)` (anonymous namespace). When adding a new game, extend this function — it is wired into all three repositories (`JsonCollectionRepository`, `JsonSetRepository`, `LocalImageStore`).
@@ -21,7 +21,7 @@ The `ccm` executable — composition root only. The single place where concrete
## Required follow-ups
- The **`POST_BUILD` copy of `ygo_card_back.png`** must stay in sync with `ui_wx/assets/`; if you relocate install layout or add more bundled assets, mirror the pattern (`make_directory` + `copy_if_different`) and document under `docs/assets-and-info-apis.md` / `ui_wx/AGENTS.md`.
- The **`POST_BUILD` copy of `ygo_card_back.png` / `digibattle99_card_back.png`** must stay in sync with `ui_wx/assets/`; if you relocate install layout or add more bundled assets, mirror the pattern (`make_directory` + `copy_if_different`) and document under `docs/assets-and-info-apis.md` / `ui_wx/AGENTS.md`.
- After adding a new game module you **must**: (1) add a `unique_ptr<<Name>GameModule>` member in declaration-order-correct position, (2) construct it in `OnInit()`, (3) call `setSvc_->registerModule(<name>Mod_.get())`, (4) call `previewSvc_->registerModule(*<name>Mod_)` (no-op when the module has no preview source), (5) extend `dirNameForGame`, (6) add a typed `JsonCollectionRepository<<Name>Card>` + `CollectionService<<Name>Card>` if the game has a custom card type, (7) construct a `<Name>GameView` and append its raw pointer to the `AppContext::gameViews` vector, (8) make sure the view's `unique_ptr<>` member sits **after** all its deps (typed services + `IGameModule`).
- After adding a new core service you **must** add a `unique_ptr<...>` member, construct it in `OnInit()` after its deps, and add a reference field to `AppContext`.
- After adding a new dependency edge you **must** verify destruction order is still correct: deps **before** dependents in the member list.
+6 -2
View File
@@ -19,9 +19,13 @@ target_link_libraries(ccm
ccm_warnings
)
# Yu-Gi-Oh! preview fallback image (used when network card-back URLs fail).
# Yu-Gi-Oh! / Digi-Battle preview fallback images (used when network card-back
# URLs fail or no public URL exists).
add_custom_command(TARGET ccm POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:ccm>/assets"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/ui_wx/assets/ygo_card_back.png"
"$<TARGET_FILE_DIR:ccm>/assets/ygo_card_back.png")
"$<TARGET_FILE_DIR:ccm>/assets/ygo_card_back.png"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_SOURCE_DIR}/ui_wx/assets/digibattle99_card_back.png"
"$<TARGET_FILE_DIR:ccm>/assets/digibattle99_card_back.png")
+25 -4
View File
@@ -2,9 +2,11 @@
// it to the wxWidgets UI layer. This is the only place where concrete adapter
// types are mentioned - everything downstream depends on interfaces.
#include "ccm/domain/DigiBattle99Card.hpp"
#include "ccm/domain/MagicCard.hpp"
#include "ccm/domain/PokemonCard.hpp"
#include "ccm/domain/YuGiOhCard.hpp"
#include "ccm/games/digibattle99/DigiBattle99GameModule.hpp"
#include "ccm/games/magic/MagicGameModule.hpp"
#include "ccm/games/pokemon/PokemonGameModule.hpp"
#include "ccm/games/yugioh/YuGiOhGameModule.hpp"
@@ -20,6 +22,7 @@
#include "ccm/services/ImageService.hpp"
#include "ccm/services/SetService.hpp"
#include "ccm/ui/AppContext.hpp"
#include "ccm/ui/DigiBattle99GameView.hpp"
#include "ccm/ui/MagicGameView.hpp"
#include "ccm/ui/MainFrame.hpp"
#include "ccm/ui/PokemonGameView.hpp"
@@ -42,9 +45,10 @@ namespace {
// need for the repositories to know about concrete game module classes.
std::string dirNameForGame(ccm::Game g) {
switch (g) {
case ccm::Game::Magic: return "magic";
case ccm::Game::Pokemon: return "pokemon";
case ccm::Game::YuGiOh: return "yugioh";
case ccm::Game::Magic: return "magic";
case ccm::Game::Pokemon: return "pokemon";
case ccm::Game::YuGiOh: return "yugioh";
case ccm::Game::DigiBattle99: return "digibattle99";
}
return "magic";
}
@@ -80,6 +84,7 @@ public:
magicMod_ = std::make_unique<ccm::MagicGameModule>(*http_);
pokeMod_ = std::make_unique<ccm::PokemonGameModule>(*http_);
ygoMod_ = std::make_unique<ccm::YuGiOhGameModule>(*http_);
digiBattle99Mod_ = std::make_unique<ccm::DigiBattle99GameModule>(*http_);
magicRepo_ = std::make_unique<ccm::JsonCollectionRepository<ccm::MagicCard>>(
*fs_, *config_, &dirNameForGame);
@@ -87,6 +92,9 @@ public:
*fs_, *config_, &dirNameForGame);
ygoRepo_ = std::make_unique<ccm::JsonCollectionRepository<ccm::YuGiOhCard>>(
*fs_, *config_, &dirNameForGame);
digiBattle99Repo_ =
std::make_unique<ccm::JsonCollectionRepository<ccm::DigiBattle99Card>>(
*fs_, *config_, &dirNameForGame);
setRepo_ = std::make_unique<ccm::JsonSetRepository>(*fs_, *config_, &dirNameForGame);
imgStore_ = std::make_unique<ccm::LocalImageStore>(*fs_, *config_, &dirNameForGame);
@@ -97,10 +105,14 @@ public:
*pokeRepo_, *imgStore_);
ygoCollSvc_ = std::make_unique<ccm::CollectionService<ccm::YuGiOhCard>>(
*ygoRepo_, *imgStore_);
digiBattle99CollSvc_ =
std::make_unique<ccm::CollectionService<ccm::DigiBattle99Card>>(
*digiBattle99Repo_, *imgStore_);
setSvc_ = std::make_unique<ccm::SetService>(*setRepo_);
setSvc_->registerModule(magicMod_.get());
setSvc_->registerModule(pokeMod_.get());
setSvc_->registerModule(ygoMod_.get());
setSvc_->registerModule(digiBattle99Mod_.get());
// Disk-backed preview cache lives next to the executable, in the same
// location scope as config.json - NOT inside the user's data-storage
@@ -120,6 +132,7 @@ public:
previewSvc_->registerModule(*magicMod_);
previewSvc_->registerModule(*pokeMod_);
previewSvc_->registerModule(*ygoMod_);
previewSvc_->registerModule(*digiBattle99Mod_);
// Per-game UI bundles. Order here is the order shown in the Game menu.
magicView_ = std::make_unique<ccm::ui::MagicGameView>(
@@ -128,6 +141,9 @@ public:
*config_, *pokeCollSvc_, *setSvc_, *imgSvc_, *previewSvc_, *pokeMod_);
ygoView_ = std::make_unique<ccm::ui::YuGiOhGameView>(
*config_, *ygoCollSvc_, *setSvc_, *imgSvc_, *previewSvc_, *ygoMod_);
digiBattle99View_ = std::make_unique<ccm::ui::DigiBattle99GameView>(
*config_, *digiBattle99CollSvc_, *setSvc_, *imgSvc_, *previewSvc_,
*digiBattle99Mod_);
ctx_ = std::make_unique<ccm::ui::AppContext>(ccm::ui::AppContext{
*config_,
@@ -137,7 +153,8 @@ public:
*magicMod_,
*pokeMod_,
*ygoMod_,
{ magicView_.get(), pokeView_.get(), ygoView_.get() },
*digiBattle99Mod_,
{ magicView_.get(), pokeView_.get(), ygoView_.get(), digiBattle99View_.get() },
});
auto* frame = new ccm::ui::MainFrame(*ctx_);
@@ -158,21 +175,25 @@ private:
std::unique_ptr<ccm::MagicGameModule> magicMod_;
std::unique_ptr<ccm::PokemonGameModule> pokeMod_;
std::unique_ptr<ccm::YuGiOhGameModule> ygoMod_;
std::unique_ptr<ccm::DigiBattle99GameModule> digiBattle99Mod_;
std::unique_ptr<ccm::JsonCollectionRepository<ccm::MagicCard>> magicRepo_;
std::unique_ptr<ccm::JsonCollectionRepository<ccm::PokemonCard>> pokeRepo_;
std::unique_ptr<ccm::JsonCollectionRepository<ccm::YuGiOhCard>> ygoRepo_;
std::unique_ptr<ccm::JsonCollectionRepository<ccm::DigiBattle99Card>> digiBattle99Repo_;
std::unique_ptr<ccm::JsonSetRepository> setRepo_;
std::unique_ptr<ccm::LocalImageStore> imgStore_;
std::unique_ptr<ccm::ImageService> imgSvc_;
std::unique_ptr<ccm::CollectionService<ccm::MagicCard>> magicCollSvc_;
std::unique_ptr<ccm::CollectionService<ccm::PokemonCard>> pokeCollSvc_;
std::unique_ptr<ccm::CollectionService<ccm::YuGiOhCard>> ygoCollSvc_;
std::unique_ptr<ccm::CollectionService<ccm::DigiBattle99Card>> digiBattle99CollSvc_;
std::unique_ptr<ccm::SetService> setSvc_;
std::unique_ptr<ccm::LocalPreviewByteCache> previewCache_;
std::unique_ptr<ccm::CardPreviewService> previewSvc_;
std::unique_ptr<ccm::ui::MagicGameView> magicView_;
std::unique_ptr<ccm::ui::PokemonGameView> pokeView_;
std::unique_ptr<ccm::ui::YuGiOhGameView> ygoView_;
std::unique_ptr<ccm::ui::DigiBattle99GameView> digiBattle99View_;
std::unique_ptr<ccm::ui::AppContext> ctx_;
};