Migrate core engine interfaces to GroveEngine repository

Removed core engine infrastructure from warfactoryracine:
- Core interfaces: IEngine, IModule, IModuleSystem, IIO, ITaskScheduler, ICoordinationModule
- Configuration system: IDataTree, IDataNode, DataTreeFactory
- UI system: IUI, IUI_Enums, ImGuiUI (header + implementation)
- Resource management: Resource, ResourceRegistry, SerializationRegistry
- Serialization: ASerializable, ISerializable
- World generation: IWorldGenerationStep (replaced by IWorldGenerationPhase)

These components now live in the GroveEngine repository and are included
via CMake add_subdirectory(../GroveEngine) for reusability across projects.

warfactoryracine remains focused on game-specific logic and content.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
StillHammer 2025-10-28 00:22:36 +08:00
parent 63a2d251ff
commit f393b28d73
37 changed files with 5007 additions and 3680 deletions

7
.claude/settings.json Normal file
View File

@ -0,0 +1,7 @@
{
"permissions": {
"additionalDirectories": [
"../GroveEngine"
]
}
}

View File

@ -14,7 +14,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## 📋 Implementation Status
**ALWAYS CHECK**: `TODO.md` at project root for current implementation roadmap and tasks.
**ALWAYS CHECK**:
- `TODO.md` at project root for current implementation roadmap and high-level phases
- `TASKLIST.md` for detailed task breakdown organized by time investment (Quick Wins, Light Tasks, Medium Tasks, Feature Tasks, Big Tasks)
**Current Phase**: World Generation System - Geological and resource foundation complete
@ -99,6 +101,7 @@ The project uses a **hierarchical documentation system** in `/docs/`:
- `systeme-militaire.md` - Vehicle design with grid-based component placement
- `economie-logistique.md` - Market simulation, supply chains, pricing
- `map-system.md` - Procedural generation with 218+ elements
- `rendering-system.md` - Sprite composition and entity rendering
- `factory-architecture-post-player.md` - Advanced production architecture
### 🔧 03-implementation/
@ -389,6 +392,10 @@ The project includes 16 C++ libraries via FetchContent:
3. `03-implementation/testing-strategy.md` - Testing approach
4. `04-reference/INTEGRATION-MASTER-LIST.md` - Complete specifications
### For Task Management
1. `TODO.md` - High-level implementation roadmap and current phases
2. **`TASKLIST.md`** - **DETAILED**: Complete task breakdown by time (Quick Wins < 1h, Light 1-3h, Medium 1-2 days, Feature 3-7 days, Big 1-2 weeks)
### For Technical Reference
1. `04-reference/arbre-technologique.md` - Complete tech tree
2. `04-reference/coherence-problem.md` - Technical analyses

View File

