Skip to content

Commit e03d891

Browse files
author
tom
committed
Port solo v1 : victoire, flash résolution, réserve QG, options persistées.
Overlay fin de partie, pulse des combats sur la carte, cycle réserve au QG, GameSettings, IA déploie en réserve, README/CHANGELOG v1.
1 parent d62898c commit e03d891

10 files changed

Lines changed: 283 additions & 69 deletions

File tree

godot/CHANGELOG.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,27 @@ Format des entrées : `AAAA-MM-JJ HH:MM:SS` (heure locale).
66

77
---
88

9+
## 2026-05-25 18:00:00 — Port solo v1 (jouable de bout en bout)
10+
11+
### Réalisé
12+
13+
- **Écran victoire** — overlay Rejouer / Menu quand un seul camp survit
14+
- **Résolution visuelle** — flash orange sur les cases en conflit avant résolution des combats
15+
- **Réserve au QG** — cycle pièce inclut la réserve ; clic QG ou bouton Déployer
16+
- **Journal d’ordres** — chaque action humaine loggée en pad dans `%OrdersLog`
17+
- **`GameSettings`** — persistance `user://forces_settings.cfg` (musique / SFX)
18+
- **IA** — déploiement depuis la réserve (normal+)
19+
- **`README.md`** — tableau de statut v1
20+
21+
### Hors scope v1 (volontaire)
22+
23+
- Multijoueur réseau
24+
- Monétisation / publicité (**uniquement sur demande utilisateur**)
25+
- Tuiles directionnelles emboîtées type Unity (le CE générique suffit pour jouer)
26+
- `Battleground.unity` absent — calibration pixel-perfect reportée
27+
28+
---
29+
930
## 2026-05-25 12:00:00 — Plateau tuiles atlas, grilles ancrées QG, sidebar
1031

1132
### Réalisé
@@ -41,11 +62,11 @@ Format des entrées : `AAAA-MM-JJ HH:MM:SS` (heure locale).
4162
- **IA** — destinations via `destinations_for`, bonus mer/air, achats réserve (normal+), fusion H (difficile)
4263
- **Tutoriel** — pages mer/air, combats, bombe H, Power
4364

44-
### À faire (prochaine étape critique)
65+
### À faire (améliorations futures)
4566

4667
- [ ] Tuiles directionnelles emboîtées (échelle Unity) si souhaité
4768
- [ ] `Battleground.unity` pour calibration pixel-perfect
48-
- [ ] Animations fin de manche ; multijoueur ; monétisation
69+
- [ ] Animations déplacement pièces ; multijoueur
4970

5071
---
5172

godot/README.md

Lines changed: 33 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,57 @@
11
# Forces — Godot 4
22

3-
Portage du jeu **Forces** (Unity / JavaScript, stage 42, 2013) vers **Godot 4.6** (4.x compatible).
3+
Portage jouable du jeu **Forces** (Unity / JavaScript, 2013) vers **Godot 4.6**.
44

5-
Le dépôt Unity d’origine reste à la racine (`Assets/`, etc.). Ce dossier est le client Godot autonome.
5+
Le dépôt Unity d’origine reste à la racine (`Assets/`). Ce dossier est le client Godot autonome.
66

7-
## Branche
7+
## Statut (v1 solo)
88

9-
Développement sur `godot4-port`.
9+
| Domaine | État |
10+
|---------|------|
11+
| Menu → config → bataille | OK |
12+
| Plateau atlas (tuiles, mer, QG) | OK |
13+
| Terre / mer / air + surbrillances | OK |
14+
| Power, réserve, achats, échanges 3→1, déploiement QG | OK |
15+
| Bombes H (fusion 100 F, frappe) | OK |
16+
| Combats multi-camps + capture drapeau | OK |
17+
| IA (Facile / Normal / Difficile) | OK |
18+
| Tutoriel, options (volumes persistés) | OK |
19+
| Multijoueur réseau | Non |
20+
| Monétisation / pub | Hors scope (sur demande) |
21+
| Tuiles directionnelles pixel-perfect Unity | Optionnel |
1022

11-
## Ouvrir le projet
23+
## Branche
1224

