diff --git a/CLAUDE.md b/CLAUDE.md index edd78c0..c832d03 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,6 +10,7 @@ Jeu éducatif de français CE1 à CM2 (7–11 ans), aventure 2D, hors ligne, God Le `Makefile` est le seul point d'entrée pour les opérations système. Tenir cette liste à jour. +- `make help` : liste les commandes (toute cible du Makefile suivie de `## description` y figure : tenir ces commentaires à jour) - `make setup` : installe l'outillage (lint, tests, modèles d'export) - `make import` : importe les ressources dans `.godot/` (deux passes ; appelé automatiquement par `run`, `test`, `validate-data`, `export-*`, `screenshot` : indispensable sur un dépôt fraîchement cloné) - `make run` : lance le jeu @@ -22,7 +23,8 @@ Le `Makefile` est le seul point d'entrée pour les opérations système. Tenir c - `make generate-sounds` : génère les effets sonores et musiques d'ambiance (synthèse procédurale numpy + `ffmpeg`) dans `assets/audio/sfx/` et `assets/audio/music/`, sans écraser l'existant (`--force` pour régénérer) - `make export-linux` / `make export-windows` : exports dans `build/` - `make screenshot SCENE=… OUT=… ARGS=…` : capture le rendu réel d'une scène dans une fenêtre (outil de développement, ouvre brièvement une fenêtre) -- `make package` : réexporte puis produit les archives distribuables (exécutable unique + licence + crédits + notice) et `SHA256SUMS` dans `dist/` +- `make export-android` / `make export-macos` / `make export-web` : APK Android (arm64 + x86_64, aussi pour ChromeOS), application macOS universelle (zip, signature ad hoc), version web +- `make package` : réexporte puis produit les archives distribuables (Linux, Windows, Android, macOS, ChromeOS ; licence + crédits + notice) et `SHA256SUMS` dans `dist/` ## Conventions de code diff --git a/Makefile b/Makefile index de243a2..48a7728 100644 --- a/Makefile +++ b/Makefile @@ -6,9 +6,17 @@ PYRIGHT := $(VENV)/bin/pyright PYTHON := $(VENV)/bin/python3 GD_SOURCES := autoload scripts tests -.PHONY: setup import run lint format validate-data test check content-review generate-dictation-audio generate-sounds export-linux export-windows package screenshot +.DEFAULT_GOAL := help -setup: +.PHONY: help setup import run lint format validate-data test check content-review generate-dictation-audio generate-sounds export-linux export-windows export-android export-macos export-web package screenshot + +# Liste les commandes : toute cible suivie de « ## description » y figure. +help: ## Affiche cette aide + @echo "Les Aventuriers des mots - commandes disponibles :" + @grep -E '^[a-zA-Z0-9_-]+:.*## ' $(MAKEFILE_LIST) | \ + awk 'BEGIN {FS = ":.*## "} {printf " make %-26s %s\n", $$1, $$2}' + +setup: ## Installe l'outillage (Godot, modeles d'export, lint, tests) tools/setup.sh # Importe les ressources (polices, SVG, audio...) dans .godot/. Indispensable @@ -16,49 +24,70 @@ setup: # jeu sans theme. Deux passes : le theme n'est valide qu'une fois les polices # importees. La premiere passe peut echouer (theme charge avant les polices) : # son code de retour est ignore, la seconde fait foi. -import: +import: ## Importe les ressources dans .godot/ (deux passes) -@$(GODOT_BIN) --headless --path . --import >/dev/null 2>&1 $(GODOT_BIN) --headless --path . --import >/dev/null -run: import +run: import ## Lance le jeu $(GODOT_BIN) --path . -lint: +lint: ## Analyse statique (gdlint, pyright) $(GDLINT) $(GD_SOURCES) $(PYRIGHT) tools/ -format: +format: ## Formate le GDScript $(GDFORMAT) $(GD_SOURCES) -validate-data: import +validate-data: import ## Valide les JSON de data/ contre leurs schemas $(GODOT_BIN) --headless --path . -s res://scripts/data/validate_data_cli.gd -test: import +test: import ## Lance les tests en mode headless $(GODOT_BIN) --headless --path . -s res://addons/gut/gut_cmdln.gd -gdir=res://tests -ginclude_subdirs -gexit -check: lint validate-data test +check: lint validate-data test ## lint + validate-data + test (avant chaque commit) -content-review: +content-review: ## Regenere docs/review/content-review.md $(PYTHON) tools/generate_content_review.py -generate-dictation-audio: +generate-dictation-audio: ## Genere l'audio provisoire des dictees (Piper) $(PYTHON) tools/generate_dictation_audio.py -generate-sounds: +generate-sounds: ## Genere les effets sonores et musiques $(PYTHON) tools/generate_sounds.py -export-linux: import +export-linux: import ## Exporte la version Linux dans build/ mkdir -p build/linux $(GODOT_BIN) --headless --path . --export-release "Linux" build/linux/aventuriers_des_mots.x86_64 -export-windows: import +export-windows: import ## Exporte la version Windows dans build/ mkdir -p build/windows $(GODOT_BIN) --headless --path . --export-release "Windows" build/windows/aventuriers_des_mots.exe -package: +package: ## Reexporte tout et produit les archives dans dist/ tools/package.sh # Capture d'une scene dans une vraie fenetre (voir tools/screenshot.gd) : # make screenshot SCENE=res://scenes/world/world_map.tscn OUT=/tmp/map.png ARGS="CE2" -screenshot: import +screenshot: import ## Capture une scene (SCENE=... OUT=... ARGS=...) $(GODOT_BIN) --path . -s res://tools/screenshot.gd -- $(SCENE) $(OUT) $(ARGS) + +# Android : APK unique (arm64 + x86_64, donc aussi les Chromebooks). Signature +# de test avec la cle de debogage de Godot (standard, publique) : aucune cle +# privee dans le depot. Pour publier sur un magasin, fournir sa propre cle via +# GODOT_ANDROID_KEYSTORE_RELEASE_PATH / _USER / _PASSWORD. +ANDROID_DEBUG_KEYSTORE ?= $(HOME)/.local/share/godot/keystores/debug.keystore + +export-android: import ## Exporte l'APK Android / ChromeOS dans build/android/ + mkdir -p build/android + GODOT_ANDROID_KEYSTORE_RELEASE_PATH="$${GODOT_ANDROID_KEYSTORE_RELEASE_PATH:-$(ANDROID_DEBUG_KEYSTORE)}" \ + GODOT_ANDROID_KEYSTORE_RELEASE_USER="$${GODOT_ANDROID_KEYSTORE_RELEASE_USER:-androiddebugkey}" \ + GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD="$${GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD:-android}" \ + $(GODOT_BIN) --headless --path . --export-release "Android" build/android/aventuriers_des_mots.apk + +export-macos: import ## Exporte l'application macOS (zip) dans build/macos/ + mkdir -p build/macos + $(GODOT_BIN) --headless --path . --export-release "macOS" build/macos/aventuriers_des_mots.zip + +export-web: import ## Exporte la version Web (navigateur, ChromeOS) dans build/web/ + mkdir -p build/web + $(GODOT_BIN) --headless --path . --export-release "Web" build/web/index.html diff --git a/README-EN.md b/README-EN.md index 746a5c0..1d17938 100644 --- a/README-EN.md +++ b/README-EN.md @@ -2,7 +2,7 @@ An educational French-language game for 7-9 year olds (French grades CE1-CE2): an offline 2D adventure with an explorable map, a guide character, and grammar, spelling, and dictation exercises. -Open source project, in development. Current status: version 0.8.0, playable offline on Linux and Windows — profiles, saves (with export and restore in the adult area), stars and an item collection, sound effects and music, settings (volumes, text size, animations), a progress dashboard for adults, three exercise types (multiple choice, sentence correction, dictation), CE1, CE2, CM1 and CM2 in three boards each (homophones, verbs, word spelling), 111 missions. All pedagogical content is still `draft` (awaiting validation) and dictation voices are placeholders (see `assets/CREDITS.md`; see `docs/specs/decisions.md` for scoping decisions and `docs/specs/cahier-des-charges.md` for the full specification, both in French — this is a French-curriculum product). +Open source project, in development. Current status: version 0.9.0, playable offline on Linux, Windows, macOS, Android and ChromeOS (macOS, Android and web builds untested on real hardware) — profiles, saves (with export and restore in the adult area), stars and an item collection, sound effects and music, settings (volumes, text size, animations), a progress dashboard for adults, three exercise types (multiple choice, sentence correction, dictation), CE1, CE2, CM1 and CM2 in three boards each (homophones, verbs, word spelling), 111 missions. All pedagogical content is still `draft` (awaiting validation) and dictation voices are placeholders (see `assets/CREDITS.md`; see `docs/specs/decisions.md` for scoping decisions and `docs/specs/cahier-des-charges.md` for the full specification, both in French — this is a French-curriculum product). ## Tech stack @@ -29,7 +29,8 @@ make run # runs the game | `make check` | lint + validate-data + test, must pass before every commit | | `make content-review` | regenerates `docs/review/content-review.md` (content validation document) | | `make generate-dictation-audio` | generates the placeholder dictation audio | -| `make export-linux` / `make export-windows` | exports into `build/` | +| `make help` | lists the available commands | +| `make export-linux` / `make export-windows` / `make export-android` / `make export-macos` / `make export-web` | exports into `build/` | | `make package` | builds the distributable archives in `dist/` (single executable + license + credits) | ## Structure diff --git a/README.md b/README.md index bba45a8..774b847 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Jeu éducatif de français pour les CE1-CE2 (7-9 ans) : une aventure 2D hors ligne, avec une carte à explorer, un personnage guide, et des exercices de grammaire, d'orthographe et de dictées. -Projet open source, en développement. Statut actuel : version 0.8.0, jouable hors ligne sur Linux et Windows — profils, sauvegardes (avec export et restauration dans l'espace adulte), étoiles et collection d'objets, sons et musique, réglages (volumes, taille du texte, animations), tableau de progrès pour l'adulte, trois types d'exercices (choix multiple, correction de phrase, dictée), CE1, CE2, CM1 et CM2 en trois tableaux chacun (homophones, verbes, orthographe des mots), 111 missions. Contenu pédagogique entièrement en brouillon (`draft`, à valider) et voix des dictées provisoires (voir `assets/CREDITS.md` ; voir `docs/specs/decisions.md` pour les décisions de cadrage et `docs/specs/cahier-des-charges.md` pour la spécification complète). +Projet open source, en développement. Statut actuel : version 0.9.0, jouable hors ligne sur Linux, Windows, macOS, Android et ChromeOS (versions macOS, Android et web non testées sur matériel) — profils, sauvegardes (avec export et restauration dans l'espace adulte), étoiles et collection d'objets, sons et musique, réglages (volumes, taille du texte, animations), tableau de progrès pour l'adulte, trois types d'exercices (choix multiple, correction de phrase, dictée), CE1, CE2, CM1 et CM2 en trois tableaux chacun (homophones, verbes, orthographe des mots), 111 missions. Contenu pédagogique entièrement en brouillon (`draft`, à valider) et voix des dictées provisoires (voir `assets/CREDITS.md` ; voir `docs/specs/decisions.md` pour les décisions de cadrage et `docs/specs/cahier-des-charges.md` pour la spécification complète). ## Pile technique @@ -29,7 +29,8 @@ make run # lance le jeu | `make check` | lint + validate-data + test, à passer avant chaque commit | | `make content-review` | régénère `docs/review/content-review.md` (document de validation du contenu) | | `make generate-dictation-audio` | génère l'audio provisoire des dictées | -| `make export-linux` / `make export-windows` | exports dans `build/` | +| `make help` | liste les commandes disponibles | +| `make export-linux` / `make export-windows` / `make export-android` / `make export-macos` / `make export-web` | exports dans `build/` | | `make package` | produit les archives distribuables dans `dist/` (exécutable unique + licence + crédits) | ## Structure diff --git a/docs/specs/decisions.md b/docs/specs/decisions.md index 1f3c5e7..bca61da 100644 --- a/docs/specs/decisions.md +++ b/docs/specs/decisions.md @@ -425,3 +425,12 @@ Volume : 6 tableaux, 18 règles, 144 QCM, 72 corrections, 54 dictées (voix Pipe - **Indice qui donnait la réponse** (« avoir » avec « je ») reformulé. Les corrections et les dictées CE1/CE2 n'ont pas révélé d'autre problème ; les tests de doublons, quasi-doublons et de position de la bonne réponse sont verts. **Non vérifié** : relecture par un enseignant. + +**Plateformes et touche Entrée (0.9.0)** + +- **Entrée valide** : dans la dictée et dans les champs de correction, la touche Entrée équivaut au bouton « Valider ». L'hôte d'exercice ignore toute validation tant que le panneau de retour est ouvert (une seule réponse comptée par essai). +- **Nouvelles cibles** : `make export-android` (APK arm64 + x86_64), `make export-macos` (application universelle, zip, signature ad hoc, non notariée), `make export-web` (Compatibility, sans threads). `make package` produit cinq archives. +- **ChromeOS** : pas de cible propre au moteur ; l'archive regroupe l'APK (runtime Android) et la version web à servir en local. Le binaire Linux couvre aussi Crostini. +- **Signature Android** : clé de débogage publique de Godot, via variables d'environnement (aucun secret dans le dépôt). Une publication sur un magasin demandera une vraie clé (`GODOT_ANDROID_KEYSTORE_RELEASE_*`). +- **Réglage de projet** : import ETC2/ASTC activé (exigé par les exports mobile et macOS universel). +- **Non vérifié** : aucune des trois nouvelles versions n'a été lancée sur un appareil. Le tactile (clavier virtuel Android pour les champs de saisie, taille des boutons), le stockage `user://` et l'audio sur Android/web restent à tester à la main. diff --git a/docs/testing/manual-checklist.md b/docs/testing/manual-checklist.md index af8f132..7dff90a 100644 --- a/docs/testing/manual-checklist.md +++ b/docs/testing/manual-checklist.md @@ -75,3 +75,10 @@ Tout ce qui suit ne peut pas être vérifié sans écran (rendu, mise en page, a - Export macOS, Android, iOS : hors périmètre de cette version (voir prompt de lancement, Objectif). - Clavier virtuel tactile, formats d'écran mobile : hors périmètre de cette version. - Animations : aucune n'existe encore (interface statique assumée pour ce prototype). + +## Plateformes supplémentaires (0.9.0) + +- `make export-android` : APK structurellement vérifié (paquet, ABI arm64-v8a et x86_64, signature) ; **jamais installé** : à tester sur une tablette, notamment le clavier virtuel dans la dictée et les champs de correction, la taille des boutons au doigt, la sauvegarde et le son. +- `make export-macos` : archive vérifiée (structure .app, signature ad hoc) ; **jamais lancée** (pas de Mac). Au premier lancement : clic droit > Ouvrir. +- `make export-web` : fichiers générés ; **jamais ouvert dans un navigateur**. Servir `build/web/` avec `python3 -m http.server`, ouvrir dans Chrome, vérifier son (le navigateur exige un premier clic), sauvegarde et plein écran. +- Touche Entrée : en dictée et en correction, Entrée valide ; une seconde pression sur Entrée pendant le retour ne doit pas compter une réponse de plus. diff --git a/export_presets.cfg b/export_presets.cfg index d1a4290..56108ef 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -45,3 +45,97 @@ script_export_mode=2 binary_format/architecture="x86_64" binary_format/embed_pck=true + +[preset.2] + +name="Android" +platform="Android" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="tests/*, addons/gut/*" +export_path="build/android/aventuriers_des_mots.apk" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.2.options] + +gradle_build/use_gradle_build=false +package/unique_name="org.deunix.aventuriersdesmots" +package/name="Les Aventuriers des mots" +package/signed=true +version/code=900 +version/name="0.9.0" +architectures/armeabi-v7a=false +architectures/arm64-v8a=true +architectures/x86=false +architectures/x86_64=true +screen/immersive_mode=true +screen/support_small=true +screen/support_normal=true +screen/support_large=true +screen/support_xlarge=true +permissions/internet=false + +[preset.3] + +name="macOS" +platform="macOS" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="tests/*, addons/gut/*" +export_path="build/macos/aventuriers_des_mots.zip" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.3.options] + +binary_format/architecture="universal" +application/bundle_identifier="org.deunix.aventuriersdesmots" +application/short_version="0.9.0" +application/version="0.9.0" +application/min_macos_version_x86_64="10.12" +application/min_macos_version_arm64="11.0" +codesign/codesign=1 +notarization/notarization=0 + +[preset.4] + +name="Web" +platform="Web" +runnable=true +advanced_options=false +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="tests/*, addons/gut/*" +export_path="build/web/index.html" +patches=PackedStringArray() +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_export_mode=2 + +[preset.4.options] + +variant/thread_support=false +vram_texture_compression/for_desktop=true +vram_texture_compression/for_mobile=false +progressive_web_app/enabled=false diff --git a/project.godot b/project.godot index 7825e88..bc6621a 100644 --- a/project.godot +++ b/project.godot @@ -11,7 +11,7 @@ config_version=5 [application] config/name="Les Aventuriers des mots" -config/version="0.8.3" +config/version="0.9.0" run/main_scene="res://scenes/boot/boot.tscn" config/features=PackedStringArray("4.6", "Forward Plus") config/icon="res://icon.svg" @@ -36,3 +36,7 @@ window/stretch/aspect="expand" [gui] theme/custom="res://assets/interface/theme.tres" + +[rendering] + +textures/vram_compression/import_etc2_astc=true diff --git a/scenes/exercises/dictation.gd b/scenes/exercises/dictation.gd index 269dae4..89b979a 100644 --- a/scenes/exercises/dictation.gd +++ b/scenes/exercises/dictation.gd @@ -30,6 +30,8 @@ func _ready() -> void: _replay_button.pressed.connect(_on_replay_pressed) _validate_button.pressed.connect(_on_validate_pressed) + # La touche Entree valide comme le bouton. + _text_edit.text_submitted.connect(func(_text: String) -> void: _on_validate_pressed()) func load_exercise(dictation: Dictionary) -> void: diff --git a/scenes/exercises/exercise_host.gd b/scenes/exercises/exercise_host.gd index b7b5c61..60d6c36 100644 --- a/scenes/exercises/exercise_host.gd +++ b/scenes/exercises/exercise_host.gd @@ -24,6 +24,7 @@ var _attempt_number := 1 var _completed_count := 0 var _seen_exercise_ids: Array[String] = [] var _outcomes: Array[String] = [] +var _feedback_open := false var _pending_outcome := "" @onready var _exercise_container: Control = %ExerciseContainer @@ -155,6 +156,10 @@ func _on_dictation_submitted(submitted_text: String) -> void: func _handle_result(result: CorrectionResult) -> void: + # La touche Entree peut re-valider une saisie encore focalisee derriere le + # panneau de retour : une seule reponse est comptee par essai. + if _feedback_open: + return var support_level := AttemptFlow.SupportLevel.EXPLANATION var is_terminal := result.is_correct @@ -183,6 +188,7 @@ func _show_feedback( result: CorrectionResult, support_level: AttemptFlow.SupportLevel, is_terminal: bool ) -> void: var feedback: Control = preload("res://scenes/ui/feedback_panel.tscn").instantiate() + _feedback_open = true _overlay_container.add_child(feedback) feedback.show_result(result, support_level) feedback.continue_pressed.connect(_on_feedback_continued.bind(feedback, is_terminal)) @@ -190,6 +196,7 @@ func _show_feedback( func _on_feedback_continued(feedback: Control, is_terminal: bool) -> void: feedback.queue_free() + _feedback_open = false if not is_terminal: _attempt_number += 1 diff --git a/scenes/exercises/sentence_correction.gd b/scenes/exercises/sentence_correction.gd index da231be..2bbd361 100644 --- a/scenes/exercises/sentence_correction.gd +++ b/scenes/exercises/sentence_correction.gd @@ -45,6 +45,8 @@ func _on_token_toggled(pressed: bool, index: int) -> void: if not _correction_edits.has(index): var edit := LineEdit.new() edit.placeholder_text = UIText.get_text("sentence_correction.correction_placeholder") + # La touche Entree valide comme le bouton. + edit.text_submitted.connect(func(_text: String) -> void: _on_validate_pressed()) _corrections_box.add_child(edit) _correction_edits[index] = edit elif _correction_edits.has(index): diff --git a/tests/exercises/test_enter_key_validation.gd b/tests/exercises/test_enter_key_validation.gd new file mode 100644 index 0000000..367418c --- /dev/null +++ b/tests/exercises/test_enter_key_validation.gd @@ -0,0 +1,34 @@ +extends GutTest +## La touche Entree valide une saisie comme le bouton « Valider ». + + +func test_enter_in_the_correction_field_submits_the_answer() -> void: + var scene: Control = load("res://scenes/exercises/sentence_correction.tscn").instantiate() + add_child_autofree(scene) + await wait_process_frames(1) + var exercise: Dictionary = DataLoader.read_json( + "res://data/exercises/ce1/ce1_accord_adjectif_correction_01.json" + ) + scene.load_exercise(exercise) + await wait_process_frames(1) + + (scene.get_node("%TokensBox").get_child(1) as Button).toggled.emit(true) + await wait_process_frames(1) + var edit: LineEdit = scene.get_node("%CorrectionsBox").get_child(0) + edit.text = "petits" + + watch_signals(scene) + edit.text_submitted.emit(edit.text) + assert_signal_emitted_with_parameters(scene, "answer_submitted", [[1], {1: "petits"}]) + + +func test_enter_in_the_dictation_field_submits_the_text() -> void: + var scene: Control = load("res://scenes/exercises/dictation.tscn").instantiate() + add_child_autofree(scene) + await wait_process_frames(1) + var edit: LineEdit = scene.get_node("%TextEdit") + edit.text = "La fleur est belle." + + watch_signals(scene) + edit.text_submitted.emit(edit.text) + assert_signal_emitted_with_parameters(scene, "answer_submitted", ["La fleur est belle."]) diff --git a/tests/exercises/test_enter_key_validation.gd.uid b/tests/exercises/test_enter_key_validation.gd.uid new file mode 100644 index 0000000..054b1c5 --- /dev/null +++ b/tests/exercises/test_enter_key_validation.gd.uid @@ -0,0 +1 @@ +uid://bahf2o3ldfkgv diff --git a/tools/package.sh b/tools/package.sh index 3e769fc..ccfaa98 100755 --- a/tools/package.sh +++ b/tools/package.sh @@ -9,13 +9,24 @@ cd "$(dirname "$0")/.." VERSION="$(sed -n 's/^config\/version="\(.*\)"/\1/p' project.godot)" [ -n "${VERSION}" ] || { echo "config/version absent de project.godot" >&2; exit 1; } +# Les presets d'export portent leur propre numero de version : on les aligne +# sur project.godot (version/code = major*10000 + minor*100 + patch). +IFS=. read -r V_MAJOR V_MINOR V_PATCH <<<"${VERSION}" +VERSION_CODE=$((V_MAJOR * 10000 + V_MINOR * 100 + V_PATCH)) +sed -i \ + -e "s/^version\/name=.*/version\/name=\"${VERSION}\"/" \ + -e "s/^version\/code=.*/version\/code=${VERSION_CODE}/" \ + -e "s/^application\/short_version=.*/application\/short_version=\"${VERSION}\"/" \ + -e "s/^application\/version=.*/application\/version=\"${VERSION}\"/" \ + export_presets.cfg + rm -rf build dist -make export-linux export-windows >/dev/null +make export-linux export-windows export-android export-macos export-web >/dev/null write_notice() { cat > "$1/LISEZMOI.txt" < SHA256SUMS) -rm -rf "dist/${LINUX}" "dist/${WINDOWS}" +ANDROID="aventuriers-des-mots-${VERSION}-android" +MACOS="aventuriers-des-mots-${VERSION}-macos-universal" +CHROMEOS="aventuriers-des-mots-${VERSION}-chromeos" + +stage "${ANDROID}" build/android/aventuriers_des_mots.apk \ + "copier l'APK sur la tablette ou le téléphone, l'ouvrir et accepter l'installation depuis cette source (Android 7 ou plus ; arm64 et x86_64)" \ + "stockage privé de l'application (Réglages Android > Applications)" + +# macOS : application non signée par un développeur identifié (signature ad hoc). +stage "${MACOS}" build/macos/aventuriers_des_mots.zip \ + "décompresser l'archive interne, puis clic droit > Ouvrir la première fois (application non notariée)" \ + "~/Library/Application Support/Godot/app_userdata/Les Aventuriers des mots/" +unzip -q "dist/${MACOS}/aventuriers_des_mots.zip" -d "dist/${MACOS}" +rm "dist/${MACOS}/aventuriers_des_mots.zip" + +# ChromeOS : l'APK (runtime Android) ou la version Web servie en local. +stage "${CHROMEOS}" build/android/aventuriers_des_mots.apk \ + "soit installer l'APK (Chromebook avec Android activé), soit servir le dossier web/ (voir LISEZMOI-WEB.txt)" \ + "stockage privé de l'application, ou du navigateur pour la version web" +mkdir -p "dist/${CHROMEOS}/web" +cp build/web/* "dist/${CHROMEOS}/web/" +cat > "dist/${CHROMEOS}/LISEZMOI-WEB.txt" < SHA256SUMS) +rm -rf "dist/${LINUX}" "dist/${WINDOWS}" "dist/${ANDROID}" "dist/${MACOS}" "dist/${CHROMEOS}" ls -lh dist