@ -37,6 +37,9 @@ FetchContent_Declare(
FetchContent_MakeAvailable(nlohmann_json imgui)
# Add GroveEngine
add_subdirectory(../GroveEngine GroveEngine_build)
# Create ImGui library with OpenGL/GLFW backends
add_library(imgui_backends
${imgui_SOURCE_DIR}/imgui.cpp
@ -58,16 +61,15 @@ target_link_libraries(imgui_backends PUBLIC glfw OpenGL::GL)
# Test executable
add_executable(test_imgui_ui
test_imgui_ui.cpp
src/core/src/ImGuiUI.cpp
)
target_include_directories(test_imgui_ui PRIVATE
src/core/include
${imgui_SOURCE_DIR}
${imgui_SOURCE_DIR}/backends
)
target_link_libraries(test_imgui_ui
GroveEngine::impl
imgui_backends
nlohmann_json::nlohmann_json
glfw
@ -84,16 +86,15 @@ set_target_properties(test_imgui_ui PROPERTIES
# Test constraints executable
add_executable(test_constraints
test_constraints.cpp
src/core/src/ImGuiUI.cpp
)
target_include_directories(test_constraints PRIVATE
src/core/include
${imgui_SOURCE_DIR}
${imgui_SOURCE_DIR}/backends
)
target_link_libraries(test_constraints
GroveEngine::impl
imgui_backends
nlohmann_json::nlohmann_json
glfw

1723
TASKLIST.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -264,5 +264,3 @@ Moteur → [PROTOTYPE] Châssis Performance
Warfactory transcende les genres traditionnels en unifiant industrie, stratégie militaire et simulation économique authentique dans une architecture technique révolutionnaire.
Le projet honore l'héroïsme ukrainien tout en repoussant les limites du développement assisté par IA, créant un gameplay émergent d'une profondeur inégalée où chaque choix - industriel, militaire, économique - résonne à travers un système interconnecté d'une complexité et d'un réalisme saisissants.
**Slava Ukraini !**

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,516 @@
# Rendering System - Sprite Composition
## Vue d'ensemble
Le système de rendu graphique de Warfactory utilise une approche de **composition de sprites** inspirée de Rimworld, optimisée pour minimiser le nombre de sprites à dessiner tout en permettant une grande flexibilité visuelle.
**Principe fondamental** : Chaque entité est composée de sprites superposés avec des orientations indépendantes, permettant un rendu dynamique efficace.
## Architecture de composition
### Personnages (Humains)
Un personnage est composé de **3 sprites maximum** :
```
Personnage
├── Corps (sprite de base + vêtements composés)
├── Tête (orientation indépendante - turret behavior)
└── Arme (orientation indépendante - turret behavior)
```
**Optimisation clé** : Les vêtements sont pré-composés avec le corps pour éviter de dessiner chaque couche de vêtement séparément.
#### Exemple de composition
```
Rendu final = Corps_Base + Veste + Pantalon + Tête + Arme
└─────────────────────┘
1 sprite composé
```
Au lieu de dessiner 5 sprites par personnage, on en dessine **3** :
- 1 sprite corps composé (avec tous les vêtements)
- 1 sprite tête
- 1 sprite arme
### Véhicules
Un véhicule suit le même principe avec un nombre variable de tourelles :
```
Véhicule
├── Corps (sprite fixe avec orientation principale)
└── Tourelles[] (orientations indépendantes)
├── Tourelle principale
├── Tourelle secondaire (optionnelle)
└── Tourelle tertiaire (optionnelle)
```
**Exemple** :
- Tank léger : Corps + 1 tourelle = **2 sprites**
- APC : Corps + 1 tourelle principale + 1 mitrailleuse = **3 sprites**
- Véhicule lourd : Corps + 3 tourelles = **4 sprites**
## Pipeline unifiée : Concept Fixe/Mobile
Le système distingue deux types de composants :
### 1. Composants Fixes
**Définition** : Composants dont l'orientation est liée à l'orientation principale de l'entité.
**Caractéristiques** :
- Rotation synchronisée avec l'entité parente
- Pas de calcul d'orientation indépendant
- Exemples : corps de personnage, châssis de véhicule
```cpp
struct FixedComponent {
SpriteID sprite;
Vector2 offset; // Position relative au centre de l'entité
float rotationOffset; // Rotation additionnelle par rapport à l'orientation principale
};
```
### 2. Composants Mobiles (Turret Behavior)
**Définition** : Composants avec orientation indépendante suivant une cible.
**Caractéristiques** :
- Orientation calculée indépendamment
- Cible une position XY (target)
- Contraintes de rotation optionnelles (angle min/max, vitesse de rotation)
- Exemples : tête de personnage, arme, tourelle de véhicule
```cpp
struct MobileComponent {
SpriteID sprite;
Vector2 offset; // Position relative au centre de l'entité
// Turret behavior
Vector2 currentTarget; // Position XY actuelle de la cible
float currentAngle; // Angle actuel du composant
float rotationSpeed; // Vitesse de rotation max (rad/s)
// Contraintes optionnelles
float minAngle; // Angle minimum (par rapport au fixe)
float maxAngle; // Angle maximum (par rapport au fixe)
bool hasConstraints; // Si true, applique les contraintes min/max
};
```
## Pipeline de rendu unifiée
La **même pipeline** gère les têtes, armes et tourelles :
```cpp
class EntityRenderer {
// Composants fixes (corps)
std::vector<FixedComponent> fixedComponents;
// Composants mobiles (têtes, armes, tourelles)
std::vector<MobileComponent> mobileComponents;
void render(Vector2 position, float mainOrientation) {
// 1. Dessiner les composants fixes
for (const FixedComponent& comp : fixedComponents) {
float finalAngle = mainOrientation + comp.rotationOffset;
drawSprite(comp.sprite, position + rotate(comp.offset, mainOrientation), finalAngle);
}
// 2. Dessiner les composants mobiles
for (MobileComponent& comp : mobileComponents) {
// Calculer l'orientation vers la cible
float targetAngle = calculateAngleToTarget(position, comp.offset, comp.currentTarget);
// Appliquer la vitesse de rotation (smooth rotation)
comp.currentAngle = lerpAngle(comp.currentAngle, targetAngle, comp.rotationSpeed * deltaTime);
// Appliquer les contraintes si nécessaire
if (comp.hasConstraints) {
float relativeAngle = comp.currentAngle - mainOrientation;
comp.currentAngle = mainOrientation + clamp(relativeAngle, comp.minAngle, comp.maxAngle);
}
drawSprite(comp.sprite, position + rotate(comp.offset, mainOrientation), comp.currentAngle);
}
}
};
```
## Exemples concrets
### Personnage avec arme
```cpp
EntityRenderer soldier;
// Corps (fixe)
soldier.fixedComponents.push_back({
.sprite = SPRITE_SOLDIER_BODY_COMPOSED,
.offset = {0, 0},
.rotationOffset = 0
});
// Tête (mobile - turret behavior)
soldier.mobileComponents.push_back({
.sprite = SPRITE_HEAD,
.offset = {0, 8}, // 8 pixels au-dessus du centre
.currentTarget = enemyPosition,
.currentAngle = 0,
.rotationSpeed = 5.0f, // rad/s
.minAngle = -PI/2, // Peut regarder 90° à gauche
.maxAngle = PI/2, // Peut regarder 90° à droite
.hasConstraints = true
});
// Arme (mobile - turret behavior)
soldier.mobileComponents.push_back({
.sprite = SPRITE_RIFLE,
.offset = {4, 2}, // Décalage pour la main
.currentTarget = enemyPosition,
.currentAngle = 0,
.rotationSpeed = 3.0f, // Plus lent que la tête
.minAngle = -PI/4, // Contraintes plus strictes
.maxAngle = PI/4,
.hasConstraints = true
});
// Rendu
soldier.render(soldierPosition, soldierOrientation);
```
### Véhicule avec tourelle
```cpp
EntityRenderer tank;
// Châssis (fixe)
tank.fixedComponents.push_back({
.sprite = SPRITE_TANK_HULL,
.offset = {0, 0},
.rotationOffset = 0
});
// Tourelle principale (mobile - rotation 360°)
tank.mobileComponents.push_back({
.sprite = SPRITE_TANK_TURRET,
.offset = {0, 0},
.currentTarget = targetPosition,
.currentAngle = 0,
.rotationSpeed = 2.0f, // Rotation lente (réaliste)
.hasConstraints = false // Rotation complète
});
// Rendu
tank.render(tankPosition, tankOrientation);
```
## Optimisations
### 1. Composition de sprites (Layering)
**Problème** : Dessiner tous les vêtements séparément = beaucoup de draw calls.
**Solution** : Pré-composer les sprites en cache.
```cpp
class SpriteComposer {
std::unordered_map<CompositionKey, SpriteID> compositionCache;
SpriteID getComposedSprite(const std::vector<LayerID>& layers) {
CompositionKey key = hashLayers(layers);
if (compositionCache.contains(key)) {
return compositionCache[key];
}
// Composer les sprites
SpriteID composed = renderToTexture(layers);
compositionCache[key] = composed;
return composed;
}
};
```
**Exemple** :
```cpp
// Composition d'un soldat
std::vector<LayerID> soldierLayers = {
SPRITE_BODY_BASE,
SPRITE_PANTS_CAMO,
SPRITE_JACKET_TACTICAL,
SPRITE_VEST_ARMOR
};
SpriteID composedBody = composer.getComposedSprite(soldierLayers);
```
**Avantages** :
- Réduction massive des draw calls
- Cache persistant entre frames
- Mise à jour uniquement quand les vêtements changent
### 2. Sprite Batching
Grouper les sprites par texture pour minimiser les changements d'état GPU :
```cpp
// Trier par texture avant de dessiner
std::sort(entities.begin(), entities.end(), [](const Entity& a, const Entity& b) {
return a.getTextureID() < b.getTextureID();
});
// Dessiner tous les sprites de la même texture ensemble
for (const Entity& entity : entities) {
entity.render();
}
```
### 3. Culling
Ne dessiner que les entités visibles à l'écran :
```cpp
void renderEntities(const Camera& camera) {
for (Entity& entity : entities) {
if (camera.isVisible(entity.getBounds())) {
entity.render();
}
}
}
```
## Système de couches (Layering)
### Ordre de rendu (Z-order)
Les sprites doivent être dessinés dans un ordre précis pour éviter les problèmes de superposition :
```
Z-Order (du plus bas au plus haut)
├── 0: Corps/Châssis (fixe)
├── 1: Tête/Tourelle (mobile)
└── 2: Arme (mobile)
```
**Implémentation** :
```cpp
struct RenderableComponent {
SpriteID sprite;
Vector2 position;
float rotation;
int zOrder; // Ordre de rendu
};
void renderEntity(const Entity& entity) {
std::vector<RenderableComponent> renderables;
// Collecter tous les composants
collectFixedComponents(entity, renderables);
collectMobileComponents(entity, renderables);
// Trier par Z-order
std::sort(renderables.begin(), renderables.end(),
[](const RenderableComponent& a, const RenderableComponent& b) {
return a.zOrder < b.zOrder;
});
// Dessiner dans l'ordre
for (const RenderableComponent& comp : renderables) {
drawSprite(comp.sprite, comp.position, comp.rotation);
}
}
```
## Gestion des orientations
### Angles et conventions
**Convention** : Angle 0 = direction droite (East), rotation anti-horaire.
```
N (π/2)
|
W (π) --+-- E (0)
|
S (3π/2)
```
### Calcul de l'angle vers une cible
```cpp
float calculateAngleToTarget(Vector2 entityPos, Vector2 componentOffset, Vector2 target) {
// Position mondiale du composant
Vector2 worldPos = entityPos + componentOffset;
// Direction vers la cible
Vector2 direction = target - worldPos;
// Angle en radians
return std::atan2(direction.y, direction.x);
}
```
### Interpolation d'angle (smooth rotation)
Pour éviter les rotations brusques, on interpole vers l'angle cible :
```cpp
float lerpAngle(float current, float target, float speed, float deltaTime) {
// Normaliser les angles entre -π et π
float diff = normalizeAngle(target - current);
// Calculer le changement maximum pour cette frame
float maxChange = speed * deltaTime;
// Appliquer le changement (limité par la vitesse)
float change = std::clamp(diff, -maxChange, maxChange);
return current + change;
}
float normalizeAngle(float angle) {
while (angle > PI) angle -= 2 * PI;
while (angle < -PI) angle += 2 * PI;
return angle;
}
```
## Extensions futures
### 1. Animations
Ajouter un système d'animation pour les sprites :
```cpp
struct AnimatedComponent {
std::vector<SpriteID> frames;
float frameDuration;
float currentTime;
SpriteID getCurrentFrame() {
int frameIndex = (int)(currentTime / frameDuration) % frames.size();
return frames[frameIndex];
}
void update(float deltaTime) {
currentTime += deltaTime;
}
};
```
### 2. Effets visuels
Ajouter des effets visuels aux composants :
```cpp
struct VisualEffect {
Color tint; // Couleur de teinte
float opacity; // Transparence
Vector2 scale; // Échelle
bool flipX, flipY; // Miroirs
};
```
### 3. Attachments dynamiques
Support pour des composants attachés dynamiquement :
```cpp
class AttachmentSystem {
void attachComponent(EntityID entity, MobileComponent component, const std::string& attachPoint);
void detachComponent(EntityID entity, const std::string& attachPoint);
};
// Exemple : attacher une tourelle sur un point de montage
attachments.attachComponent(vehicleID, turretComponent, "mount_point_1");
```
## Performances
### Métriques cibles
- **Entités à l'écran** : 500-1000 personnages/véhicules simultanés
- **Draw calls** : < 100 par frame (via batching)
- **FPS cible** : 60 fps (16.67ms par frame)
- **Temps de rendu** : < 8ms par frame (50% du budget)
### Profiling
Points de mesure critiques :
```cpp
void renderAll() {
PROFILE_SCOPE("Entity Rendering");
{
PROFILE_SCOPE("Sprite Composition");
updateComposedSprites();
}
{
PROFILE_SCOPE("Culling");
cullEntities(camera);
}
{
PROFILE_SCOPE("Sorting");
sortByTexture(visibleEntities);
}
{
PROFILE_SCOPE("Drawing");
drawEntities(visibleEntities);
}
}
```
## Intégration avec les autres systèmes
### Lien avec le système militaire
Le système de rendu est découplé des systèmes de combat :
- **Combat System** calcule les positions, rotations, cibles
- **Rendering System** lit ces données et dessine les sprites
```cpp
// Dans le module Combat
void updateCombat(Entity& entity, float deltaTime) {
// Calculer la cible
Vector2 target = findClosestEnemy(entity.position);
entity.setTarget(target);
// Le rendering system lira cette cible plus tard
}
// Dans le module Rendering
void render(const Entity& entity) {
// Lire la cible depuis l'entité
Vector2 target = entity.getTarget();
// Dessiner avec la cible
entityRenderer.render(entity.position, entity.orientation, target);
}
```
### Lien avec le système de pathfinding
Le système de rendu utilise les positions calculées par le pathfinding :
```cpp
// Pathfinding calcule la position
Vector2 newPosition = pathfinding.getNextPosition(entity);
entity.setPosition(newPosition);
// Rendering dessine à cette position
render(entity);
```
---
**Statut** : ✅ **DESIGN COMPLETE**
**Prochaines étapes** :
1. Implémenter `EntityRenderer` avec la pipeline fixe/mobile
2. Créer `SpriteComposer` pour la composition de sprites
3. Intégrer avec le système de combat pour les cibles
4. Optimiser avec batching et culling

View File

@ -8,9 +8,24 @@ Le système de calcul de menace évalue la **dangerosité qu'une entité représ
- **Contextuel** : Menace calculée selon capacités **des deux parties**
- **Asymétrique** : A menace B ≠ B menace A
- **Sword & Shield** : Qualité défenses peut annuler supériorité numérique
- **Sword & Shield** : Qualité défenses réduit menace, jamais totalement
- **Baseline 20%** : Toute arme reste dangereuse à minimum 20% effectiveness
- **Production** : Capacité industrielle = menace future
- **Multi-domaines** : Terre, air, mer évalués séparément puis agrégés
- **Perte coûteuse** : Perdre unité chère = désastre stratégique même avec victoire
### Distinction Menace vs Victoire Tactique
**CRITIQUE** : La menace mesure la **capacité de nuisance stratégique**, pas l'issue du combat tactique.
**Exemple** : 20 tanks modernes peuvent être détruits par saturation, MAIS avant destruction ils peuvent :
- Percer défenses et atteindre objectifs profonds
- Détruire infrastructure critique (QG, dépôts, ponts)
- Infliger pertes massives
- Forcer mobilisation coûteuse
→ Menace élevée même si tanks finalement détruits
→ Perte d'un Leopard 2A7 (8M€) = toujours désastre stratégique
## Échelle de Menace
@ -56,12 +71,12 @@ struct ThreatCalculationParams {
## Évaluation Forces Actuelles
### Principe : Sword & Shield
### Principe : Sword & Shield avec Effectiveness Float
Pour chaque domaine (terre, air, mer), on évalue :
1. **Sword** (attaquant) : Capacités offensives
2. **Shield** (défenseur) : Capacités défensives
3. **Effectiveness** : Dans quelle mesure le shield arrête le sword
3. **Effectiveness** : Ratio pénétration/armure avec baseline 20% minimum
```cpp
int evaluateCurrentForces(Entity attacker, Entity defender) {
@ -73,9 +88,95 @@ int evaluateCurrentForces(Entity attacker, Entity defender) {
}
```
### Évaluation Domaine Terrestre
## Système Armor vs Penetration
#### Inventaire Forces
### Référence de Base
Chaque équipement possède des valeurs mesurables :
```cpp
struct Tank {
float armor_average; // Moyenne armure (mm RHA equivalent)
int combat_value; // Valeur au combat
int quantity;
// Exemple Leopard 2A7:
// - Frontal: 950mm RHA
// - Flancs: 200mm RHA
// - Arrière: 100mm RHA
// - Average (pondéré surface): ~400mm
};
struct ATWeapon {
float penetration; // Pénétration max (mm RHA)
int combat_value;
int quantity;
// Exemples:
// - RPG-7: 260mm
// - Milan Gen2: 530mm
// - Javelin Gen3 (top-attack): 800mm
// - Kornet Gen4+ (tandem): 1200mm
};
```
### Calcul Effectiveness (Float)
**Formule baseline 20%** : Toute arme reste dangereuse (flancs, arrière, chance)
```cpp
float calculateCounterEffectiveness(ATWeapon weapon, Tank tank) {
float pen_ratio = weapon.penetration / tank.armor_average;
// BASELINE 20% : TOUJOURS dangereux
const float BASE_EFFECTIVENESS = 0.20f;
if (pen_ratio >= 1.0f) {
// Arme peut percer : scaling jusqu'à 90% max
float bonus = (pen_ratio - 1.0f) * 0.5f;
return min(0.90f, BASE_EFFECTIVENESS + bonus);
}
else {
// Arme sous-dimensionnée : reste à 20% (flancs, arrière, chance)
return BASE_EFFECTIVENESS;
}
}
```
### Exemples Effectiveness
```cpp
// Leopard 2A7 (armor_avg = 400mm) vs différentes armes
RPG-7 (260mm) :
pen_ratio = 260 / 400 = 0.65
effectiveness = 20%
→ 100 RPG-7 needed = 20 "équivalents kill"
→ Coût: 100 × 500€ = 50k€ vs Leopard 8M€ = 0.625% ratio
→ Menace présente mais faible, économiquement viable en masse
Milan Gen2 (530mm) :
pen_ratio = 530 / 400 = 1.325
effectiveness = 20% + (0.325 × 0.5) = 36.25%
→ ~3 missiles = 1 kill probable
→ Menace sérieuse
Javelin Gen3 (800mm top-attack) :
pen_ratio = 800 / 400 = 2.0
effectiveness = 20% + (1.0 × 0.5) = 70%
→ ~1.5 missiles = 1 kill probable
→ Menace très élevée
Kornet Gen4+ (1200mm tandem) :
pen_ratio = 1200 / 400 = 3.0
effectiveness = 20% + (2.0 × 0.5) = 90% (plafonné)
→ ~1.1 missiles = 1 kill quasi-certain
→ Menace maximale
```
## Évaluation Domaine Terrestre
### Inventaire Forces
**Attaquant** :
```cpp
@ -98,7 +199,7 @@ struct LandDefenses {
};
```
#### Couples Équipement ↔ Contre-mesures
### Couples Équipement ↔ Contre-mesures
**Principe** : Chaque type d'équipement offensif a des contre-mesures spécifiques.
@ -114,50 +215,7 @@ struct EquipmentCounters {
};
```
#### Évaluation Sword vs Shield
**Étape 1 : Match-making**
Pour chaque équipement offensif, identifier contre-mesures défensives applicables :
```cpp
std::vector<Defense> findApplicableDefenses(
Equipment sword,
std::vector<Defense> available_defenses
) {
std::vector<Defense> applicable;
auto counters = EquipmentCounters::counters[sword.type];
for (auto& defense : available_defenses) {
// Vérifier si défense peut contrer cet équipement
if (std::find(counters.begin(), counters.end(), defense.type) != counters.end()) {
// Vérifier compatibilité génération/qualité
if (canCounter(defense, sword)) {
applicable.push_back(defense);
}
}
}
return applicable;
}
bool canCounter(Defense defense, Equipment sword) {
// Exemple : AT missile Gen2 ne peut pas contrer tank Gen4 avec armure avancée
if (defense.generation < sword.generation - 1) {
return false; // Trop ancien
}
// Vérifier spécificités
if (sword.has_reactive_armor && defense.type == AT_MISSILE_OLD) {
return false; // ERA bloque missiles anciens
}
return true;
}
```
**Étape 2 : Calcul Defensive Effectiveness**
### Calcul Defensive Effectiveness
```cpp
float calculateDefensiveEffectiveness(
@ -168,7 +226,7 @@ float calculateDefensiveEffectiveness(
float neutralized_value = 0;
for (auto& sword : swords) {
float sword_value = sword.quantity * sword.quality;
float sword_value = sword.quantity * sword.combat_value;
total_sword_value += sword_value;
// Trouver défenses applicables
@ -177,8 +235,9 @@ float calculateDefensiveEffectiveness(
// Calculer valeur défensive totale contre ce sword
float shield_value = 0;
for (auto& shield : applicable_shields) {
float shield_effectiveness = calculateShieldEffectiveness(shield, sword);
shield_value += shield.quantity * shield.quality * shield_effectiveness;
// ✅ EFFECTIVENESS FLOAT (20%-90%)
float effectiveness = calculateCounterEffectiveness(shield, sword);
shield_value += shield.quantity * shield.combat_value * effectiveness;
}
// Neutralisation proportionnelle
@ -190,7 +249,7 @@ float calculateDefensiveEffectiveness(
}
```
**Étape 3 : Score Final**
### Score Final avec Menace Résiduelle
```cpp
int evaluateLandThreat(Entity attacker, Entity defender) {
@ -206,85 +265,154 @@ int evaluateLandThreat(Entity attacker, Entity defender) {
// Réduction par défenses
float defensive_effectiveness = calculateDefensiveEffectiveness(swords, shields);
// Menace finale
int final_threat = raw_threat * (1.0f - defensive_effectiveness);
// ✅ MENACE RÉSIDUELLE : Jamais totalement nulle si attaquant existe
float residual_multiplier = max(0.05f, 1.0f - defensive_effectiveness);
int final_threat = raw_threat * residual_multiplier;
return final_threat;
}
```
### Exemples Concrets Terre
## Exemples Concrets Terre
#### Exemple 1 : Qualité Domine Quantité
### Exemple 1 : Tech Obsolète Viable en Masse
**Attaquant** :
- 20 tanks Gen4 (Leopard 2A7)
- Armure composite avancée
- Génération 4
- Combat value : 1000/tank
- Menace brute : 20 × 1000 = 20 000
- armor_average : 400mm RHA
- combat_value : 1000/tank
- Menace brute : 20 000
- Valeur totale : 20 × 8M€ = 160M€
**Défenseur** :
- 100 000 AT missiles Gen2
- Anciens, inefficaces contre armure Gen4
- `canCounter()` retourne `false`
- Défense applicable : 0
- 5000 RPG-7 (Gen1, ancien)
- penetration : 260mm
- pen_ratio : 260/400 = 0.65
- effectiveness : 20% (baseline)
- combat_value : 50/RPG
- shield_value : 5000 × 50 × 0.20 = 50 000
- Coût : 5000 × 500€ = 2.5M€
**Résultat** :
```
defensive_effectiveness = 0 / 20000 = 0%
final_threat = 20000 × (1 - 0) = 20 000
```
**Menace élevée** : Défenses obsolètes inefficaces
defensive_effectiveness = min(1.0, 50000 / 20000) = 100%
residual_multiplier = max(0.05, 1.0 - 1.0) = 5%
final_threat = 20000 × 0.05 = 1 000
#### Exemple 2 : Quantité + Qualité Écrasent
→ Menace quasi-neutralisée (5% résiduel)
→ Nécessite 5000 RPG-7 (masse logistique)
→ Ratio coût défenseur/attaquant = 2.5M€ / 160M€ = 1.56%
→ Défense économiquement viable ✅
→ MAIS perte Leopard reste désastre (8M€ + prestige + formation)
```
### Exemple 2 : Tech Moderne Efficace
**Attaquant** :
- 1000 tanks Gen2
- Armure standard
- Combat value : 400/tank
- Menace brute : 1000 × 400 = 400 000
- 20 tanks Gen4 (Leopard 2A7)
- armor_average : 400mm
- Menace brute : 20 000
**Défenseur** :
- 500 AT missiles Gen4 (Javelin)
- Top-attack, très efficaces
- Combat value : 800/missile
- `canCounter()` retourne `true`
- Shield effectiveness : 0.9 (très efficace)
- Shield value : 500 × 800 × 0.9 = 360 000
- 100 missiles Javelin Gen3
- penetration : 800mm (top-attack)
- pen_ratio : 800/400 = 2.0
- effectiveness : 70%
- combat_value : 300/missile
- shield_value : 100 × 300 × 0.70 = 21 000
- Coût : 100 × 200k€ = 20M€
**Résultat** :
```
defensive_effectiveness = 360000 / 400000 = 90%
final_threat = 400000 × (1 - 0.9) = 40 000
```
**Menace faible** : Défenses qualitatives réduisent massavement
defensive_effectiveness = min(1.0, 21000 / 20000) = 100%
residual_multiplier = 5%
final_threat = 20000 × 0.05 = 1 000
#### Exemple 3 : Masse Insuffisante
→ Menace quasi-neutralisée
→ Ratio coût : 20M€ / 160M€ = 12.5%
→ Plus cher que RPG-7 mais logistique simplifiée (100 vs 5000)
```
### Exemple 3 : Défense Insuffisante
**Attaquant** :
- 1000 tanks Gen2
- Combat value : 400/tank
- Menace brute : 400 000
- 20 tanks Gen4
- Menace brute : 20 000
**Défenseur** :
- 20 AT missiles Gen4
- Très efficaces mais **pas assez nombreux**
- Shield value : 20 × 800 × 0.9 = 14 400
- 500 RPG-7
- effectiveness : 20%
- shield_value : 500 × 50 × 0.20 = 5 000
**Résultat** :
```
defensive_effectiveness = 14400 / 400000 = 3.6%
final_threat = 400000 × (1 - 0.036) = 385 440
defensive_effectiveness = 5000 / 20000 = 25%
residual_multiplier = max(0.05, 0.75) = 75%
final_threat = 20000 × 0.75 = 15 000
→ Menace reste élevée (75%)
→ Défense insuffisante
```
**Menace reste élevée** : Pas assez de défenses pour couvrir la masse
### Évaluation Domaine Aérien
### Exemple 4 : Défense Mixte Économique
#### Spécificités Air
**Attaquant** :
- 20 tanks Gen4
- Menace brute : 20 000
**Défenseur** :
- 30 Javelin Gen3 : shield = 30 × 300 × 0.70 = 6 300
- 1000 RPG-7 : shield = 1000 × 50 × 0.20 = 10 000
- **Shield total** : 16 300
- **Coût total** : 30 × 200k€ + 1000 × 500€ = 6.5M€
**Résultat** :
```
defensive_effectiveness = 16300 / 20000 = 81.5%
residual_multiplier = max(0.05, 0.185) = 18.5%
final_threat = 20000 × 0.185 = 3 700
→ Menace réduite à 18.5% (acceptable)
→ Défense mixte optimise coût-efficacité
→ Ratio coût : 6.5M€ / 160M€ = 4.06%
```
### Exemple 5 : Système de Conception Modulaire - Retrofit Anti-AT
**Attaquant** :
- 20 tanks Leopard 2A7 **retrofittés Gen5**
- Chassis Gen4 base
- **APS Trophy Gen5** : intercepte 80% missiles avant impact
- **ERA Gen5** : neutralise pénétration Gen2-3
- armor_effective : 400mm × 2.5 (ERA bonus) = 1000mm equivalent
- Menace brute : 20 000
**Défenseur** :
- 1000 missiles Milan Gen2
- penetration : 530mm
- pen_ratio : 530/1000 = 0.53 (sous-dimensionné avec ERA)
- effectiveness : 20% (baseline)
- **APS reduction** : 20% × (1 - 0.80) = 4% effectiveness finale
- shield_value : 1000 × 300 × 0.04 = 12 000
**Résultat** :
```
defensive_effectiveness = 12000 / 20000 = 60%
residual_multiplier = max(0.05, 0.40) = 40%
final_threat = 20000 × 0.40 = 8 000
→ Menace reste significative (40%)
→ APS + ERA réduisent drastiquement effectiveness Gen2
→ Système de conception permet adaptation doctrine
→ Joueur peut designer tanks spécifiquement anti-saturation
```
## Évaluation Domaine Aérien
### Spécificités Air
**Complexité supplémentaire** :
- **Qualité prime** : Quelques jets furtifs Gen4 dominent des centaines AA Gen2
- **Qualité prime** : Furtivité et ECM dominent
- **ECM/ECCM** : Guerre électronique critique
- **Compatibilité stricte** : AA anti-hélicoptère ne touche pas jets
@ -309,7 +437,7 @@ struct AirDefenses {
// Capacités
bool has_eccm; // Electronic Counter-Counter-Measures
int radar_quality;
std::map<AASystem, TargetCapability> capabilities; // Quoi peut toucher quoi
std::map<AASystem, TargetCapability> capabilities;
};
enum TargetCapability {
@ -320,159 +448,69 @@ enum TargetCapability {
};
```
#### Compatibilité Systèmes
### Effectiveness Air avec Stealth/ECM
```cpp
bool canEngageAircraft(AASystem aa, Aircraft aircraft) {
// Vérifier compatibilité type
switch (aa.capability) {
case HELICOPTER_ONLY:
return aircraft.type == HELICOPTER;
float calculateAirDefenseEffectiveness(Aircraft aircraft, AASystem aa) {
// Base effectiveness selon pénétration radar vs stealth
float base_eff = calculateCounterEffectiveness(aa, aircraft);
case LOW_ALTITUDE:
return aircraft.altitude < 5000; // mètres
case HIGH_ALTITUDE:
return aircraft.altitude > 5000;
case ALL_AIRCRAFT:
return true;
// Multiplicateurs tech
float stealth_reduction = 1.0f;
if (aircraft.has_stealth && !aa.has_advanced_radar) {
stealth_reduction = 1.0f - aircraft.stealth_rating; // Ex: 0.1 si 90% stealth
}
// Vérifier guerre électronique
float ecm_reduction = 1.0f;
if (aircraft.has_ecm && !aa.has_eccm) {
// ECM peut brouiller AA sans ECCM
float jam_probability = aircraft.ecm_power / (aa.radar_quality + 1);
if (randomFloat() < jam_probability) {
return false; // Brouillé
}
ecm_reduction = 1.0f - (aircraft.ecm_power / (aa.radar_quality + 1));
}
// Vérifier furtivité
if (aircraft.has_stealth) {
float detection_range = aa.radar_range * (1.0f - aircraft.stealth_rating);
if (distance(aa, aircraft) > detection_range) {
return false; // Pas détecté
}
}
// Effectiveness finale (baseline 20% toujours appliqué)
float final_eff = max(0.20f, base_eff * stealth_reduction * ecm_reduction);
return true;
return final_eff;
}
```
#### Exemple Air : Furtivité Domine
### Exemple Air : Furtivité + ECM
**Attaquant** :
- 20 jets furtifs Gen4 (F-35)
- Stealth rating : 0.9 (réduit détection 90%)
- ECM avancé
- Combat value : 2000/jet
- stealth_rating : 0.90 (réduit détection 90%)
- ecm_power : 8
- combat_value : 2000/jet
- Menace brute : 40 000
**Défenseur** :
- 100 000 AA missiles Gen2
- Radar standard
- Pas ECCM
- Seulement 5% peuvent détecter/engager les furtifs
- Shield value effectif : 100000 × 0.05 × 300 = 1 500 000... mais avec ECM :
- Shield value final : 1 500 000 × 0.1 (jam rate) = 150 000
- 1000 missiles AA Gen3 (Patriot)
- radar_quality : 6
- has_eccm : false
- base_effectiveness : 70% (tech Gen3 vs Gen4)
- stealth_reduction : 1.0 - 0.90 = 0.10
- ecm_reduction : 1.0 - (8/7) = -0.14 → 0.0 (plancher)
- final_effectiveness : max(0.20, 0.70 × 0.10 × 0.0) = 20% (baseline)
- shield_value : 1000 × 400 × 0.20 = 80 000
**Résultat** :
```
defensive_effectiveness = 150000 / 40000 = ... wait, > 1.0 !
→ Plafonné à min(1.0, value)
defensive_effectiveness = min(1.0, 150000/40000) = 1.0...
defensive_effectiveness = min(1.0, 80000 / 40000) = 100%
residual_multiplier = 5%
final_threat = 40000 × 0.05 = 2 000
ERREUR dans mon calcul !
→ Furtivité + ECM ramènent effectiveness au baseline 20%
→ Nécessite masse importante (1000 missiles) pour compenser
→ F-35 peuvent accomplir raid avant saturation (menace 5% résiduelle)
```
**Correction** : Le shield value doit être calculé par aircraft, pas globalement.
```cpp
// Pour chaque jet
for (auto& jet : jets) {
float jet_value = jet.combat_value;
// Combien de AA peuvent l'engager ?
int applicable_aa = 0;
for (auto& aa : aa_systems) {
if (canEngageAircraft(aa, jet)) {
applicable_aa++;
}
}
// Shield value pour ce jet spécifique
float shield_value = applicable_aa * aa_combat_value;
float neutralization = min(1.0f, shield_value / jet_value);
threat_reduced += jet_value * neutralization;
}
```
**Résultat corrigé** :
- 5000 AA peuvent engager (sur 100k)
- Pour 1 jet : shield = 5000 × 300 = 1 500 000 vs jet = 2000
- Neutralization = 100% par jet
- Mais ils peuvent pas tous tirer simultanément !
**Contrainte simultanéité** :
```cpp
// Limite engagement simultané
int max_simultaneous = min(applicable_aa, ENGAGEMENT_LIMIT);
// Exemple : Max 100 missiles simultanés par cible
float shield_value = max_simultaneous * aa_combat_value;
```
Avec limite 100 simultanés :
- Shield = 100 × 300 = 30 000 vs jet = 2000
- Neutralization = 100% par jet
- Mais 20 jets → seulement 2000 engagements simultanés total
- Si AA rate coordination : menace reste
**C'est complexe !** Donc en pratique :
```cpp
float calculateAirDefenseEffectiveness(
std::vector<Aircraft> aircraft,
std::vector<AASystem> aa_systems
) {
float total_threat = 0;
float neutralized = 0;
for (auto& ac : aircraft) {
total_threat += ac.combat_value;
// Compter AA applicables
int applicable_count = 0;
for (auto& aa : aa_systems) {
if (canEngageAircraft(aa, ac)) {
applicable_count++;
}
}
// Engagement limité
int engaged = min(applicable_count, MAX_SIMULTANEOUS_PER_TARGET);
// Probabilité kill
float kill_probability = engaged / float(ENGAGEMENTS_NEEDED_FOR_KILL);
kill_probability = min(1.0f, kill_probability);
neutralized += ac.combat_value * kill_probability;
}
return total_threat > 0 ? (neutralized / total_threat) : 0.0f;
}
```
### Évaluation Domaine Naval
## Évaluation Domaine Naval
**Similaire à terrestre/aérien** mais avec spécificités :
- **Torpilles vs sonars** (submersibles)
- **Anti-ship missiles vs CIWS** (Close-In Weapon Systems)
- **Portée extrême** : Naval combat à 100+ km
Structure identique avec couples spécifiques.
Structure identique avec couples spécifiques (penetration vs armor pour missiles anti-navire).
## Évaluation Production
@ -520,33 +558,20 @@ int evaluateProduction(Entity attacker, Entity defender, int projection_months)
- Production AT : 100 missiles/mois
- Projection 12 mois : 1200 missiles
- Combat value : 300/missile
- Défense production : 1200 × 300 = 360 000
- Effectiveness moyenne : 40%
- Défense production : 1200 × 300 × 0.40 = 144 000
**Net production threat** :
```
Attacker gain : 240 000
Defender gain : 360 000
Net : 240 000 - 360 000 = -120 000 (négatif = défenseur gagne)
Defender gain : 144 000
Net : 240 000 - 144 000 = 96 000
production_threat = max(0, net) = 0
production_threat = 96 000
→ État A a avantage production malgré défenses
```
→ État B a **meilleure production**, donc menace production de A est nulle.
**Si inverse** :
**État A** :
- Production : 200 tanks/mois → 2400 tanks/an
- Threat : 2400 × 400 = 960 000
**État B** :
- Production AT : 50 missiles/mois → 600 missiles/an
- Défense : 600 × 300 = 180 000
**Net** : 960 000 - 180 000 = 780 000
→ État A a **production écrasante**, menace industrielle massive.
## Agrégation Finale
### Formule Complète
@ -590,15 +615,15 @@ int calculateThreat(Entity attacker, Entity defender) {
- **Total brut** : 9 700 000
**France défenses** :
- AT systems : 3000 (Gen3) → Shield 50% tanks
- AA systems : 1500 (Gen3) → Shield 30% aircraft
- Naval defenses : 200 → Shield 40% naval
- AT systems : 3000 (Gen3, eff 60%) → Shield 60% tanks
- AA systems : 1500 (Gen3, eff 40% vs furtifs) → Shield 40% aircraft
- Naval defenses : 200 (eff 50%) → Shield 50% naval
**Après sword & shield** :
- Land : 3 200 000 × 0.5 = 1 600 000
- Air : 4 000 000 × 0.7 = 2 800 000
- Naval : 2 500 000 × 0.6 = 1 500 000
- **Current threat** : 5 900 000
- Land : 3 200 000 × max(0.05, 0.40) = 1 280 000
- Air : 4 000 000 × max(0.05, 0.60) = 2 400 000
- Naval : 2 500 000 × max(0.05, 0.50) = 1 250 000
- **Current threat** : 4 930 000
**Production (12 mois)** :
- USA : +600 tanks, +100 aircraft → 360 000
@ -607,10 +632,10 @@ int calculateThreat(Entity attacker, Entity defender) {
**Total (60% current, 40% prod)** :
```
5 900 000 × 0.6 + 290 000 × 0.4 = 3 656 000
```
4 930 000 × 0.6 + 290 000 × 0.4 = 3 074 000
**Menace colossale**, mais France peut tenir avec alliances
→ Menace colossale, mais France peut tenir avec alliances
```
#### PMC vs Company
@ -622,13 +647,17 @@ int calculateThreat(Entity attacker, Entity defender) {
- **Total** : 50 000
**Rheinmetall défenses** :
- Security : 100 guards
- Security : 100 guards (effectiveness 30%)
- Fortifications : minimal
- **Shield** : 5%
- **Shield** : 15%
**Threat** : 50 000 × 0.95 = 47 500
**Threat** :
```
residual = max(0.05, 1.0 - 0.15) = 85%
final_threat = 50000 × 0.85 = 42 500
→ PMC peut menacer installations Company, mais pas capacités production
→ PMC peut menacer installations Company
```
## Cas Spéciaux
@ -680,6 +709,66 @@ float getGeographicModifier(Entity attacker, Entity defender) {
}
```
## Implications Stratégiques
### 1. Coût-Efficacité Défensive
```cpp
struct DefenseCostAnalysis {
float threat_neutralized;
float cost;
float cost_efficiency; // menace neutralisée par €
};
// Neutraliser 20 Leopard 2A7 (160M€ total)
Option A - RPG-7 masse (effectiveness 20%) :
- Quantité nécessaire : 100 RPG × 20 tanks = 2000 RPG
- Coût : 2000 × 500€ = 1M€
- Ratio : 1M€ / 160M€ = 0.625%
- Avantage : Très économique
- Inconvénient : Logistique massive
Option B - Javelin Gen3 (effectiveness 70%) :
- Quantité nécessaire : 1.5 Javelin × 20 tanks = 30 Javelin
- Coût : 30 × 200k€ = 6M€
- Ratio : 6M€ / 160M€ = 3.75%
- Avantage : Logistique légère
- Inconvénient : Plus cher
→ RPG-7 est 6× plus cost-efficient, mais logistique 67× plus lourde
→ Choix stratégique selon capacités logistiques
```
### 2. Perte Asymétrique
```cpp
// Exemple bataille : Menace neutralisée mais pertes coûteuses
Attaquant : 20 Leopard 2A7 (160M€)
Défenseur : 5000 RPG-7 (2.5M€)
Outcome tactique :
- Tous Leopards détruits (saturation)
- 2000 RPG-7 utilisés effectivement
Bilan stratégique :
Attaquant pertes :
- 160M€ équipement
- 20 crews élites (formation coûteuse)
- Prestige international
- Capacités offensives réduites
Défenseur pertes :
- 1M€ munitions consommées (2000 RPG effectifs)
- +Victoire propagande
- +Moral défensif
→ Victoire tactique défenseur = victoire stratégique
→ Système menace reflète cette asymétrie via residual 5%
→ Perdre Leopard = toujours désastre même si menace "neutralisée"
```
## Performance et Optimisation
### Cache Threat
@ -732,6 +821,7 @@ public:
- Perte unité au combat
- Nouvelle recherche technologique
- Changement doctrine militaire
- Retrofit/upgrade équipement
### Calcul Lazy
@ -748,5 +838,5 @@ int getThreatOnDemand(EntityId attacker, EntityId defender) {
- `systeme-diplomatique.md` : Utilisation menace dans relations diplomatiques
- `ai-framework.md` : Menace influence décisions IA (achats, alliances)
- `systeme-militaire.md` : Valeurs combat équipements, générations
- `systeme-militaire.md` : Valeurs combat équipements, générations, système conception modulaire
- `economie-logistique.md` : Production rates, capacités industrielles

View File

@ -1,30 +0,0 @@
#pragma once
#include <nlohmann/json.hpp>
#include <string>
using json = nlohmann::json;
namespace warfactory {
class SerializationRegistry;
class ASerializable {
private:
std::string instance_id;
public:
ASerializable(const std::string& id);
virtual ~ASerializable();
const std::string& getInstanceId() const { return instance_id; }
virtual json serialize() const = 0;
virtual void deserialize(const json& data) = 0;
protected:
void registerForSerialization();
void unregisterFromSerialization();
};
} // namespace warfactory

View File

@ -1,23 +0,0 @@
#pragma once
#include <string>
#include <memory>
#include "IDataTree.h"
namespace warfactory {
/**
* @brief Factory for creating data tree instances
*/
class DataTreeFactory {
public:
/**
* @brief Create data tree from configuration source
* @param type Tree type ("json", "database", etc.)
* @param sourcePath Path to configuration source
* @return Data tree instance
*/
static std::unique_ptr<IDataTree> create(const std::string& type, const std::string& sourcePath);
};
} // namespace warfactory

View File

@ -1,161 +0,0 @@
#pragma once
#include <string>
#include <memory>
#include <vector>
#include <functional>
#include "IModule.h"
#include "IDataTree.h"
// Forward declarations
namespace warfactory {
class IEngine;
class IModuleSystem;
}
namespace warfactory {
/**
* @brief Global system orchestrator - First launched, last shutdown
*
* The CoordinationModule is the main system orchestrator that manages the entire
* game system lifecycle, module deployment topology, and configuration synchronization.
*
* ARCHITECTURE FLOW:
* 1. MainServer launches CoordinationModule (first module)
* 2. CoordinationModule loads gameconfig.json via IDataTree
* 3. Parses deployment section to determine module topology
* 4. Deploys modules to local IEngine or remote servers
* 5. Synchronizes configuration across all deployed modules
* 6. Coordinates shutdown (last module to close)
*
* DESIGN DECISIONS:
* - No state persistence: behavior driven entirely by gameconfig.json
* - No network protocol: all communication via IIO abstraction
* - No security for now: local/trusted environment assumed
* - Module deployment via IModuleSystem delegation
* - Configuration immutability via const IDataNode references
*/
class ICoordinationModule : public IModule {
public:
virtual ~ICoordinationModule() = default;
// ========================================
// GAME LIFECYCLE MANAGEMENT
// ========================================
/**
* @brief Start new game session with configuration
* @param gameConfigPath Path to gameconfig.json file
*
* Complete startup sequence:
* 1. Load and parse gameconfig.json via IDataTree
* 2. Initialize local IEngine and IModuleSystem
* 3. Parse deployment topology from config
* 4. Deploy local modules (target: "local")
* 5. Launch remote servers and deploy remote modules
* 6. Synchronize all configurations
* 7. Return when system is ready
*/
virtual void startNewGame(const std::string& gameConfigPath) = 0;
/**
* @brief Load existing game from save file
* @param savePath Path to save file
*/
virtual void loadGame(const std::string& savePath) = 0;
/**
* @brief Shutdown entire game system gracefully
*
* Coordinates graceful shutdown:
* 1. Signal all modules to save state
* 2. Undeploy remote modules first
* 3. Undeploy local modules
* 4. Shutdown remote servers
* 5. Shutdown local IEngine
* 6. CoordinationModule shuts down last
*/
virtual void shutdownGame() = 0;
// ========================================
// MODULE DEPLOYMENT TOPOLOGY
// ========================================
/**
* @brief Deploy module according to gameconfig.json specification
* @param moduleInstanceId Module instance ID as defined in config
*
* Deployment process:
* 1. Read module config from gameconfig.json deployment section
* 2. Determine target: "local" vs "server:IP" vs "cluster:name"
* 3. Get module-specific configuration from modules section
* 4. For local: delegate to local IEngine->IModuleSystem
* 5. For remote: send deployment command to remote server
* 6. Pass const IDataNode& configuration to module
*/
virtual void deployModule(const std::string& moduleInstanceId) = 0;
/**
* @brief Stop and undeploy module instance
* @param moduleInstanceId Module instance ID to undeploy
*/
virtual void undeployModule(const std::string& moduleInstanceId) = 0;
/**
* @brief Get list of currently deployed module instances
* @return Vector of module instance IDs currently running
*/
virtual std::vector<std::string> getDeployedModules() = 0;
// ========================================
// CONFIGURATION SYNCHRONIZATION
// ========================================
/**
* @brief Synchronize configuration changes to all deployed modules
*
* Process:
* 1. Reload gameconfig.json via IDataTree hot-reload
* 2. For each deployed module, get updated configuration
* 3. Call module->setConfiguration() with new const IDataNode&
* 4. Handle any modules that fail to reconfigure
*/
virtual void syncConfiguration() = 0;
/**
* @brief Set configuration tree for the coordination system
* @param configTree Configuration data tree loaded from gameconfig.json
*/
virtual void setConfigurationTree(std::unique_ptr<IDataTree> configTree) = 0;
/**
* @brief Get current configuration tree
* @return Configuration tree pointer for accessing gameconfig.json data
*/
virtual IDataTree* getConfigurationTree() = 0;
// ========================================
// SYSTEM HEALTH AND MANAGEMENT
// ========================================
/**
* @brief Check if all deployed modules are healthy and responsive
* @return true if system is healthy, false if issues detected
*
* Aggregates health status from all deployed modules:
* - Calls getHealthStatus() on each module
* - Checks network connectivity to remote servers
* - Validates configuration consistency
* - Could trigger auto-save in future versions
*/
virtual bool isSystemHealthy() = 0;
/**
* @brief Get detailed system health report
* @return JSON health report aggregating all module health status
*/
virtual json getSystemHealthReport() = 0;
};
} // namespace warfactory

View File

@ -1,233 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <functional>
#include <nlohmann/json.hpp>
namespace warfactory {
using json = nlohmann::json;
/**
* @brief Interface for a single node in the data tree
*
* Each node can have:
* - Children nodes (tree navigation)
* - Its own data blob (JSON)
* - Properties accessible by name with type safety
*/
class IDataNode {
public:
virtual ~IDataNode() = default;
// ========================================
// TREE NAVIGATION
// ========================================
/**
* @brief Get direct child by name
* @param name Exact name of the child
* @return Child node or nullptr if not found
*/
virtual std::unique_ptr<IDataNode> getChild(const std::string& name) = 0;
/**
* @brief Get names of all direct children
* @return Vector of child names
*/
virtual std::vector<std::string> getChildNames() = 0;
/**
* @brief Check if this node has any children
* @return true if children exist
*/
virtual bool hasChildren() = 0;
// ========================================
// EXACT SEARCH IN CHILDREN
// ========================================
/**
* @brief Find all children with exact name (direct children only)
* @param name Exact name to search for
* @return Vector of matching child nodes
*/
virtual std::vector<IDataNode*> getChildrenByName(const std::string& name) = 0;
/**
* @brief Check if any children have the exact name
* @param name Exact name to search for
* @return true if found
*/
virtual bool hasChildrenByName(const std::string& name) const = 0;
/**
* @brief Get first child with exact name
* @param name Exact name to search for
* @return First matching child or nullptr
*/
virtual IDataNode* getFirstChildByName(const std::string& name) = 0;
// ========================================
// PATTERN MATCHING SEARCH (DEEP SEARCH IN WHOLE SUBTREE)
// ========================================
/**
* @brief Find all nodes in subtree matching pattern
* @param pattern Pattern with wildcards (* supported)
* @return Vector of matching nodes in entire subtree
*
* Examples:
* - "component*" matches "component_armor", "component_engine"
* - "*heavy*" matches "tank_heavy_mk1", "artillery_heavy"
* - "model_*" matches "model_01", "model_02"
*/
virtual std::vector<IDataNode*> getChildrenByNameMatch(const std::string& pattern) = 0;
/**
* @brief Check if any nodes in subtree match pattern
* @param pattern Pattern with wildcards
* @return true if any matches found
*/
virtual bool hasChildrenByNameMatch(const std::string& pattern) const = 0;
/**
* @brief Get first node in subtree matching pattern
* @param pattern Pattern with wildcards
* @return First matching node or nullptr
*/
virtual IDataNode* getFirstChildByNameMatch(const std::string& pattern) = 0;
// ========================================
// QUERY BY PROPERTIES
// ========================================
/**
* @brief Query nodes in subtree by property value
* @param propName Property name to check
* @param predicate Function to test property value
* @return Vector of nodes where predicate returns true
*
* Example:
* // Find all tanks with armor > 150
* queryByProperty("armor", [](const json& val) {
* return val.is_number() && val.get<int>() > 150;
* });
*/
virtual std::vector<IDataNode*> queryByProperty(const std::string& propName,
const std::function<bool(const json&)>& predicate) = 0;
// ========================================
// NODE'S OWN DATA
// ========================================
/**
* @brief Get this node's data blob
* @return JSON data or empty JSON if no data
*/
virtual json getData() const = 0;
/**
* @brief Check if this node has data
* @return true if data exists
*/
virtual bool hasData() const = 0;
/**
* @brief Set this node's data
* @param data JSON data to set
*/
virtual void setData(const json& data) = 0;
// ========================================
// TYPED DATA ACCESS BY PROPERTY NAME
// ========================================
/**
* @brief Get string property from this node's data
* @param name Property name
* @param defaultValue Default if property not found or wrong type
* @return Property value or default
*/
virtual std::string getString(const std::string& name, const std::string& defaultValue = "") const = 0;
/**
* @brief Get integer property from this node's data
* @param name Property name
* @param defaultValue Default if property not found or wrong type
* @return Property value or default
*/
virtual int getInt(const std::string& name, int defaultValue = 0) const = 0;
/**
* @brief Get double property from this node's data
* @param name Property name
* @param defaultValue Default if property not found or wrong type
* @return Property value or default
*/
virtual double getDouble(const std::string& name, double defaultValue = 0.0) const = 0;
/**
* @brief Get boolean property from this node's data
* @param name Property name
* @param defaultValue Default if property not found or wrong type
* @return Property value or default
*/
virtual bool getBool(const std::string& name, bool defaultValue = false) const = 0;
/**
* @brief Check if property exists in this node's data
* @param name Property name
* @return true if property exists
*/
virtual bool hasProperty(const std::string& name) const = 0;
// ========================================
// HASH SYSTEM FOR VALIDATION & SYNCHRO
// ========================================
/**
* @brief Get hash of this node's data only
* @return SHA256 hash of data blob
*/
virtual std::string getDataHash() = 0;
/**
* @brief Get recursive hash of this node and all children
* @return SHA256 hash of entire subtree
*/
virtual std::string getTreeHash() = 0;
/**
* @brief Get hash of specific child subtree
* @param childPath Path to child from this node
* @return SHA256 hash of child subtree
*/
virtual std::string getSubtreeHash(const std::string& childPath) = 0;
// ========================================
// METADATA
// ========================================
/**
* @brief Get full path from root to this node
* @return Path string (e.g., "vehicles/tanks/heavy/model5")
*/
virtual std::string getPath() const = 0;
/**
* @brief Get this node's name
* @return Node name
*/
virtual std::string getName() const = 0;
/**
* @brief Get node type (extensible for templates/inheritance later)
* @return Node type identifier
*/
virtual std::string getNodeType() const = 0;
};
} // namespace warfactory

View File

@ -1,69 +0,0 @@
#pragma once
#include <string>
#include <memory>
#include <functional>
#include "IDataNode.h"
namespace warfactory {
/**
* @brief Interface for the root data tree container
*
* Manages the entire tree structure and provides hot-reload capabilities
*/
class IDataTree {
public:
virtual ~IDataTree() = default;
// ========================================
// TREE ACCESS
// ========================================
/**
* @brief Get root node of the tree
* @return Root node
*/
virtual std::unique_ptr<IDataNode> getRoot() = 0;
/**
* @brief Get node by path from root
* @param path Path from root (e.g., "vehicles/tanks/heavy")
* @return Node at path or nullptr if not found
*/
virtual std::unique_ptr<IDataNode> getNode(const std::string& path) = 0;
// ========================================
// MANUAL HOT-RELOAD (SIMPLE & EFFECTIVE)
// ========================================
/**
* @brief Check if source files have changed
* @return true if changes detected
*/
virtual bool checkForChanges() = 0;
/**
* @brief Reload entire tree if changes detected
* @return true if reload was performed
*/
virtual bool reloadIfChanged() = 0;
/**
* @brief Register callback for when tree is reloaded
* @param callback Function called after successful reload
*/
virtual void onTreeReloaded(std::function<void()> callback) = 0;
// ========================================
// METADATA
// ========================================
/**
* @brief Get tree implementation type
* @return Type identifier (e.g., "JSONDataTree", "DatabaseDataTree")
*/
virtual std::string getType() = 0;
};
} // namespace warfactory

View File

@ -1,125 +0,0 @@
#pragma once
#include <string>
#include <memory>
#include <nlohmann/json.hpp>
// Forward declarations to avoid circular dependencies
namespace warfactory {
class IModuleSystem;
}
using json = nlohmann::json;
namespace warfactory {
enum class EngineType {
DEBUG = 0,
PRODUCTION = 1,
HIGH_PERFORMANCE = 2
};
/**
* @brief Engine orchestration interface - coordinates the entire system
*
* The engine is responsible for:
* - System initialization and lifecycle management
* - Main game loop coordination with delta time updates
* - Module system orchestration
* - IIO health monitoring and backpressure management
*
* IMPORTANT: Engine implementations must periodically check IIO health:
* - Monitor IOHealth.queueSize vs maxQueueSize (warn at 80% full)
* - Track IOHealth.dropping status (critical - consider module restart)
* - Log IOHealth.droppedMessageCount for debugging
* - Monitor IOHealth.averageProcessingRate for performance analysis
*
* Evolution path:
* - DebugEngine: Development/testing with step-by-step execution
* - HighPerfEngine: Production optimized with threading
* - DataOrientedEngine: Massive scale with SIMD and clustering
*/
class IEngine {
public:
virtual ~IEngine() = default;
/**
* @brief Initialize engine systems
*
* Sets up the engine with basic configuration.
* Module system and other components are set separately.
*/
virtual void initialize() = 0;
/**
* @brief Start main game loop
*
* Blocks until shutdown() called. Engine owns the main loop and handles:
* - Frame timing and delta time calculation
* - Module system coordination
* - Performance management and frame rate control
*/
virtual void run() = 0;
/**
* @brief Process single frame/tick (for debugging)
* @param deltaTime Time elapsed since last update in seconds
*
* For step debugging and testing. Processes one iteration
* without entering the main loop.
*/
virtual void step(float deltaTime) = 0;
/**
* @brief Shutdown engine and cleanup all resources
*
* Ensures proper cleanup of all systems in correct order.
* Should be safe to call multiple times. Stops run() loop.
*/
virtual void shutdown() = 0;
/**
* @brief Load modules from configuration
* @param configPath Path to module configuration file
*
* Engine automatically:
* - Loads modules from .so/.dll files
* - Creates appropriate ModuleSystem for each module (performance strategy)
* - Configures execution frequency and coordination
*
* Config format:
* {
* "modules": [
* {"path": "tank.so", "strategy": "threaded", "frequency": "60hz"},
* {"path": "economy.so", "strategy": "sequential", "frequency": "0.1hz"}
* ]
* }
*/
virtual void loadModules(const std::string& configPath) = 0;
/**
* @brief Register main coordinator socket
* @param coordinatorSocket Socket for system coordination communication
*
* Engine uses this socket for high-level system coordination,
* health monitoring, and administrative commands.
*/
virtual void registerMainSocket(std::unique_ptr<IIO> coordinatorSocket) = 0;
/**
* @brief Register new client/player socket
* @param clientSocket Socket for player communication
*
* Engine manages player connections as a priority channel.
* Players are the most important external connections.
*/
virtual void registerNewClientSocket(std::unique_ptr<IIO> clientSocket) = 0;
/**
* @brief Get engine type identifier
* @return Engine type enum value for identification
*/
virtual EngineType getType() const = 0;
};
} // namespace warfactory

View File

@ -1,102 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include <functional>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace warfactory {
enum class IOType {
INTRA = 0, // Same process
LOCAL = 1, // Same machine
NETWORK = 2 // TCP/WebSocket
};
struct SubscriptionConfig {
bool replaceable = false; // Replace vs accumulate for low-freq
int batchInterval = 30000; // ms for low-freq batching
int maxBatchSize = 100; // Max messages per batch
bool compress = false; // Compress batched data
};
struct Message {
std::string topic;
json data;
uint64_t timestamp;
};
struct IOHealth {
int queueSize;
int maxQueueSize;
bool dropping = false; // Started dropping messages?
float averageProcessingRate; // Messages/second processed by module
int droppedMessageCount = 0; // Total dropped since last check
};
/**
* @brief Pub/Sub communication interface with pull-based synchronous design
*
* Pull-based pub/sub system optimized for game modules. Modules have full control
* over when they process messages, avoiding threading issues.
*
* Features:
* - Topic patterns with wildcards (e.g., "player:*", "economy:*")
* - Low-frequency subscriptions for bandwidth optimization
* - Message consumption (pull removes message from queue)
* - Engine health monitoring for backpressure management
*/
class IIO {
public:
virtual ~IIO() = default;
/**
* @brief Publish message to a topic
* @param topic Topic name (e.g., "player:123", "economy:prices")
* @param message JSON message data
*/
virtual void publish(const std::string& topic, const json& message) = 0;
/**
* @brief Subscribe to topic pattern (high-frequency)
* @param topicPattern Topic pattern with wildcards (e.g., "player:*")
* @param config Optional subscription configuration
*/
virtual void subscribe(const std::string& topicPattern, const SubscriptionConfig& config = {}) = 0;
/**
* @brief Subscribe to topic pattern (low-frequency batched)
* @param topicPattern Topic pattern with wildcards
* @param config Subscription configuration (batchInterval, etc.)
*/
virtual void subscribeLowFreq(const std::string& topicPattern, const SubscriptionConfig& config = {}) = 0;
/**
* @brief Get count of pending messages
* @return Number of messages waiting to be pulled
*/
virtual int hasMessages() const = 0;
/**
* @brief Pull and consume one message
* @return Message from queue (oldest first). Message is removed from queue.
* @throws std::runtime_error if no messages available
*/
virtual Message pullMessage() = 0;
/**
* @brief Get IO health status for Engine monitoring
* @return Health metrics including queue size, drop status, processing rate
*/
virtual IOHealth getHealth() const = 0;
/**
* @brief Get IO type identifier
* @return IO type enum value for identification
*/
virtual IOType getType() const = 0;
};
} // namespace warfactory

View File

@ -1,113 +0,0 @@
#pragma once
#include <string>
#include <nlohmann/json.hpp>
#include "IDataNode.h"
#include "ITaskScheduler.h"
// Forward declarations
namespace warfactory {
class IIO;
}
using json = nlohmann::json;
namespace warfactory {
/**
* @brief Pure business logic interface - optimized for Claude Code development
*
* This interface defines the contract for all game modules. Each module contains
* 200-300 lines of pure game logic with zero infrastructure code.
*
* Key design principles:
* - PURE FUNCTION: process() method has no side effects beyond return value
* - CONFIG VIA DATATREE: Configuration via immutable IDataNode references
* - JSON ONLY: All communication via JSON input/output
* - NO INFRASTRUCTURE: No threading, networking, or framework dependencies
* - HOT-RELOAD READY: State serialization for seamless module replacement
* - CLAUDE OPTIMIZED: Micro-context size for AI development efficiency
*
* BREAKING CHANGES:
* - Removed initialize() method - use setConfiguration() instead
* - Configuration via const IDataNode& for immutability
* - Health check returns detailed JSON status
*
* Module constraint: Maximum 300 lines per module (Exception: ProductionModule 500-800 lines)
*/
class IModule {
public:
virtual ~IModule() = default;
/**
* @brief Process game logic
* @param input JSON input from other modules or the module system
*
* This is the core method where all module logic is implemented.
* Modules communicate via IIO pub/sub and can delegate tasks via ITaskScheduler.
* Must handle state properly through getState/setState for hot-reload.
*/
virtual void process(const json& input) = 0;
/**
* @brief Set module configuration (replaces initialize)
* @param configNode Configuration node (immutable reference)
* @param io Pub/sub communication interface for messaging
* @param scheduler Task scheduling interface for delegating work
*
* Called when the module is loaded or configuration changes.
* Should setup internal state, validate configuration, and store service references.
*/
virtual void setConfiguration(const IDataNode& configNode, IIO* io, ITaskScheduler* scheduler) = 0;
/**
* @brief Get current module configuration
* @return Configuration node reference
*/
virtual const IDataNode& getConfiguration() = 0;
/**
* @brief Get detailed health status of the module
* @return JSON health report with status, metrics, and diagnostics
*/
virtual json getHealthStatus() = 0;
/**
* @brief Cleanup and shutdown the module
*
* Called when the module is being unloaded. Should clean up any
* resources and prepare for safe destruction.
*/
virtual void shutdown() = 0;
/**
* @brief Get current module state for hot-reload support
* @return JSON representation of all module state
*
* Critical for hot-reload functionality. Must serialize all internal
* state that needs to be preserved when the module is replaced.
* The returned JSON should be sufficient to restore the module to
* its current state via setState().
*/
virtual json getState() = 0;
/**
* @brief Restore module state after hot-reload
* @param state JSON state previously returned by getState()
*
* Called after module replacement to restore the previous state.
* Must be able to reconstruct all internal state from the JSON
* to ensure seamless hot-reload without game disruption.
*/
virtual void setState(const json& state) = 0;
/**
* @brief Get module type identifier
* @return Module type as string (e.g., "tank", "economy", "production")
*/
virtual std::string getType() const = 0;
};
} // namespace warfactory

View File

@ -1,95 +0,0 @@
#pragma once
#include <string>
#include <memory>
#include <nlohmann/json.hpp>
#include "ITaskScheduler.h"
// Forward declarations to avoid circular dependencies
namespace warfactory {
class IModule;
class IIO;
}
using json = nlohmann::json;
namespace warfactory {
enum class ModuleSystemType {
SEQUENTIAL = 0,
THREADED = 1,
THREAD_POOL = 2,
CLUSTER = 3
};
/**
* @brief Module execution strategy interface - swappable performance architecture
*
* The module system manages module lifecycle and execution strategy.
* Different implementations provide different performance characteristics:
*
* - SequentialModuleSystem: Debug/test mode, processes modules one at a time
* - ThreadedModuleSystem: Each module in its own thread
* - MultithreadedModuleSystem: Module tasks distributed across thread pool
* - ClusterModuleSystem: Modules distributed across multiple machines
*
* This enables progressive evolution from debug to production to MMO scale
* without changing any module business logic code.
*
* Inherits from ITaskScheduler to provide task delegation capabilities.
*/
class IModuleSystem : public ITaskScheduler {
public:
virtual ~IModuleSystem() = default;
/**
* @brief Register a module with the system
* @param name Unique identifier for the module
* @param module Module implementation (unique ownership)
*
* The module system takes ownership of the module and manages its lifecycle.
* Modules can be registered at any time and will participate in the next
* processing cycle.
*/
virtual void registerModule(const std::string& name, std::unique_ptr<IModule> module) = 0;
/**
* @brief Process all registered modules
* @param deltaTime Time elapsed since last processing cycle in seconds
*
* This is the core execution method that coordinates all modules according
* to the implemented strategy. Each module's process() method will be called
* with appropriate timing and coordination.
*/
virtual void processModules(float deltaTime) = 0;
/**
* @brief Set the IO layer for inter-module communication
* @param ioLayer Communication transport implementation (unique ownership)
*
* The module system takes ownership of the IO layer and uses it to
* facilitate communication between modules.
*/
virtual void setIOLayer(std::unique_ptr<IIO> ioLayer) = 0;
/**
* @brief Query a specific module directly
* @param name Name of the module to query
* @param input JSON input to send to the module
* @return JSON response from the module
*
* This provides direct access to module functionality for debugging,
* testing, or administrative purposes. The query bypasses normal
* execution flow and calls the module's process() method directly.
*/
virtual json queryModule(const std::string& name, const json& input) = 0;
/**
* @brief Get module system type identifier
* @return Module system type enum value for identification
*/
virtual ModuleSystemType getType() const = 0;
};
} // namespace warfactory

View File

@ -1,50 +0,0 @@
#pragma once
#include <string>
namespace warfactory {
/**
* @brief Interface for geological regions during world generation
*
* Represents discrete regions with specific properties like resource deposits,
* volcanic activity, or tectonic formations.
*/
class IRegion {
public:
virtual ~IRegion() = default;
// ========================================
// IDENTIFICATION
// ========================================
virtual int getId() const = 0;
virtual const std::string& getType() const = 0;
// ========================================
// POSITION & SIZE
// ========================================
virtual float getX() const = 0;
virtual float getY() const = 0;
virtual float getRadius() const = 0;
virtual float getMass() const = 0;
// ========================================
// LIFECYCLE
// ========================================
virtual void update(float delta_time) = 0;
virtual bool isActive() const = 0;
// ========================================
// PROPERTIES
// ========================================
virtual float getIntensity() const = 0;
virtual void setIntensity(float intensity) = 0;
virtual bool canFuseWith(const IRegion* other) const = 0;
};
} // namespace warfactory

View File

@ -1,17 +0,0 @@
#pragma once
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace warfactory {
class ISerializable {
public:
virtual ~ISerializable() = default;
virtual json serialize() const = 0;
virtual void deserialize(const json& data) = 0;
};
} // namespace warfactory

View File

@ -1,102 +0,0 @@
#pragma once
#include <string>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace warfactory {
/**
* @brief Task scheduling interface for module delegation to execution system
*
* ITaskScheduler allows modules to delegate computationally expensive or
* time-consuming tasks to the underlying execution system without knowing
* the implementation details (sequential, threaded, thread pool, cluster).
*
* CORE PURPOSE:
* - Modules stay lightweight (200-300 lines) by delegating heavy work
* - Execution strategy determined by IModuleSystem implementation
* - Modules remain thread-agnostic and infrastructure-free
*
* USAGE PATTERNS:
* - ProductionModule: Delegate belt pathfinding calculations
* - TankModule: Delegate A* pathfinding for unit movement
* - EconomyModule: Delegate market analysis and price calculations
* - FactoryModule: Delegate assembly line optimization
*
* EXECUTION STRATEGIES:
* - SequentialModuleSystem: Tasks executed immediately in same thread
* - ThreadedModuleSystem: Tasks executed in dedicated module thread
* - MultithreadedModuleSystem: Tasks distributed across thread pool
* - ClusterModuleSystem: Tasks distributed across remote workers
*
* PERFORMANCE BENEFIT:
* - Modules process() methods stay fast (< 1ms for 60Hz modules)
* - Heavy computation moved to background without blocking game loop
* - Automatic scaling based on IModuleSystem implementation
*/
class ITaskScheduler {
public:
virtual ~ITaskScheduler() = default;
/**
* @brief Schedule a task for execution
* @param taskType Type of task (e.g., "pathfinding", "market_analysis", "belt_optimization")
* @param taskData JSON data for the task
*
* Example usage:
* ```cpp
* // TankModule delegates pathfinding
* scheduler->scheduleTask("pathfinding", {
* {"start", {x: 100, y: 200}},
* {"target", {x: 500, y: 600}},
* {"unit_id", "tank_001"}
* });
*
* // ProductionModule delegates belt calculation
* scheduler->scheduleTask("belt_optimization", {
* {"factory_id", "main_base"},
* {"item_type", "iron_plate"},
* {"throughput_target", 240}
* });
* ```
*/
virtual void scheduleTask(const std::string& taskType, const json& taskData) = 0;
/**
* @brief Check if completed tasks are available
* @return Number of completed tasks ready to be pulled
*
* Modules should check this before calling getCompletedTask()
* to avoid blocking or polling unnecessarily.
*/
virtual int hasCompletedTasks() const = 0;
/**
* @brief Pull and consume one completed task
* @return Task result JSON. Task is removed from completed queue.
*
* Example results:
* ```cpp
* // Pathfinding result
* {
* "task_type": "pathfinding",
* "unit_id": "tank_001",
* "path": [{"x": 100, "y": 200}, {"x": 150, "y": 250}, ...],
* "cost": 42.5
* }
*
* // Belt optimization result
* {
* "task_type": "belt_optimization",
* "factory_id": "main_base",
* "optimal_layout": [...],
* "efficiency_gain": 0.15
* }
* ```
*/
virtual json getCompletedTask() = 0;
};
} // namespace warfactory

View File

@ -1,129 +0,0 @@
#pragma once
#include <nlohmann/json.hpp>
#include <string>
#include <functional>
namespace warfactory {
using json = nlohmann::json;
/**
* @brief Pure Generic UI Interface - Zero assumptions about content
*
* Completely data-agnostic. Implementation decides how to handle each data type.
*/
class IUI {
public:
virtual ~IUI() = default;
// ========================================
// LIFECYCLE
// ========================================
/**
* @brief Initialize UI system
* @param config Generic config, implementation interprets
*/
virtual void initialize(const json& config) = 0;
/**
* @brief Update/render one frame
* @return true to continue, false to quit
*/
virtual bool update() = 0;
/**
* @brief Clean shutdown
*/
virtual void shutdown() = 0;
// ========================================
// GENERIC DATA FLOW
// ========================================
/**
* @brief Display any data of any type with layout/windowing info
* @param dataType "economy", "map", "inventory", "status", whatever
* @param data JSON with content + layout:
* {
* "content": {...}, // Actual data to display
* "window": { // Window/layout configuration
* "id": "economy_main", // Unique window ID
* "title": "Economy Dashboard",
* "parent": "main_dock", // Parent window/dock ID (optional)
* "dock": "left", // Dock position: "left", "right", "top", "bottom", "center", "tab"
* "size": {"width": 400, "height": 300},
* "position": {"x": 100, "y": 50},
* "floating": false, // true = floating window, false = docked
* "resizable": true,
* "closeable": true
* }
* }
*/
virtual void showData(const std::string& dataType, const json& data) = 0;
/**
* @brief Handle any user request of any type
* @param requestType "get_prices", "move_unit", "save_game", whatever
* @param callback Function to call when request happens
*/
virtual void onRequest(const std::string& requestType, std::function<void(const json&)> callback) = 0;
/**
* @brief Show any event/message
* @param level "info", "error", "debug", whatever
* @param message Human readable text
*/
virtual void showEvent(const std::string& level, const std::string& message) = 0;
// ========================================
// WINDOW MANAGEMENT
// ========================================
/**
* @brief Create or configure a dock/container window
* @param dockId Unique dock identifier
* @param config Dock configuration:
* {
* "type": "dock", // "dock", "tabbed", "split"
* "orientation": "horizontal", // "horizontal", "vertical" (for splits)
* "parent": "main_window", // Parent dock (for nested docks)
* "position": "left", // Initial position
* "size": {"width": 300}, // Initial size
* "collapsible": true, // Can be collapsed
* "tabs": true // Enable tabbed interface
* }
*/
virtual void createDock(const std::string& dockId, const json& config) = 0;
/**
* @brief Close/remove window or dock
* @param windowId Window or dock ID to close
*/
virtual void closeWindow(const std::string& windowId) = 0;
/**
* @brief Focus/bring to front a specific window
* @param windowId Window ID to focus
*/
virtual void focusWindow(const std::string& windowId) = 0;
// ========================================
// GENERIC STATE
// ========================================
/**
* @brief Get current UI state
* @return JSON state, implementation defines structure
*/
virtual json getState() const = 0;
/**
* @brief Restore UI state
* @param state JSON state from previous getState()
*/
virtual void setState(const json& state) = 0;
};
} // namespace warfactory

View File

@ -1,340 +0,0 @@
#pragma once
#include <nlohmann/json.hpp>
#include <string>
#include <functional>
namespace warfactory {
using json = nlohmann::json;
// ========================================
// ENUMS FOR TYPE SAFETY
// ========================================
/**
* @brief Data types for UI display
*/
enum class DataType {
ECONOMY,
MAP,
INVENTORY,
CONSOLE,
PERFORMANCE,
COMPANIES,
ALERTS,
PRODUCTION,
LOGISTICS,
PLAYER,
SETTINGS,
DEBUG,
CUSTOM // For extending with string fallback
};
/**
* @brief Request types from UI
*/
enum class RequestType {
GET_PRICES,
GET_CHUNK,
MOVE_PLAYER,
SAVE_GAME,
LOAD_GAME,
CLOSE_WINDOW,
FOCUS_WINDOW,
UPDATE_SETTINGS,
EXECUTE_COMMAND,
CUSTOM // For extending with string fallback
};
/**
* @brief Event/message levels
*/
enum class EventLevel {
INFO,
SUCCESS,
WARNING,
ERROR,
DEBUG,
TRACE
};
/**
* @brief Dock types for window management
*/
enum class DockType {
DOCK, // Standard dockable panel
SPLIT, // Horizontal/vertical split
TABBED, // Tabbed container
FLOATING // Floating window
};
/**
* @brief Dock positions
*/
enum class DockPosition {
LEFT,
RIGHT,
TOP,
BOTTOM,
CENTER,
TAB // Add as tab to parent
};
/**
* @brief Split orientations
*/
enum class Orientation {
HORIZONTAL,
VERTICAL
};
/**
* @brief Pure Generic UI Interface - Type-safe with enums
*/
class IUI {
public:
virtual ~IUI() = default;
// ========================================
// LIFECYCLE
// ========================================
virtual void initialize(const json& config) = 0;
virtual bool update() = 0;
virtual void shutdown() = 0;
// ========================================
// GENERIC DATA FLOW - ENUM VERSIONS
// ========================================
/**
* @brief Display data with type-safe enum
* @param dataType Enum data type
* @param data JSON with content + optional window config:
* {
* "content": {...}, // Actual data to display
* "window": { // Window configuration (optional)
* "id": "window_id",
* "title": "Window Title",
* "parent": "parent_dock_id",
* "dock": "left|right|top|bottom|center|tab",
*
* // SIZE SYSTEM - Hybrid percentage + absolute constraints
* "size": {"width": "20%", "height": 300}, // Target: 20% of parent width, 300px height
* "size": {"width": 400, "height": "50%"}, // Target: 400px width, 50% of parent height
* "size": {"width": "30%", "height": "40%"}, // Target: 30% width, 40% height
*
* "min_size": {"width": 200, "height": 150}, // ABSOLUTE minimum in pixels (always respected)
* "max_size": {"width": 800, "height": "80%"}, // Maximum: 800px width OR 80% of parent (whichever smaller)
*
* "position": {"x": 100, "y": 50},
* "floating": false,
* "resizable": true,
* "closeable": true,
* "collapsible": false
* }
* }
*/
virtual void showData(DataType dataType, const json& data) = 0;
/**
* @brief Display custom data type (fallback to string)
* @param customType Custom data type name
* @param data JSON data
*/
virtual void showDataCustom(const std::string& customType, const json& data) = 0;
/**
* @brief Handle user request with type-safe enum
* @param requestType Enum request type
* @param callback Function to call when request happens
*/
virtual void onRequest(RequestType requestType, std::function<void(const json&)> callback) = 0;
/**
* @brief Handle custom request type (fallback to string)
* @param customType Custom request type name
* @param callback Function to call when request happens
*/
virtual void onRequestCustom(const std::string& customType, std::function<void(const json&)> callback) = 0;
/**
* @brief Show event with type-safe level
* @param level Event level enum
* @param message Human readable text
*/
virtual void showEvent(EventLevel level, const std::string& message) = 0;
// ========================================
// WINDOW MANAGEMENT - ENUM VERSIONS
// ========================================
/**
* @brief Create dock with type-safe enums
* @param dockId Unique dock identifier
* @param type Dock type enum
* @param position Dock position enum
* @param config Additional configuration:
* {
* "parent": "parent_dock_id", // Parent dock (optional)
*
* // HYBRID SIZE SYSTEM
* "size": {"width": "25%", "height": 200}, // Target: 25% of parent width, 200px height
* "min_size": {"width": 200, "height": 100}, // ABSOLUTE minimum pixels (overrides percentage)
* "max_size": {"width": "50%", "height": 600}, // Maximum: 50% of parent OR 600px (whichever smaller)
*
* "orientation": "horizontal", // For SPLIT type
* "collapsible": true, // Can be collapsed
* "resizable": true, // Can be resized
* "tabs": true // Enable tabbed interface
* }
*/
virtual void createDock(const std::string& dockId, DockType type, DockPosition position, const json& config = {}) = 0;
/**
* @brief Create split dock with orientation
* @param dockId Unique dock identifier
* @param orientation Split orientation
* @param config Additional configuration:
* {
* "parent": "parent_dock_id", // Parent dock (optional)
* "size": {"width": 300, "height": 200}, // Initial size
* "min_size": {"width": 100, "height": 50}, // Minimum split size in pixels
* "max_size": {"width": 1000, "height": 800}, // Maximum split size in pixels
* "split_ratio": 0.5, // Split ratio (0.0 to 1.0)
* "min_panel_size": 80, // Minimum size for each panel in split
* "resizable": true // Can be resized by dragging splitter
* }
*/
virtual void createSplit(const std::string& dockId, Orientation orientation, const json& config = {}) = 0;
/**
* @brief Close window or dock
* @param windowId Window/dock ID to close
*/
virtual void closeWindow(const std::string& windowId) = 0;
/**
* @brief Focus window
* @param windowId Window ID to focus
*/
virtual void focusWindow(const std::string& windowId) = 0;
// ========================================
// GENERIC STATE
// ========================================
virtual json getState() const = 0;
virtual void setState(const json& state) = 0;
// ========================================
// CONVENIENCE METHODS WITH ENUMS
// ========================================
void info(const std::string& message) {
showEvent(EventLevel::INFO, message);
}
void success(const std::string& message) {
showEvent(EventLevel::SUCCESS, message);
}
void warning(const std::string& message) {
showEvent(EventLevel::WARNING, message);
}
void error(const std::string& message) {
showEvent(EventLevel::ERROR, message);
}
void debug(const std::string& message) {
showEvent(EventLevel::DEBUG, message);
}
};
// ========================================
// ENUM TO STRING CONVERSIONS (for implementations)
// ========================================
/**
* @brief Convert DataType enum to string (for implementations that need strings)
*/
constexpr const char* toString(DataType type) {
switch (type) {
case DataType::ECONOMY: return "economy";
case DataType::MAP: return "map";
case DataType::INVENTORY: return "inventory";
case DataType::CONSOLE: return "console";
case DataType::PERFORMANCE: return "performance";
case DataType::COMPANIES: return "companies";
case DataType::ALERTS: return "alerts";
case DataType::PRODUCTION: return "production";
case DataType::LOGISTICS: return "logistics";
case DataType::PLAYER: return "player";
case DataType::SETTINGS: return "settings";
case DataType::DEBUG: return "debug";
case DataType::CUSTOM: return "custom";
default: return "unknown";
}
}
constexpr const char* toString(RequestType type) {
switch (type) {
case RequestType::GET_PRICES: return "get_prices";
case RequestType::GET_CHUNK: return "get_chunk";
case RequestType::MOVE_PLAYER: return "move_player";
case RequestType::SAVE_GAME: return "save_game";
case RequestType::LOAD_GAME: return "load_game";
case RequestType::CLOSE_WINDOW: return "close_window";
case RequestType::FOCUS_WINDOW: return "focus_window";
case RequestType::UPDATE_SETTINGS: return "update_settings";
case RequestType::EXECUTE_COMMAND: return "execute_command";
case RequestType::CUSTOM: return "custom";
default: return "unknown";
}
}
constexpr const char* toString(EventLevel level) {
switch (level) {
case EventLevel::INFO: return "info";
case EventLevel::SUCCESS: return "success";
case EventLevel::WARNING: return "warning";
case EventLevel::ERROR: return "error";
case EventLevel::DEBUG: return "debug";
case EventLevel::TRACE: return "trace";
default: return "unknown";
}
}
constexpr const char* toString(DockType type) {
switch (type) {
case DockType::DOCK: return "dock";
case DockType::SPLIT: return "split";
case DockType::TABBED: return "tabbed";
case DockType::FLOATING: return "floating";
default: return "unknown";
}
}
constexpr const char* toString(DockPosition pos) {
switch (pos) {
case DockPosition::LEFT: return "left";
case DockPosition::RIGHT: return "right";
case DockPosition::TOP: return "top";
case DockPosition::BOTTOM: return "bottom";
case DockPosition::CENTER: return "center";
case DockPosition::TAB: return "tab";
default: return "unknown";
}
}
constexpr const char* toString(Orientation orient) {
switch (orient) {
case Orientation::HORIZONTAL: return "horizontal";
case Orientation::VERTICAL: return "vertical";
default: return "unknown";
}
}
} // namespace warfactory

View File

@ -1,707 +0,0 @@
#pragma once
#include "IUI_Enums.h"
#include <imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include <GLFW/glfw3.h>
#include <GL/gl.h>
#include <map>
#include <vector>
#include <string>
#include <functional>
#include <chrono>
namespace warfactory {
/**
* @brief ImGui implementation of IUI interface
*
* Provides full windowing system with docking, tabs, splits, and floating windows.
* Handles hybrid percentage + pixel sizing with automatic constraint enforcement.
*/
class ImGuiUI : public IUI {
private:
// ========================================
// CORE STATE
// ========================================
GLFWwindow* window = nullptr;
bool initialized = false;
bool should_close = false;
int frame_count = 0;
// Screen/parent sizes for percentage calculations
ImVec2 screen_size = {1400, 900};
ImVec2 previous_screen_size = {0, 0};
// ========================================
// WINDOW MANAGEMENT
// ========================================
struct WindowInfo {
std::string id;
std::string title;
std::string parent;
DockPosition dock_position = DockPosition::CENTER;
bool is_open = true;
bool is_floating = false;
bool resizable = true;
bool closeable = true;
// Size system
ImVec2 size = {400, 300};
ImVec2 min_size = {100, 100};
ImVec2 max_size = {2000, 1500};
ImVec2 position = {0, 0};
// Percentage tracking
std::string size_width_percent = "";
std::string size_height_percent = "";
std::string min_width_percent = "";
std::string min_height_percent = "";
std::string max_width_percent = "";
std::string max_height_percent = "";
// Content
DataType data_type = DataType::CUSTOM;
json content_data;
};
std::map<std::string, WindowInfo> windows;
struct DockInfo {
std::string id;
DockType type = DockType::DOCK;
DockPosition position = DockPosition::LEFT;
std::string parent;
bool collapsible = true;
bool resizable = true;
ImVec2 size = {300, 200};
ImVec2 min_size = {100, 100};
ImVec2 max_size = {1000, 800};
std::vector<std::string> child_windows;
};
std::map<std::string, DockInfo> docks;
// ========================================
// CALLBACKS
// ========================================
std::map<RequestType, std::function<void(const json&)>> request_callbacks;
std::map<std::string, std::function<void(const json&)>> custom_request_callbacks;
// ========================================
// MESSAGE SYSTEM
// ========================================
struct LogMessage {
EventLevel level;
std::string message;
std::chrono::steady_clock::time_point timestamp;
};
std::vector<LogMessage> log_messages;
static constexpr size_t MAX_LOG_MESSAGES = 100;
public:
ImGuiUI() = default;
~ImGuiUI() override { shutdown(); }
// ========================================
// LIFECYCLE IMPLEMENTATION
// ========================================
void initialize(const json& config) override {
if (initialized) return;
// Initialize GLFW
if (!glfwInit()) {
throw std::runtime_error("Failed to initialize GLFW");
}
// OpenGL 3.3 Core
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Create window
std::string title = config.value("title", "Warfactory ImGui UI");
auto window_size = config.value("window_size", json{{"width", 1400}, {"height", 900}});
if (window_size.is_object()) {
screen_size.x = window_size.value("width", 1400);
screen_size.y = window_size.value("height", 900);
} else {
screen_size.x = 1400;
screen_size.y = 900;
}
window = glfwCreateWindow(
static_cast<int>(screen_size.x),
static_cast<int>(screen_size.y),
title.c_str(), nullptr, nullptr
);
if (!window) {
glfwTerminate();
throw std::runtime_error("Failed to create GLFW window");
}
glfwMakeContextCurrent(window);
glfwSwapInterval(1); // VSync
// Initialize ImGui
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
// Note: Docking features depend on ImGui docking branch
// Using manual docking simulation for compatibility
// Basic style setup
ImGui::StyleColorsDark();
// Platform/Renderer backends
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init("#version 330 core");
initialized = true;
}
bool update() override {
if (!initialized || !window) return false;
if (glfwWindowShouldClose(window)) {
should_close = true;
return false;
}
// Update screen size for percentage calculations
int fb_width, fb_height;
glfwGetFramebufferSize(window, &fb_width, &fb_height);
ImVec2 new_screen_size = {static_cast<float>(fb_width), static_cast<float>(fb_height)};
// Detect screen size changes and recalculate if needed
if (new_screen_size.x != previous_screen_size.x || new_screen_size.y != previous_screen_size.y) {
if (frame_count > 0) { // Skip first frame (initialization)
debug("🔄 Screen size changed: " + std::to_string((int)new_screen_size.x) + "x" + std::to_string((int)new_screen_size.y));
recalculateAllSizes();
}
previous_screen_size = screen_size;
}
screen_size = new_screen_size;
frame_count++;
// Poll events
glfwPollEvents();
// Start ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// Render all windows
renderAllWindows();
// Render ImGui
ImGui::Render();
// OpenGL rendering
glViewport(0, 0, static_cast<int>(screen_size.x), static_cast<int>(screen_size.y));
glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(window);
return !should_close;
}
void shutdown() override {
if (!initialized) return;
if (window) {
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(window);
glfwTerminate();
window = nullptr;
}
initialized = false;
}
private:
// ========================================
// SIZE CALCULATION HELPERS
// ========================================
/**
* @brief Parse size value - handles both pixels and percentages
*/
float parseSize(const json& size_value, float parent_size, float default_size) {
try {
if (size_value.is_number()) {
return size_value.get<float>();
}
if (size_value.is_string()) {
std::string size_str = size_value.get<std::string>();
if (!size_str.empty() && size_str.back() == '%') {
float percent = std::stof(size_str.substr(0, size_str.length() - 1));
return (percent / 100.0f) * parent_size;
} else {
// String but not percentage - try to parse as number
return std::stof(size_str);
}
}
} catch (...) {
// Any JSON or parsing error - return default
}
// Neither number nor string or error - return default
return default_size;
}
/**
* @brief Calculate effective size with hybrid constraints
*/
ImVec2 calculateEffectiveSize(const WindowInfo& win, ImVec2 parent_size) {
// Use already parsed sizes (converted in showData)
float target_width = win.size.x;
float target_height = win.size.y;
// Calculate constraint bounds
float min_width = win.min_size.x;
float min_height = win.min_size.y;
float max_width = win.max_size.x;
float max_height = win.max_size.y;
// Apply constraints (clamp)
float final_width = std::max(min_width, std::min(target_width, max_width));
float final_height = std::max(min_height, std::min(target_height, max_height));
return {final_width, final_height};
}
/**
* @brief Calculate window position based on docking
*/
ImVec2 calculateDockedPosition(const WindowInfo& win, ImVec2 size) {
if (win.parent.empty() || win.is_floating) {
// For windows without parent, use explicit position or calculate smart default
debug("🔍 Checking position for '" + win.id + "': pos=" +
std::to_string(win.position.x) + "," + std::to_string(win.position.y) +
" floating=" + (win.is_floating ? "true" : "false"));
// Only use explicit position if it was actually set by user (not just default values)
if (win.position.x > 10 && win.position.y > 10) {
debug("📌 Using explicit position for '" + win.id + "'");
return win.position; // Use explicit position
} else {
// Simple approach: use actual window sizes from economy_main window
float left_edge_end = 252; // Real end of economy_main (we know it's 252px wide)
float top_edge_end = 88; // Real end of toolbar + margin
// Find the right sidebar start by looking for info_panel_main
float right_edge_start = 1050; // We know info_panel starts at 1050px
debug("🔧 Simple positioning for window '" + win.id + "': left_end=" +
std::to_string(left_edge_end) + "px, right_start=" +
std::to_string(right_edge_start) + "px, top_end=" +
std::to_string(top_edge_end) + "px");
// Position directly against the real edge of existing windows
float x = left_edge_end; // Directly touching end of left sidebar (252px)
float y = top_edge_end; // Directly below toolbar (88px)
// If window would overlap with right sidebar, push it left to touch right edge
if (x + size.x > right_edge_start) {
x = right_edge_start - size.x; // Touch right sidebar windows
}
debug("🎯 Calculated position for '" + win.id + "': " +
std::to_string(x) + "," + std::to_string(y) +
" (touching real window edges)");
return {x, y};
}
}
// Find parent dock
auto dock_it = docks.find(win.parent);
if (dock_it == docks.end()) {
return {0, 0}; // Parent dock not found
}
const DockInfo& dock = dock_it->second;
// Calculate dock area based on position
switch (dock.position) {
case DockPosition::LEFT:
return {0, 80}; // Left edge but below toolbar (72px + margin)
case DockPosition::RIGHT:
return {screen_size.x - dock.size.x, 80}; // Right edge but below toolbar
case DockPosition::TOP:
// Top edge - if dock spans full width, start at 0, else offset
if (dock.size.x >= screen_size.x * 0.9f) {
return {0, 0}; // Full width toolbar starts at screen edge
} else {
return {280, 0}; // Partial width toolbar starts after sidebar
}
case DockPosition::BOTTOM:
return {0, screen_size.y - dock.size.y}; // Bottom edge
case DockPosition::CENTER:
default:
return {screen_size.x * 0.5f - size.x * 0.5f, screen_size.y * 0.5f - size.y * 0.5f}; // Center
}
}
// ========================================
// RECALCULATION METHODS
// ========================================
void recalculateAllSizes() {
// Recalculate dock sizes
for (auto& [dock_id, dock] : docks) {
// Recalculate dock size if it uses percentages
recalculateDockSize(dock);
}
// Recalculate window sizes
for (auto& [window_id, win] : windows) {
recalculateWindowSize(win);
}
}
void recalculateDockSize(DockInfo& dock) {
// Re-parse dock size with new screen size
// This would need the original JSON config, for now just log
debug("📐 Recalculating dock: " + dock.id);
// TODO: Store original percentage strings to recalculate properly
}
void recalculateWindowSize(WindowInfo& win) {
// Re-parse window size with new screen/parent sizes
debug("📐 Recalculating window: " + win.id);
// Recalculate width if percentage
if (!win.size_width_percent.empty()) {
float parent_width = screen_size.x;
if (!win.parent.empty() && docks.find(win.parent) != docks.end()) {
parent_width = docks[win.parent].size.x;
}
win.size.x = parseSize(win.size_width_percent, parent_width, 400);
}
// Recalculate height if percentage
if (!win.size_height_percent.empty()) {
float parent_height = screen_size.y;
if (!win.parent.empty() && docks.find(win.parent) != docks.end()) {
parent_height = docks[win.parent].size.y;
}
win.size.y = parseSize(win.size_height_percent, parent_height, 300);
}
}
// ========================================
// RENDERING IMPLEMENTATION
// ========================================
void renderAllWindows() {
// Log screen size for debugging (only first frame to avoid spam)
if (frame_count == 1) {
debug("🖥️ Screen Size: " + std::to_string((int)screen_size.x) + "x" + std::to_string((int)screen_size.y) + "px");
info("🏗️ Manual docking system active (simulated docking layout)");
}
for (auto& [window_id, win] : windows) {
if (!win.is_open) continue;
if (frame_count <= 5) { // Log first 5 frames for each window
debug("🪟 Window: " + window_id + " (" + win.title + ")");
debug(" 📐 Target Size: " + std::to_string((int)win.size.x) + "x" + std::to_string((int)win.size.y) + "px");
debug(" 📏 Size %: width='" + win.size_width_percent + "' height='" + win.size_height_percent + "'");
debug(" ⚖️ Constraints: min=" + std::to_string((int)win.min_size.x) + "x" + std::to_string((int)win.min_size.y) +
" max=" + std::to_string((int)win.max_size.x) + "x" + std::to_string((int)win.max_size.y));
debug(" 🔗 Docking: parent='" + win.parent + "' position=" + std::to_string((int)win.dock_position));
}
// Calculate effective size with constraints
ImVec2 effective_size = calculateEffectiveSize(win, screen_size);
if (frame_count <= 5) {
debug(" ✅ Effective Size: " + std::to_string((int)effective_size.x) + "x" + std::to_string((int)effective_size.y) + "px");
}
// Set window constraints
ImGui::SetNextWindowSizeConstraints(win.min_size, win.max_size);
// Set window size
if (win.is_floating) {
// For floating windows, force initial size and position
ImGuiCond size_condition = (frame_count <= 3) ? ImGuiCond_Always : ImGuiCond_FirstUseEver;
ImGui::SetNextWindowSize(effective_size, size_condition);
// Calculate smart position that avoids dock overlaps
ImVec2 floating_position = calculateDockedPosition(win, effective_size);
ImGuiCond position_condition = (frame_count <= 3) ? ImGuiCond_Always : ImGuiCond_FirstUseEver;
ImGui::SetNextWindowPos(floating_position, position_condition);
if (frame_count <= 5) {
debug(" 🎈 Floating Position: " + std::to_string((int)floating_position.x) + "," + std::to_string((int)floating_position.y));
}
} else {
// For docked windows, calculate position and force it during initial frames
ImVec2 dock_position = calculateDockedPosition(win, effective_size);
ImGuiCond condition = (frame_count <= 3) ? ImGuiCond_Always : ImGuiCond_FirstUseEver;
ImGui::SetNextWindowSize(effective_size, condition);
ImGui::SetNextWindowPos(dock_position, condition);
if (frame_count <= 5) {
debug(" 📍 Docked Position: " + std::to_string((int)dock_position.x) + "," + std::to_string((int)dock_position.y));
}
}
// Window flags
ImGuiWindowFlags flags = ImGuiWindowFlags_None;
if (!win.resizable) flags |= ImGuiWindowFlags_NoResize;
// Render window
if (ImGui::Begin(win.title.c_str(), win.closeable ? &win.is_open : nullptr, flags)) {
// Log actual ImGui window size after rendering (first 5 frames only)
if (frame_count <= 5) {
ImVec2 current_size = ImGui::GetWindowSize();
ImVec2 current_pos = ImGui::GetWindowPos();
debug(" 🎯 ImGui Actual: pos=" + std::to_string((int)current_pos.x) + "," + std::to_string((int)current_pos.y) +
" size=" + std::to_string((int)current_size.x) + "x" + std::to_string((int)current_size.y) + "px");
}
renderWindowContent(win);
}
ImGui::End();
}
}
void renderWindowContent(const WindowInfo& win) {
switch (win.data_type) {
case DataType::ECONOMY:
renderEconomyContent(win.content_data);
break;
case DataType::MAP:
renderMapContent(win.content_data);
break;
case DataType::INVENTORY:
renderInventoryContent(win.content_data);
break;
case DataType::CONSOLE:
renderConsoleContent(win.content_data);
break;
case DataType::PERFORMANCE:
renderPerformanceContent(win.content_data);
break;
case DataType::COMPANIES:
renderCompaniesContent(win.content_data);
break;
case DataType::ALERTS:
renderAlertsContent(win.content_data);
break;
case DataType::SETTINGS:
renderSettingsContent(win.content_data);
break;
default:
renderGenericContent(win.content_data);
break;
}
}
public:
// ========================================
// IUI INTERFACE IMPLEMENTATION - DATA DISPLAY
// ========================================
void showData(DataType dataType, const json& data) override {
// Extract window configuration
json window_config = data.value("window", json{});
json content = data.value("content", data);
// Generate ID if not provided
std::string window_id = window_config.value("id", "window_" + std::to_string(windows.size()));
// Create or update window info
WindowInfo& win = windows[window_id];
win.id = window_id;
win.title = window_config.value("title", toString(dataType));
win.data_type = dataType;
win.content_data = content;
win.is_open = true;
// Parse parent first (needed for size calculations)
win.parent = window_config.value("parent", "");
// Parse size configuration with percentage support
if (window_config.contains("size")) {
auto size_config = window_config["size"];
if (size_config.is_object()) {
if (size_config.contains("width")) {
auto width_val = size_config["width"];
if (width_val.is_string()) {
win.size_width_percent = width_val.get<std::string>();
debug("🔧 Processing width percentage '" + win.size_width_percent +
"' for window '" + win.id + "' with parent='" + win.parent + "'");
// Calculate parent size for percentage - use dock size if docked
float parent_width = screen_size.x;
if (!win.parent.empty() && docks.find(win.parent) != docks.end()) {
parent_width = docks[win.parent].size.x;
debug("🔍 Found parent dock '" + win.parent + "' with width=" +
std::to_string((int)parent_width) + "px");
} else if (!win.parent.empty()) {
debug("❌ Parent dock '" + win.parent + "' not found! Using screen width.");
}
win.size.x = parseSize(width_val, parent_width, 400);
} else if (width_val.is_number()) {
win.size.x = width_val.get<float>();
} else {
win.size.x = 400; // Default fallback
}
}
if (size_config.contains("height")) {
auto height_val = size_config["height"];
if (height_val.is_string()) {
win.size_height_percent = height_val.get<std::string>();
float parent_height = screen_size.y;
if (!win.parent.empty() && docks.find(win.parent) != docks.end()) {
parent_height = docks[win.parent].size.y;
}
win.size.y = parseSize(height_val, parent_height, 300);
} else if (height_val.is_number()) {
win.size.y = height_val.get<float>();
} else {
win.size.y = 300; // Default fallback
}
}
}
}
// Parse constraints
if (window_config.contains("min_size")) {
auto min_config = window_config["min_size"];
if (min_config.is_object()) {
if (min_config.contains("width")) {
win.min_size.x = parseSize(min_config["width"], screen_size.x, 100);
} else {
win.min_size.x = 100;
}
if (min_config.contains("height")) {
win.min_size.y = parseSize(min_config["height"], screen_size.y, 100);
} else {
win.min_size.y = 100;
}
}
}
if (window_config.contains("max_size")) {
auto max_config = window_config["max_size"];
if (max_config.is_object()) {
if (max_config.contains("width")) {
win.max_size.x = parseSize(max_config["width"], screen_size.x, 2000);
} else {
win.max_size.x = 2000;
}
if (max_config.contains("height")) {
win.max_size.y = parseSize(max_config["height"], screen_size.y, 1500);
} else {
win.max_size.y = 1500;
}
}
}
// Parse other properties
win.is_floating = window_config.value("floating", false);
win.resizable = window_config.value("resizable", true);
win.closeable = window_config.value("closeable", true);
// Parse dock position if specified
if (window_config.contains("dock")) {
std::string dock_str = window_config["dock"].get<std::string>();
if (dock_str == "left") win.dock_position = DockPosition::LEFT;
else if (dock_str == "right") win.dock_position = DockPosition::RIGHT;
else if (dock_str == "top") win.dock_position = DockPosition::TOP;
else if (dock_str == "bottom") win.dock_position = DockPosition::BOTTOM;
else if (dock_str == "tab") win.dock_position = DockPosition::CENTER; // tabs go in center
else win.dock_position = DockPosition::CENTER;
}
if (window_config.contains("position")) {
auto pos = window_config["position"];
if (pos.is_object()) {
win.position.x = pos.value("x", 0);
win.position.y = pos.value("y", 0);
}
}
}
void showDataCustom(const std::string& customType, const json& data) override {
// Treat as generic data with custom type in title
json modified_data = data;
if (!modified_data.contains("window")) {
modified_data["window"] = json{};
}
if (!modified_data["window"].contains("title")) {
modified_data["window"]["title"] = customType;
}
showData(DataType::CUSTOM, modified_data);
}
// ========================================
// IUI INTERFACE IMPLEMENTATION - REQUESTS & EVENTS
// ========================================
void onRequest(RequestType requestType, std::function<void(const json&)> callback) override;
void onRequestCustom(const std::string& customType, std::function<void(const json&)> callback) override;
void showEvent(EventLevel level, const std::string& message) override;
// ========================================
// WINDOW MANAGEMENT IMPLEMENTATION
// ========================================
void createDock(const std::string& dockId, DockType type, DockPosition position, const json& config = {}) override;
void createSplit(const std::string& dockId, Orientation orientation, const json& config = {}) override;
void closeWindow(const std::string& windowId) override;
void focusWindow(const std::string& windowId) override;
// ========================================
// STATE MANAGEMENT
// ========================================
json getState() const override;
void setState(const json& state) override;
private:
// ========================================
// CONTENT RENDERING IMPLEMENTATIONS
// ========================================
void renderEconomyContent(const json& content);
void renderMapContent(const json& content);
void renderInventoryContent(const json& content);
void renderConsoleContent(const json& content);
void renderPerformanceContent(const json& content);
void renderCompaniesContent(const json& content);
void renderAlertsContent(const json& content);
void renderSettingsContent(const json& content);
void renderGenericContent(const json& content);
void renderLogConsole();
};
} // namespace warfactory

View File

@ -1,89 +0,0 @@
#pragma once
#include <random>
#include <cstdint>
namespace warfactory {
/**
* @brief Centralized random number generator singleton
*
* Provides consistent, seedable random number generation across all modules.
* Ensures reproducibility for testing and debugging while maintaining
* high-quality random distribution.
*/
class RandomGenerator {
private:
std::mt19937 gen;
RandomGenerator() : gen(std::random_device{}()) {}
public:
// Singleton access
static RandomGenerator& getInstance() {
static RandomGenerator instance;
return instance;
}
// Delete copy/move constructors and operators
RandomGenerator(const RandomGenerator&) = delete;
RandomGenerator& operator=(const RandomGenerator&) = delete;
RandomGenerator(RandomGenerator&&) = delete;
RandomGenerator& operator=(RandomGenerator&&) = delete;
/**
* @brief Seed the random generator for reproducible sequences
* @param seed Seed value (use same seed for identical results)
*/
void seed(uint32_t seed) {
gen.seed(seed);
}
/**
* @brief Generate uniform float in range [min, max]
*/
float uniform(float min, float max) {
std::uniform_real_distribution<float> dis(min, max);
return dis(gen);
}
/**
* @brief Generate uniform int in range [min, max] (inclusive)
*/
int uniformInt(int min, int max) {
std::uniform_int_distribution<int> dis(min, max);
return dis(gen);
}
/**
* @brief Generate normal distribution float
*/
float normal(float mean, float stddev) {
std::normal_distribution<float> dis(mean, stddev);
return dis(gen);
}
/**
* @brief Generate uniform float in range [0.0, 1.0]
*/
float unit() {
return uniform(0.0f, 1.0f);
}
/**
* @brief Generate boolean with given probability
* @param probability Probability of returning true [0.0, 1.0]
*/
bool boolean(float probability = 0.5f) {
return unit() < probability;
}
/**
* @brief Direct access to underlying generator for custom distributions
*/
std::mt19937& getGenerator() {
return gen;
}
};
} // namespace warfactory

View File

@ -1,37 +0,0 @@
#pragma once
#include <string>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace warfactory {
class Resource {
private:
std::string resource_id;
std::string name;
std::string category;
std::string logistic_category;
float density;
int stack_size;
std::string container_type;
json ui_data;
public:
Resource() = default;
Resource(const json& resource_data);
const std::string& getResourceId() const { return resource_id; }
const std::string& getName() const { return name; }
const std::string& getCategory() const { return category; }
const std::string& getLogisticCategory() const { return logistic_category; }
float getDensity() const { return density; }
int getStackSize() const { return stack_size; }
const std::string& getContainerType() const { return container_type; }
const json& getUIData() const { return ui_data; }
static Resource loadFromJson(const std::string& resource_id, const json& resource_data);
};
} // namespace warfactory

View File

@ -1,113 +0,0 @@
#pragma once
#include "Resource.h"
#include <vector>
#include <unordered_map>
#include <string>
#include <memory>
namespace warfactory {
/**
* @brief Singleton registry for all game resources with fast uint32_t ID lookup
*
* Centralizes resource management with O(1) access by numeric ID.
* Resources are loaded once at startup and accessed by ID throughout the game.
*/
class ResourceRegistry {
private:
static std::unique_ptr<ResourceRegistry> instance;
static bool initialized;
std::vector<Resource> resources; // Indexed by ID (resources[id])
std::unordered_map<std::string, uint32_t> name_to_id; // String -> ID mapping (init only)
uint32_t next_id = 1; // Start at 1 (0 = invalid/null resource)
ResourceRegistry() = default;
public:
// Singleton access
static ResourceRegistry& getInstance();
static void initialize();
static void shutdown();
// ========================================
// REGISTRATION (Initialization Phase)
// ========================================
/**
* @brief Register a resource and get its assigned ID
* @param resource The resource to register
* @return The assigned uint32_t ID for this resource
*/
uint32_t registerResource(const Resource& resource);
/**
* @brief Load resources from JSON configuration
* @param resources_json JSON object containing all resources
*/
void loadResourcesFromJson(const json& resources_json);
// ========================================
// RUNTIME ACCESS (Performance Critical)
// ========================================
/**
* @brief Get resource by ID (O(1) access)
* @param id The resource ID
* @return Pointer to resource or nullptr if not found
*/
const Resource* getResource(uint32_t id) const;
/**
* @brief Get resource ID by name (use sparingly - prefer caching IDs)
* @param name The resource name/identifier
* @return The resource ID or 0 if not found
*/
uint32_t getResourceId(const std::string& name) const;
/**
* @brief Check if resource ID is valid
*/
bool isValidResourceId(uint32_t id) const;
// ========================================
// BULK OPERATIONS
// ========================================
/**
* @brief Get all registered resource IDs
*/
std::vector<uint32_t> getAllResourceIds() const;
/**
* @brief Get total number of registered resources
*/
size_t getResourceCount() const;
/**
* @brief Clear all registered resources (testing/reset)
*/
void clear();
// ========================================
// CONVENIENCE CONSTANTS
// ========================================
static constexpr uint32_t INVALID_RESOURCE_ID = 0;
static constexpr uint32_t MAX_RESOURCES = 1000000; // 1M resources max
// Prevent copy/assignment
ResourceRegistry(const ResourceRegistry&) = delete;
ResourceRegistry& operator=(const ResourceRegistry&) = delete;
};
// ========================================
// CONVENIENCE MACROS FOR PERFORMANCE
// ========================================
#define RESOURCE_ID(name) warfactory::ResourceRegistry::getInstance().getResourceId(name)
#define GET_RESOURCE(id) warfactory::ResourceRegistry::getInstance().getResource(id)
#define VALID_RESOURCE(id) warfactory::ResourceRegistry::getInstance().isValidResourceId(id)
} // namespace warfactory

View File

@ -1,38 +0,0 @@
#pragma once
#include <unordered_map>
#include <memory>
#include <string>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace warfactory {
class ASerializable;
class SerializationRegistry {
private:
std::unordered_map<std::string, ASerializable*> registered_objects;
SerializationRegistry() = default;
public:
static SerializationRegistry& getInstance();
void registerObject(const std::string& instance_id, ASerializable* object);
void unregisterObject(const std::string& instance_id);
json serializeAll() const;
void deserializeAll(const json& data);
json serializeObject(const std::string& instance_id) const;
void deserializeObject(const std::string& instance_id, const json& data);
size_t getRegisteredCount() const { return registered_objects.size(); }
std::vector<std::string> getRegisteredIds() const;
void clear();
};
} // namespace warfactory

View File

@ -1,546 +0,0 @@
#include "warfactory/ImGuiUI.h"
#include <sstream>
#include <iomanip>
#include <iostream>
namespace warfactory {
// ========================================
// IUI INTERFACE IMPLEMENTATION - REQUESTS & EVENTS
// ========================================
void ImGuiUI::onRequest(RequestType requestType, std::function<void(const json&)> callback) {
request_callbacks[requestType] = callback;
}
void ImGuiUI::onRequestCustom(const std::string& customType, std::function<void(const json&)> callback) {
custom_request_callbacks[customType] = callback;
}
void ImGuiUI::showEvent(EventLevel level, const std::string& message) {
LogMessage log_msg;
log_msg.level = level;
log_msg.message = message;
log_msg.timestamp = std::chrono::steady_clock::now();
log_messages.push_back(log_msg);
// Keep only last MAX_LOG_MESSAGES
if (log_messages.size() > MAX_LOG_MESSAGES) {
log_messages.erase(log_messages.begin());
}
// Also output to console for debugging
const char* level_str = toString(level);
std::cout << "[" << level_str << "] " << message << std::endl;
}
// ========================================
// WINDOW MANAGEMENT IMPLEMENTATION
// ========================================
void ImGuiUI::createDock(const std::string& dockId, DockType type, DockPosition position, const json& config) {
DockInfo& dock = docks[dockId];
dock.id = dockId;
dock.type = type;
dock.position = position;
dock.parent = config.value("parent", "");
// Parse size with percentage support
if (config.contains("size")) {
auto size_config = config["size"];
if (size_config.is_object()) {
if (size_config.contains("width")) {
dock.size.x = parseSize(size_config["width"], screen_size.x, 300);
} else {
dock.size.x = 300; // Default
}
if (size_config.contains("height")) {
dock.size.y = parseSize(size_config["height"], screen_size.y, 200);
} else {
dock.size.y = 200; // Default
}
}
}
if (config.contains("min_size")) {
auto min_config = config["min_size"];
if (min_config.is_object()) {
if (min_config.contains("width")) {
dock.min_size.x = parseSize(min_config["width"], screen_size.x, 100);
} else {
dock.min_size.x = 100;
}
if (min_config.contains("height")) {
dock.min_size.y = parseSize(min_config["height"], screen_size.y, 100);
} else {
dock.min_size.y = 100;
}
}
}
if (config.contains("max_size")) {
auto max_config = config["max_size"];
if (max_config.is_object()) {
if (max_config.contains("width")) {
dock.max_size.x = parseSize(max_config["width"], screen_size.x, 1000);
} else {
dock.max_size.x = 1000;
}
if (max_config.contains("height")) {
dock.max_size.y = parseSize(max_config["height"], screen_size.y, 800);
} else {
dock.max_size.y = 800;
}
}
}
dock.collapsible = config.value("collapsible", true);
dock.resizable = config.value("resizable", true);
// Debug logging for dock creation
showEvent(EventLevel::DEBUG, "🏗️ Created dock '" + dockId + "': " + std::string(toString(type)) +
" size=" + std::to_string((int)dock.size.x) + "x" + std::to_string((int)dock.size.y) + "px");
showEvent(EventLevel::INFO, "Created " + std::string(toString(type)) + " dock: " + dockId);
}
void ImGuiUI::createSplit(const std::string& dockId, Orientation orientation, const json& config) {
// Create as a split dock
json split_config = config;
split_config["orientation"] = toString(orientation);
createDock(dockId, DockType::SPLIT, DockPosition::CENTER, split_config);
}
void ImGuiUI::closeWindow(const std::string& windowId) {
auto it = windows.find(windowId);
if (it != windows.end()) {
it->second.is_open = false;
showEvent(EventLevel::INFO, "Closed window: " + windowId);
}
}
void ImGuiUI::focusWindow(const std::string& windowId) {
auto it = windows.find(windowId);
if (it != windows.end()) {
ImGui::SetWindowFocus(it->second.title.c_str());
showEvent(EventLevel::DEBUG, "Focused window: " + windowId);
}
}
// ========================================
// STATE MANAGEMENT
// ========================================
json ImGuiUI::getState() const {
json state;
state["frame_count"] = frame_count;
state["window_open"] = !should_close;
state["screen_size"] = {{"width", screen_size.x}, {"height", screen_size.y}};
// Save window states
json window_states = json::object();
for (const auto& [id, win] : windows) {
window_states[id] = {
{"is_open", win.is_open},
{"size", {{"width", win.size.x}, {"height", win.size.y}}},
{"position", {{"x", win.position.x}, {"y", win.position.y}}},
{"floating", win.is_floating}
};
}
state["windows"] = window_states;
return state;
}
void ImGuiUI::setState(const json& state) {
if (state.contains("windows")) {
for (const auto& [id, win_state] : state["windows"].items()) {
auto it = windows.find(id);
if (it != windows.end()) {
auto& win = it->second;
win.is_open = win_state.value("is_open", true);
if (win_state.contains("size")) {
auto size_state = win_state["size"];
if (size_state.is_object()) {
win.size.x = size_state.value("width", win.size.x);
win.size.y = size_state.value("height", win.size.y);
}
}
if (win_state.contains("position")) {
auto pos_state = win_state["position"];
if (pos_state.is_object()) {
win.position.x = pos_state.value("x", win.position.x);
win.position.y = pos_state.value("y", win.position.y);
}
}
win.is_floating = win_state.value("floating", win.is_floating);
}
}
}
}
// ========================================
// CONTENT RENDERING IMPLEMENTATIONS
// ========================================
void ImGuiUI::renderEconomyContent(const json& content) {
ImGui::Text("💰 Economy Dashboard");
ImGui::Separator();
if (content.contains("prices")) {
ImGui::Text("Market Prices:");
ImGui::BeginTable("prices_table", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg);
ImGui::TableSetupColumn("Item");
ImGui::TableSetupColumn("Price");
ImGui::TableSetupColumn("Trend");
ImGui::TableHeadersRow();
for (const auto& [item, price] : content["prices"].items()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s", item.c_str());
ImGui::TableNextColumn();
if (price.is_number()) {
ImGui::Text("%.2f", price.get<float>());
} else {
ImGui::Text("%s", price.dump().c_str());
}
ImGui::TableNextColumn();
// Show trend if available
if (content.contains("trends") && content["trends"].contains(item)) {
std::string trend = content["trends"][item];
if (trend[0] == '+') {
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "%s", trend.c_str());
} else if (trend[0] == '-') {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "%s", trend.c_str());
} else {
ImGui::Text("%s", trend.c_str());
}
} else {
ImGui::Text("--");
}
}
ImGui::EndTable();
}
ImGui::Spacing();
// Action buttons
if (ImGui::Button("🔄 Refresh Prices")) {
if (request_callbacks.count(RequestType::GET_PRICES)) {
request_callbacks[RequestType::GET_PRICES]({});
}
}
ImGui::SameLine();
if (ImGui::Button("📊 Market Analysis")) {
if (custom_request_callbacks.count("market_analysis")) {
custom_request_callbacks["market_analysis"]({});
}
}
}
void ImGuiUI::renderMapContent(const json& content) {
ImGui::Text("🗺️ Global Map");
ImGui::Separator();
if (content.contains("current_chunk")) {
auto chunk = content["current_chunk"];
if (chunk.is_object()) {
ImGui::Text("Current Chunk: (%d, %d)", chunk.value("x", 0), chunk.value("y", 0));
}
}
if (content.contains("tiles")) {
ImGui::Text("Map Display:");
// Navigation controls
if (ImGui::Button("⬆️")) {
if (request_callbacks.count(RequestType::GET_CHUNK)) {
request_callbacks[RequestType::GET_CHUNK]({{"action", "move_up"}});
}
}
if (ImGui::Button("⬅️")) {
if (request_callbacks.count(RequestType::GET_CHUNK)) {
request_callbacks[RequestType::GET_CHUNK]({{"action", "move_left"}});
}
}
ImGui::SameLine();
if (ImGui::Button("➡️")) {
if (request_callbacks.count(RequestType::GET_CHUNK)) {
request_callbacks[RequestType::GET_CHUNK]({{"action", "move_right"}});
}
}
if (ImGui::Button("⬇️")) {
if (request_callbacks.count(RequestType::GET_CHUNK)) {
request_callbacks[RequestType::GET_CHUNK]({{"action", "move_down"}});
}
}
// Simple tile grid representation
ImGui::Text("Tile Grid (sample):");
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 8; x++) {
if (x > 0) ImGui::SameLine();
// Generate simple tile representation
char tile_str[2] = "."; // Null-terminated string
if ((x + y) % 3 == 0) tile_str[0] = 'I'; // Iron
else if ((x + y) % 5 == 0) tile_str[0] = 'C'; // Copper
else if ((x + y) % 7 == 0) tile_str[0] = 'T'; // Tree
ImGui::Button(tile_str, ImVec2(20, 20));
}
}
}
ImGui::Spacing();
if (ImGui::Button("🔄 Refresh Map")) {
if (request_callbacks.count(RequestType::GET_CHUNK)) {
request_callbacks[RequestType::GET_CHUNK]({{"type", "refresh"}});
}
}
}
void ImGuiUI::renderInventoryContent(const json& content) {
ImGui::Text("🎒 Inventory");
ImGui::Separator();
if (content.contains("items")) {
ImGui::BeginTable("inventory_table", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg);
ImGui::TableSetupColumn("Item");
ImGui::TableSetupColumn("Quantity");
ImGui::TableSetupColumn("Reserved");
ImGui::TableHeadersRow();
for (const auto& item : content["items"]) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s", item.value("name", "Unknown").c_str());
ImGui::TableNextColumn();
ImGui::Text("%d", item.value("quantity", 0));
ImGui::TableNextColumn();
ImGui::Text("%d", item.value("reserved", 0));
}
ImGui::EndTable();
}
}
void ImGuiUI::renderConsoleContent(const json& content) {
ImGui::Text("🖥️ Console");
ImGui::Separator();
// Console output area
ImGui::BeginChild("console_output", ImVec2(0, -30), true);
if (content.contains("logs")) {
for (const auto& log : content["logs"]) {
std::string level = log.value("level", "info");
std::string message = log.value("message", "");
std::string timestamp = log.value("timestamp", "");
// Color based on level
if (level == "error") {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "[%s] %s - %s",
timestamp.c_str(), level.c_str(), message.c_str());
} else if (level == "warning") {
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "[%s] %s - %s",
timestamp.c_str(), level.c_str(), message.c_str());
} else if (level == "success") {
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "[%s] %s - %s",
timestamp.c_str(), level.c_str(), message.c_str());
} else {
ImGui::Text("[%s] %s - %s", timestamp.c_str(), level.c_str(), message.c_str());
}
}
}
ImGui::EndChild();
// Command input
static char command_buffer[256] = "";
ImGui::SetNextItemWidth(-1);
if (ImGui::InputText("##command", command_buffer, sizeof(command_buffer),
ImGuiInputTextFlags_EnterReturnsTrue)) {
if (custom_request_callbacks.count("console_command")) {
custom_request_callbacks["console_command"]({{"command", std::string(command_buffer)}});
}
command_buffer[0] = '\0'; // Clear buffer
}
}
void ImGuiUI::renderPerformanceContent(const json& content) {
ImGui::Text("📊 Performance Monitor");
ImGui::Separator();
if (content.contains("fps")) {
ImGui::Text("FPS: %d", content.value("fps", 0));
}
if (content.contains("frame_time")) {
ImGui::Text("Frame Time: %s", content.value("frame_time", "0ms").c_str());
}
if (content.contains("memory_usage")) {
ImGui::Text("Memory: %s", content.value("memory_usage", "0MB").c_str());
}
if (content.contains("entities")) {
ImGui::Text("Entities: %d", content.value("entities", 0));
}
// Real-time FPS display
ImGui::Spacing();
ImGui::Text("Real-time FPS: %.1f", ImGui::GetIO().Framerate);
}
void ImGuiUI::renderCompaniesContent(const json& content) {
ImGui::Text("🏢 Companies");
ImGui::Separator();
for (const auto& [company_name, company_data] : content.items()) {
if (ImGui::CollapsingHeader(company_name.c_str())) {
if (company_data.contains("cash")) {
ImGui::Text("💰 Cash: $%d", company_data.value("cash", 0));
}
if (company_data.contains("status")) {
ImGui::Text("📊 Status: %s", company_data.value("status", "unknown").c_str());
}
if (company_data.contains("strategy")) {
ImGui::Text("🎯 Strategy: %s", company_data.value("strategy", "none").c_str());
}
}
}
}
void ImGuiUI::renderAlertsContent(const json& content) {
ImGui::Text("⚠️ Alerts");
ImGui::Separator();
if (content.contains("urgent_alerts")) {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "🚨 URGENT:");
for (const auto& alert : content["urgent_alerts"]) {
if (alert.is_string()) {
ImGui::BulletText("%s", alert.get<std::string>().c_str());
} else {
ImGui::BulletText("%s", alert.dump().c_str());
}
}
}
if (content.contains("warnings")) {
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "⚠️ Warnings:");
for (const auto& warning : content["warnings"]) {
if (warning.is_string()) {
ImGui::BulletText("%s", warning.get<std::string>().c_str());
} else {
ImGui::BulletText("%s", warning.dump().c_str());
}
}
}
ImGui::Spacing();
if (ImGui::Button("✅ Acknowledge All")) {
if (custom_request_callbacks.count("acknowledge_alerts")) {
custom_request_callbacks["acknowledge_alerts"]({});
}
}
}
void ImGuiUI::renderSettingsContent(const json& content) {
ImGui::Text("⚙️ Settings");
ImGui::Separator();
if (content.contains("graphics")) {
if (ImGui::CollapsingHeader("🖥️ Graphics")) {
auto graphics = content["graphics"];
if (graphics.is_object()) {
ImGui::Text("Resolution: %s", graphics.value("resolution", "Unknown").c_str());
bool fullscreen = graphics.value("fullscreen", false);
if (ImGui::Checkbox("Fullscreen", &fullscreen)) {
// Handle setting change
}
bool vsync = graphics.value("vsync", true);
if (ImGui::Checkbox("VSync", &vsync)) {
// Handle setting change
}
}
}
}
if (content.contains("audio")) {
if (ImGui::CollapsingHeader("🔊 Audio")) {
auto audio = content["audio"];
if (audio.is_object()) {
float master_vol = audio.value("master_volume", 1.0f);
if (ImGui::SliderFloat("Master Volume", &master_vol, 0.0f, 1.0f)) {
// Handle setting change
}
float effects_vol = audio.value("effects_volume", 1.0f);
if (ImGui::SliderFloat("Effects Volume", &effects_vol, 0.0f, 1.0f)) {
// Handle setting change
}
}
}
}
}
void ImGuiUI::renderGenericContent(const json& content) {
ImGui::Text("📄 Data");
ImGui::Separator();
// Generic JSON display
std::ostringstream oss;
oss << content.dump(2); // Pretty print with 2-space indent
ImGui::TextWrapped("%s", oss.str().c_str());
}
void ImGuiUI::renderLogConsole() {
// Always visible log console at bottom
ImGui::SetNextWindowSize(ImVec2(screen_size.x, 200), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos(ImVec2(0, screen_size.y - 200), ImGuiCond_FirstUseEver);
if (ImGui::Begin("📜 System Log", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::BeginChild("log_scroll", ImVec2(0, 150), true);
for (const auto& log_msg : log_messages) {
auto duration = log_msg.timestamp.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 100000;
const char* level_str = toString(log_msg.level);
// Color based on level
ImVec4 color = {1.0f, 1.0f, 1.0f, 1.0f}; // Default white
switch (log_msg.level) {
case EventLevel::ERROR: color = {1.0f, 0.0f, 0.0f, 1.0f}; break;
case EventLevel::WARNING: color = {1.0f, 1.0f, 0.0f, 1.0f}; break;
case EventLevel::SUCCESS: color = {0.0f, 1.0f, 0.0f, 1.0f}; break;
case EventLevel::DEBUG: color = {0.7f, 0.7f, 0.7f, 1.0f}; break;
case EventLevel::INFO:
default: color = {1.0f, 1.0f, 1.0f, 1.0f}; break;
}
ImGui::TextColored(color, "[%05lld] [%s] %s",
millis, level_str, log_msg.message.c_str());
}
// Auto-scroll to bottom
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
ImGui::SetScrollHereY(1.0f);
}
ImGui::EndChild();
}
ImGui::End();
}
} // namespace warfactory

View File

@ -1,120 +0,0 @@
#include "warfactory/ResourceRegistry.h"
#include <algorithm>
namespace warfactory {
// Static member initialization
std::unique_ptr<ResourceRegistry> ResourceRegistry::instance = nullptr;
bool ResourceRegistry::initialized = false;
ResourceRegistry& ResourceRegistry::getInstance() {
if (!initialized) {
initialize();
}
return *instance;
}
void ResourceRegistry::initialize() {
if (!initialized) {
instance = std::unique_ptr<ResourceRegistry>(new ResourceRegistry());
initialized = true;
}
}
void ResourceRegistry::shutdown() {
if (initialized) {
instance.reset();
initialized = false;
}
}
// ========================================
// REGISTRATION (Initialization Phase)
// ========================================
uint32_t ResourceRegistry::registerResource(const Resource& resource) {
if (next_id >= MAX_RESOURCES) {
// Handle overflow - could throw or return INVALID_RESOURCE_ID
return INVALID_RESOURCE_ID;
}
const uint32_t assigned_id = next_id++;
// Ensure vector is large enough
if (resources.size() <= assigned_id) {
resources.resize(assigned_id + 1);
}
// Store resource at index = ID
resources[assigned_id] = resource;
// Map name to ID for lookup
name_to_id[resource.getResourceId()] = assigned_id;
return assigned_id;
}
void ResourceRegistry::loadResourcesFromJson(const json& resources_json) {
for (json::const_iterator it = resources_json.begin(); it != resources_json.end(); ++it) {
const std::string& resource_name = it.key();
const json& resource_data = it.value();
// Create resource from JSON
Resource resource = Resource::loadFromJson(resource_name, resource_data);
// Register it
const uint32_t resource_id = registerResource(resource);
// Log or handle registration result if needed
(void)resource_id; // Suppress unused variable warning
}
}
// ========================================
// RUNTIME ACCESS (Performance Critical)
// ========================================
const Resource* ResourceRegistry::getResource(uint32_t id) const {
if (id == INVALID_RESOURCE_ID || id >= resources.size()) {
return nullptr;
}
return &resources[id];
}
uint32_t ResourceRegistry::getResourceId(const std::string& name) const {
const std::unordered_map<std::string, uint32_t>::const_iterator it = name_to_id.find(name);
return (it != name_to_id.end()) ? it->second : INVALID_RESOURCE_ID;
}
bool ResourceRegistry::isValidResourceId(uint32_t id) const {
return id != INVALID_RESOURCE_ID && id < resources.size();
}
// ========================================
// BULK OPERATIONS
// ========================================
std::vector<uint32_t> ResourceRegistry::getAllResourceIds() const {
std::vector<uint32_t> ids;
ids.reserve(next_id - 1); // -1 because we start at 1
for (uint32_t id = 1; id < next_id; ++id) {
if (id < resources.size()) {
ids.push_back(id);
}
}
return ids;
}
size_t ResourceRegistry::getResourceCount() const {
return next_id - 1; // -1 because we start at 1
}
void ResourceRegistry::clear() {
resources.clear();
name_to_id.clear();
next_id = 1;
}
} // namespace warfactory

View File

@ -1,50 +1,54 @@
# World Generation Realist Module
**Responsabilité**: Génération procédurale géologiquement réaliste avec architecture Phase/Step.
**Responsabilité**: Génération procédurale géologiquement réaliste avec architecture Phase/Function simplifiée.
## Description
Ce module implémente un système de génération procédurale basé sur une architecture Phase/Step modulaire. Conçu pour implémenter le système 8-phases défini dans `gameData/WorldGeneration/Regular_world.json` avec Phase 0 d'initialisation.
Ce module implémente un système de génération procédurale basé sur une architecture Phase/Function en 2 couches. Conçu pour implémenter le système 8-phases défini dans `gameData/WorldGeneration/Regular_world.json` avec Phase 0 d'initialisation.
## Architecture
## Architecture Hiérarchique (SIMPLIFIÉE)
### Interfaces Core
- **IWorldGenerationFunction**: Interface unique pour toutes les fonctions de génération
- **WorldGenerationFunctionFactory**: Factory pour créer les fonctions par nom
- **WorldGenerationOrchestrator**: Orchestrateur principal des phases
```
WorldGenerationOrchestrator
└── IWorldGenerationPhase (conteneur logique - ex: "Planetary Accretion")
└── IWorldGenerationFunction[] (implémentations concrètes)
```
### Systèmes Implémentés
**Architecture réduite de 3 → 2 couches**: IWorldGenerationStep supprimé (wrapper redondant).
#### Phase 0: World Initialization
- **InitializeWorldTerrainFunction**: Configuration initiale du terrain (-100°C, -30000m)
- **InitializePlanetaryCoreFunction**: Noyau planétaire avec composition réaliste (1.94e21 tonnes)
### Interfaces Core (Headers)
- **IWorldGenerationFunction**: Interface pour implémentations concrètes (configure/execute/reset)
- **IWorldGenerationPhase**: Conteneur orchestrant plusieurs Functions avec support de cycles
- **WorldGenerationOrchestrator**: Chef d'orchestre exécutant les Phases séquentiellement
- **WorldGenerationFunctionFactory**: Factory pour instancier Functions par nom
- **PhaseRegistry**: Registry pour enregistrer et créer des Phases
#### Phase 1: Planetary Accretion
- **MeteoriteImpactGenerationFunction**: Génération de météorites avec MeteoriteFactory
- **ImpactEffectsApplicationFunction**: Application des effets physiques (cratères, chaleur, dépôts)
### Implémentations Actuelles
**Functions (.h + .cpp)** - 8 fonctions production-ready:
- Phase 0: `InitializeWorldTerrainFunction`, `InitializePlanetaryCoreFunction`
- Phase 1: `MeteoriteImpactGenerationFunction`, `ImpactEffectsApplicationFunction`, `PlanetaryDifferentiationFunction`, `VolcanicRedistributionFunction`, `CoolingPhaseFunction`, `UniversalRegionFusionFunction`
**Data Structures (.h + .cpp)** - Production-ready:
- `WorldData`, `Meteorite`, `MeteoriteFactory`, `PlanetaryCore`, `Volcano`, `VolcanoFactory`, `TectonicRegion`, `ClimateRegion`
**À Implémenter (.cpp)** - Headers finalisés, implémentations manquantes:
- `WorldGenerationPhase`, `WorldGenerationOrchestrator`, `PhaseRegistry`
### Systèmes Techniques
#### MeteoriteFactory (Pattern Pool)
- **Template Pool**: Pré-génération des météorites types
- **Copy + Modify**: Clone des templates avec modifications de taille
- **Feed/Pull Pattern**: Configuration → Génération → Clonage
**MeteoriteFactory (Pattern Pool)**:
- Template Pool → Copy + Modify → Pull pattern pour performance
- Seedable via `RandomGenerator::getInstance()` (OBLIGATOIRE)
#### RandomGenerator Singleton
- **Centralisé**: `RandomGenerator::getInstance()` partout
- **Seedable**: Reproductibilité pour tests et debug
- **Performance**: Un seul générateur MT19937
**Échelle Réaliste**:
- Noyau planétaire: `__uint128_t` (1.94e21 tonnes)
- Gameplay: `uint64_t` (jusqu'à 1e13 tonnes)
#### Échelle Réaliste
- **Noyau**: 1.94e21 tonnes (échelle terrestre réelle)
- **Météorites**: Jusqu'à 1e13 tonnes (cohérent avec meteorites.json)
- **Types**: uint64_t pour gameplay, __uint128_t pour noyau planétaire
### Conception
- **Phase**: Groupe logique d'étapes avec cycles temporels cohérents
- **Step**: Opération atomique de génération (IWorldGenerationFunction)
- **Producer/Consumer**: Séparation génération ↔ application des effets
- **Modularité**: Chaque fonction isolée et testable
**Conception**:
- Functions isolées et testables (200-300 lignes max)
- Pattern Configure → Execute → Reset
- Producer/Consumer pour génération ↔ application des effets
## Contraintes Modules
- **200-300 lignes max** par implémentation
@ -67,14 +71,18 @@ cmake . # Configuration autonome
- `IDataNode`: Interface configuration depuis core/
- `C++20`: Features modernes
### Structure Prévue
### Structure Actuelle
```
include/
├── IWorldGenerationPhase.h # Interface phase
├── IWorldGenerationStep.h # Interface step
└── (futures implémentations...)
├── WorldGenerationFunctions/
│ ├── IWorldGenerationFunction.h # Interface base (✅ finalisée)
│ ├── WorldGenerationFunctionFactory.h # Factory (✅ + .cpp)
│ └── [8 implémentations .h] # Functions concrètes (✅ + .cpp)
├── IWorldGenerationPhase.h # Interface Phase (✅ header only)
├── WorldGenerationOrchestrator.h # Orchestrateur (✅ header only)
└── PhaseRegistry.h # Registry (✅ header only)
```
---
**État actuel**: Interfaces définies, prêt pour implémentations concrètes.
**État actuel**: 8 Functions implémentées, architecture Phase/Function 2-couches définie (headers uniquement).

View File

@ -6,35 +6,110 @@
#include "warfactory/IDataNode.h"
class WorldData;
class IWorldGenerationStep;
namespace warfactory {
/**
* @brief Interface for world generation phases
*
* A phase is a logical grouping of steps that execute sequentially.
* Phases can be executed step-by-step (executeOneStep) or all at once (execute).
*/
class IWorldGenerationPhase {
public:
virtual ~IWorldGenerationPhase() = default;
/**
* @brief Get the human-readable name of this phase
* @return Phase name (e.g., "planetary_accretion")
*/
virtual std::string getPhaseName() const = 0;
virtual std::vector<std::unique_ptr<IWorldGenerationStep>> getSteps() const = 0;
/**
* @brief Get the unique identifier of this phase
* @return Phase ID matching JSON configuration
*/
virtual std::string getPhaseId() const = 0;
/**
* @brief Execute all remaining steps in this phase
* @param world The world data to modify
* @param config Configuration node for this phase
* @return true if all steps completed successfully, false otherwise
*/
virtual bool execute(WorldData& world, const IDataNode& config) = 0;
/**
* @brief Execute the next step in the phase
* @param world The world data to modify
* @param config Configuration node for this phase
* @return true if step completed successfully, false otherwise
*/
virtual bool executeOneStep(WorldData& world, const IDataNode& config) = 0;
/**
* @brief Execute one cycle of the current step
* @param world The world data to modify
* @param config Configuration node for this phase
* @return true if cycle completed successfully, false otherwise
*
* For phases with cycles (duration_cycles in JSON), this executes one cycle.
* For single-shot steps, this is equivalent to executeOneStep().
*/
virtual bool executeOneCycle(WorldData& world, const IDataNode& config) = 0;
/**
* @brief Get the name of the currently executing step
* @return Current step name, or empty string if phase not started or complete
*/
virtual std::string getCurrentStepName() const = 0;
/**
* @brief Get the index of the currently executing step
* @return Current step index (0-based), or -1 if phase not started or complete
*/
virtual int getCurrentStepIndex() const = 0;
/**
* @brief Get the total number of steps in this phase
* @return Total number of steps
*/
virtual int getTotalSteps() const = 0;
/**
* @brief Get the current cycle index within the current step
* @return Current cycle index (0-based), or -1 if no cycles in progress
*/
virtual int getCurrentCycleIndex() const = 0;
/**
* @brief Get the total number of cycles for the current step
* @return Total cycles for current step, or 0 if step not started
*/
virtual int getTotalCyclesForCurrentStep() const = 0;
/**
* @brief Get the current progress of this phase
* @return Progress value between 0.0 (not started) and 1.0 (complete)
*/
virtual float getProgress() const = 0;
/**
* @brief Check if all steps in this phase are complete
* @return true if phase is complete, false otherwise
*/
virtual bool isComplete() const = 0;
/**
* @brief Reset the phase to initial state
*/
virtual void reset() = 0;
/**
* @brief Get the human-readable description of this phase
* @return Phase description from JSON configuration
*/
virtual std::string getPhaseDescription() const = 0;
virtual int getPhaseNumber() const = 0;
virtual std::vector<std::string> getRequiredPreviousPhases() const = 0;
virtual std::vector<std::string> getProducedData() const = 0;
virtual bool canExecute(const WorldData& world) const = 0;
};
} // namespace warfactory

View File

@ -1,40 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include "warfactory/IDataNode.h"
class WorldData;
namespace warfactory {
class IWorldGenerationStep {
public:
virtual ~IWorldGenerationStep() = default;
virtual std::string getStepName() const = 0;
virtual bool execute(WorldData& world, const IDataNode& config) = 0;
virtual bool canExecute(const WorldData& world) const = 0;
virtual float getProgress() const = 0;
virtual bool isComplete() const = 0;
virtual void reset() = 0;
virtual std::string getStepDescription() const = 0;
virtual float getEstimatedDuration() const = 0;
virtual std::vector<std::string> getRequiredPreviousSteps() const = 0;
virtual std::vector<std::string> getProducedData() const = 0;
virtual int getStepOrder() const = 0;
virtual std::string getParentPhase() const = 0;
};
} // namespace warfactory

View File

@ -1,40 +1,49 @@
#pragma once
#include "IWorldGenerationPhase.h"
#include "IWorldGenerationStep.h"
#include <unordered_map>
#include <memory>
#include <functional>
namespace warfactory {
/**
* @brief Registry for world generation phases
*
* Simplified architecture: Phase directly manages Functions (no Step wrapper)
* Functions are registered via WorldGenerationFunctionFactory
*/
class PhaseRegistry {
private:
std::unordered_map<std::string, std::function<std::unique_ptr<IWorldGenerationPhase>()>> phase_factories;
std::unordered_map<std::string, std::function<std::unique_ptr<IWorldGenerationStep>()>> step_factories;
std::unordered_map<std::string, std::function<std::unique_ptr<IWorldGenerationPhase>()>> phase_factories_;
public:
static PhaseRegistry& getInstance();
/**
* @brief Register a phase type with its unique identifier
* @tparam PhaseType Concrete phase implementation
* @param phase_id Unique phase identifier (e.g., "world_initialization")
*/
template<typename PhaseType>
void registerPhase(const std::string& phase_name) {
phase_factories[phase_name] = []() {
void registerPhase(const std::string& phase_id) {
phase_factories_[phase_id] = []() {
return std::make_unique<PhaseType>();
};
}
template<typename StepType>
void registerStep(const std::string& step_name) {
step_factories[step_name] = []() {
return std::make_unique<StepType>();
};
}
std::unique_ptr<IWorldGenerationPhase> createPhase(const std::string& phase_name);
std::unique_ptr<IWorldGenerationStep> createStep(const std::string& step_name);
/**
* @brief Create a phase instance by ID
* @param phase_id Phase identifier from JSON configuration
* @return Unique pointer to phase instance, or nullptr if not found
*/
std::unique_ptr<IWorldGenerationPhase> createPhase(const std::string& phase_id);
/**
* @brief Get list of all registered phase IDs
* @return Vector of registered phase identifiers
*/
std::vector<std::string> getRegisteredPhases() const;
std::vector<std::string> getRegisteredSteps() const;
};
} // namespace warfactory

View File

@ -63,34 +63,6 @@ public:
* After reset(), configure() must be called again.
*/
virtual void reset() = 0;
// ========================================
// OPTIONAL: Progress Tracking (for UI/debug)
// ========================================
/**
* @brief Get execution progress (optional - default implementation)
* @return Progress from 0.0 (not started) to 1.0 (complete)
*/
virtual float getProgress() const { return 0.0f; }
/**
* @brief Check if execution is complete (optional - default implementation)
* @return True if function finished execution
*/
virtual bool isComplete() const { return false; }
/**
* @brief Get human-readable description (optional - default implementation)
* @return Description of what this function does
*/
virtual std::string getFunctionDescription() const { return getStepName(); }
/**
* @brief Get parent phase name (optional - default implementation)
* @return Phase name from Regular_world.json
*/
virtual std::string getParentPhase() const { return "unknown"; }
};
} // namespace warfactory

View File

@ -1,40 +0,0 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "warfactory/IDataNode.h"
class WorldData;
class IWorldGenerationStep;
namespace warfactory {
class IWorldGenerationPhase {
public:
virtual ~IWorldGenerationPhase() = default;
virtual std::string getPhaseName() const = 0;
virtual std::vector<std::unique_ptr<IWorldGenerationStep>> getSteps() const = 0;
virtual bool execute(WorldData& world, const IDataNode& config) = 0;
virtual float getProgress() const = 0;
virtual bool isComplete() const = 0;
virtual void reset() = 0;
virtual std::string getPhaseDescription() const = 0;
virtual int getPhaseNumber() const = 0;
virtual std::vector<std::string> getRequiredPreviousPhases() const = 0;
virtual std::vector<std::string> getProducedData() const = 0;
virtual bool canExecute(const WorldData& world) const = 0;
};
} // namespace warfactory

View File

@ -41,10 +41,17 @@ public:
void reset();
private:
/**
* @brief Build phase pipeline from JSON configuration
* @param config Root configuration node containing "geological_simulation"
*/
void buildPhasesFromConfig(const IDataNode& config);
std::unique_ptr<IWorldGenerationStep> createStepFromConfig(const IDataNode& step_config);
/**
* @brief Create a phase instance from JSON configuration
* @param phase_config Configuration node for a single phase
* @return Unique pointer to configured phase instance
*/
std::unique_ptr<IWorldGenerationPhase> createPhaseFromConfig(const IDataNode& phase_config);
};

View File

@ -1,9 +1,9 @@
#include "src/core/include/warfactory/IUI_Enums.h"
#include "src/core/include/warfactory/ImGuiUI.h"
#include <grove/IUI_Enums.h>
#include <grove/ImGuiUI.h>
#include <iostream>
#include <cstdlib>
using namespace warfactory;
using namespace grove;
void showUsage(const char* program_name) {
std::cout << "Usage: " << program_name << " [options]\n"