13-
1. Installer [Godot 4.6](https://godotengine.org/download) (ou la version que tu utilises déjà).
14-
2. Importer le dossier `godot/` comme projet.
15-
## Flux joueur
25+
`godot4-port`
1626

17-
1. **Menu** — Jouer / Partie rapide / Tutoriel / Options / Quitter
18-
2. **Créer partie** — Vert = vous ; Bleu/Rouge/Jaune = IA ou joueur (+ difficulté IA)
19-
3. **Bataille** — planification des ordres (moteur actuel)
27+
## Lancer
2028

21-
F5 démarre sur le **menu principal**.
29+
1. Godot 4.6 → importer `godot/`
30+
2. **F5** : menu principal
31+
3. **Partie rapide** ou **Jouer** → bataille
2232

23-
## Tests headless (CLI / CI / agent)
33+
Contrôles bataille : clic case ; reclic = changer de pièce ; clic destination = ordre ; **Fin manche** = IA + résolution.
2434

25-
Sans ouvrir l’éditeur, pour vérifier compilation + moteur :
35+
## Tests headless
2636

2737
```powershell
2838
cd godot
29-
# Une fois Godot installé, optionnel :
30-
$env:GODOT_BIN = "C:\Users\tom\Downloads\Godot_v4.6.3-stable_win64.exe"
31-
.\tools\run_headless.ps1 # smoke (défaut)
32-
.\tools\run_headless.ps1 compile # charge le projet 3 frames
33-
.\tools\run_headless.ps1 import # importe les assets
34-
```
35-
36-
Linux/macOS :
37-
38-
```bash
39-
cd godot
40-
export GODOT_BIN=/path/to/godot
41-
./tools/run_headless.sh smoke
39+
$env:GODOT_BIN = "C:\...\Godot_v4.6.3-stable_win64_console.exe"
40+
.\tools\run_headless.ps1
4241
```
4342

44-
Équivalent manuel :
45-
46-
```bash
47-
godot --headless --path . "res://scenes/headless_test.tscn"
48-
godot --headless --path . "res://scenes/menu/main_menu.tscn" --quit-after 2
49-
```
50-
51-
Le workflow GitHub `.github/workflows/godot-headless.yml` lance les mêmes checks sur push `godot/**`.
52-
5343
## Architecture
5444

5545
| Dossier | Rôle |
5646
|---------|------|
57-
| `scenes/menu/` | Menu principal, config partie, options, tutoriel |
58-
| `scenes/battle/` | Écran de bataille (ex-UI debug) |
59-
| `scripts/app/game_session.gd` | Autoload — config persistée menu → bataille |
60-
| `scripts/core/` | Moteur sans UI |
61-
| `data/land_adjacency.json` | Graphe terrestre |
62-
63-
## Roadmap
64-
65-
Voir [`CHANGELOG.md`](CHANGELOG.md) pour le détail daté.
47+
| `scenes/menu/` | Menu, config, options, tutoriel |
48+
| `scenes/battle/` | Carte + sidebar |
49+
| `scripts/core/` | Moteur (`game_state`, graphes, combats) |
50+
| `scripts/ai/` | `ai_planner.gd` |
51+
| `data/*.json` | Layout plateau, adjacences, atlas |
6652

67-
1. ~~**Moteur terrestre**~~ — état, ordres multi-camps, résolution simplifiée, capture drapeau
68-
2. ~~**Menus & flux**~~ — menu → config → bataille
69-
3. ~~**IA basique**~~`ai_planner.gd` (Facile/Normal/Difficile)
70-
4. **Mer / air** — parsers `Dep_mer.js` / `Dep_air.js`
71-
5. **Plateau visuel** — assets Unity / JP
72-
6. **Règles Power** — jetons, réserve, échanges, bombes H
73-
7. **UI bataille** — remplacement `infoButtons.js` / `playerTurn.js`
74-
8. **Multijoueur** — WebSocket / ENet
53+
Journal détaillé : [`CHANGELOG.md`](CHANGELOG.md).
7554

7655
## Legacy Unity
7756

78-
Référence : `../Assets/Scripts/`
57+
Référence règles / parsers : `../Assets/Scripts/`

godot/project.godot

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ BoardCatalog="*res://scripts/core/board_catalog.gd"
2727
BoardGraph="*res://scripts/core/board_graph.gd"
2828
GameSession="*res://scripts/app/game_session.gd"
2929
AudioManager="*res://scripts/app/audio_manager.gd"
30+
GameSettings="*res://scripts/app/game_settings.gd"
3031

3132
[display]
3233

godot/scenes/battle/battle.tscn

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,61 @@ layout_mode = 2
199199
size_flags_horizontal = 3
200200
text = "Fin manche"
201201

202+
[node name="GameOverLayer" type="CanvasLayer" parent="."]
203+
layer = 10
204+
visible = false
205+
206+
[node name="Dim" type="ColorRect" parent="GameOverLayer"]
207+
anchors_preset = 15
208+
anchor_right = 1.0
209+
anchor_bottom = 1.0
210+
grow_horizontal = 2
211+
grow_vertical = 2
212+
mouse_filter = 0
213+
color = Color(0.02, 0.04, 0.1, 0.72)
214+
215+
[node name="Center" type="CenterContainer" parent="GameOverLayer"]
216+
anchors_preset = 15
217+
anchor_right = 1.0
218+
anchor_bottom = 1.0
219+
grow_horizontal = 2
220+
grow_vertical = 2
221+
222+
[node name="Panel" type="PanelContainer" parent="GameOverLayer/Center"]
223+
layout_mode = 2
224+
225+
[node name="VBox" type="VBoxContainer" parent="GameOverLayer/Center/Panel"]
226+
layout_mode = 2
227+
theme_override_constants/separation = 12
228+
229+
[node name="GameOverLabel" type="Label" parent="GameOverLayer/Center/Panel/VBox"]
230+
unique_name_in_owner = true
231+
layout_mode = 2
232+
theme_override_font_sizes/font_size = 22
233+
text = "Victoire"
234+
horizontal_alignment = 1
235+
236+
[node name="GameOverHint" type="Label" parent="GameOverLayer/Center/Panel/VBox"]
237+
layout_mode = 2
238+
theme_override_colors/font_color = Color(0.75, 0.78, 0.85, 1)
239+
text = "Un seul camp reste en jeu."
240+
horizontal_alignment = 1
241+
242+
[node name="Buttons" type="HBoxContainer" parent="GameOverLayer/Center/Panel/VBox"]
243+
layout_mode = 2
244+
alignment = 1
245+
theme_override_constants/separation = 10
246+
247+
[node name="ReplayButton" type="Button" parent="GameOverLayer/Center/Panel/VBox/Buttons"]
248+
unique_name_in_owner = true
249+
layout_mode = 2
250+
text = "Rejouer"
251+
252+
[node name="MenuButtonGO" type="Button" parent="GameOverLayer/Center/Panel/VBox/Buttons"]
253+
unique_name_in_owner = true
254+
layout_mode = 2
255+
text = "Menu"
256+
202257
[node name="DebugLayer" type="Control" parent="."]
203258
visible = false
204259
layout_mode = 1
@@ -258,6 +313,8 @@ offset_top = 208.0
258313
offset_right = 400.0
259314
offset_bottom = 300.0
260315

316+
[connection signal="pressed" from="GameOverLayer/Center/Panel/VBox/Buttons/ReplayButton" to="." method="_on_replay_pressed"]
317+
[connection signal="pressed" from="GameOverLayer/Center/Panel/VBox/Buttons/MenuButtonGO" to="." method="_on_menu_pressed"]
261318
[connection signal="pressed" from="Sidebar/MenuRow/MenuButton" to="." method="_on_menu_pressed"]
262319
[connection signal="pressed" from="Sidebar/MenuRow/EndRoundButton" to="." method="_on_end_round_pressed"]
263320
[connection signal="pressed" from="Sidebar/ReservePanel/VBox/ReserveActions/BuySoldierButton" to="." method="_on_buy_soldier_pressed"]

godot/scripts/ai/ai_planner.gd

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ static func plan_turn(state: GameState, camp: GameConstants.Camp, difficulty: Ga
5353
if err.is_empty():
5454
logs.append(" · %s : %s%s" % [piece.label(), from_sector, dest])
5555
_maybe_ai_buy(state, camp, difficulty, rng, logs)
56+
_maybe_ai_deploy(state, camp, difficulty, rng, logs)
5657
_maybe_ai_hbomb(state, camp, difficulty, rng, logs)
5758
if logs.is_empty():
5859
logs.append(" · (aucun ordre)")
@@ -213,6 +214,33 @@ static func _maybe_ai_buy(
213214
logs.append(" · achat %s (réserve)" % GameConstants.piece_type_label(pick))
214215

215216

217+
static func _maybe_ai_deploy(
218+
state: GameState,
219+
camp: GameConstants.Camp,
220+
difficulty: GameSession.Difficulty,
221+
rng: RandomNumberGenerator,
222+
logs: Array[String],
223+
) -> void:
224+
if difficulty == GameSession.Difficulty.EASY:
225+
return
226+
if state.camp_orders_used(camp) >= GameConstants.MAX_ORDERS_PER_ROUND:
227+
return
228+
var reserves: Array[PieceInstance] = state.reserve_pieces(camp)
229+
if reserves.is_empty() or rng.randf() > 0.55:
230+
return
231+
reserves.sort_custom(func(a: PieceInstance, b: PieceInstance) -> bool:
232+
return a.combat_force() > b.combat_force()
233+
)
234+
var hq: String = BoardCatalog.hq_for_camp(camp)
235+
for piece: PieceInstance in reserves:
236+
if state.has_piece_moved(piece.id):
237+
continue
238+
var err: String = state.try_deploy_from_reserve(piece, hq)
239+
if err.is_empty():
240+
logs.append(" · déploiement %s sur %s" % [piece.label(), hq])
241+
return
242+
243+
216244
static func _maybe_ai_hbomb(
217245
state: GameState,
218246
camp: GameConstants.Camp,

godot/scripts/app/game_settings.gd

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
extends Node
2+
## Persistance locale (volumes, etc.) — user://forces_settings.cfg
3+
4+
const CFG_PATH := "user://forces_settings.cfg"
5+
6+
7+
func _ready() -> void:
8+
load_into_session()
9+
10+
11+
func load_into_session() -> void:
12+
var file := ConfigFile.new()
13+
if file.load(CFG_PATH) != OK:
14+
return
15+
GameSession.music_volume = clampf(float(file.get_value("audio", "music", 0.8)), 0.0, 1.0)
16+
GameSession.sfx_volume = clampf(float(file.get_value("audio", "sfx", 0.8)), 0.0, 1.0)
17+
18+
19+
func save_from_session() -> void:
20+
var file := ConfigFile.new()
21+
file.set_value("audio", "music", GameSession.music_volume)
22+
file.set_value("audio", "sfx", GameSession.sfx_volume)
23+
file.save(CFG_PATH)

0 commit comments

Comments
 (0)