Bibliothèque de sprites animaux CraftPix (1/3) : catalogue, galerie, accès admin
Premier commit d'une fonctionnalité découpée en plusieurs lots (voir le plan "Bibliothèque de sprites animaux CraftPix") : intègre 14 familles d'animaux (15 variantes de couleur chacune) comme personnages Forge sélectionnables, à côté des 6 Kenney existants — réservé au rôle admin, licence CraftPix oblige (interdiction contractuelle de rendre ces sprites utilisables par un compte "user" via l'application). - screens/labels/animal_sprite_library.py (nouveau) : charge un manifest JSON généré une fois (voir scripts/generate_animal_sprite_manifest.py, commit suivant) et construit ADMIN_SPRITE_LIBRARY, dans le même format que l'existant PUBLIC_SPRITE_LIBRARY (screens/labels/sprite_library.py, ex-SPRITE_LIBRARY, renommé pour distinguer les deux). screens.SPRITE_LIBRARY reste le catalogue FUSIONNÉ (utilisé par resolve_personnage_animations pour la résolution runtime, sans filtrage par rôle — voir le constat d'exploration : le payload de jeu et /jouer/<slug> ne vérifient déjà aucun rôle nulle part). - screens/labels/sprite_gallery.py (nouveau) : sprite_gallery_families() groupe la galerie par famille — un animal n'apparaît qu'une fois (sa variante "de base"), ses 15 couleurs se choisissent depuis le panneau de propriétés (render_variant_gallery, templates/screen_edit.html), répondant à la suggestion de l'utilisateur plutôt que d'encombrer la galerie d'ajout de 210 tuiles quasi identiques. - routes/screens/screen_edit.py, routes/scenes/scene_edit_view.py : la galerie passée au template est filtrée par rôle (PUBLIC_SPRITE_LIBRARY pour un compte "user", SPRITE_LIBRARY complet pour un admin) — même idiome que core/auth_guard.py. - core/sprite_gate.py (nouveau) + 4 routes d'écriture (element_add, element_set_personnage_data, scene_object_add, scene_object_personnage_data) : ferme la brèche d'un POST direct qui contournerait la galerie filtrée (403 si un compte non-admin tente d'assigner un personnage animal). - tests/conftest.py : nouvelles fixtures user_client/user_game (compte "user" non-admin avec un projet assigné) pour tester le filtrage par rôle de bout en bout. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
43abfc9b93
commit
449c36fd5d
+13
-1
@@ -55,7 +55,19 @@ from .labels.element_action_properties import (
|
||||
)
|
||||
from .labels.element_visibility import ELEMENT_VISIBILITY_VALUES, ELEMENT_VISIBILITY_LABELS
|
||||
from .labels.data_operations import DATA_OPERATIONS, DATA_OPERATION_LABELS
|
||||
from .labels.sprite_library import SPRITE_LIBRARY
|
||||
from .labels.sprite_library import PUBLIC_SPRITE_LIBRARY
|
||||
from .labels.animal_sprite_library import ADMIN_SPRITE_LIBRARY
|
||||
|
||||
# SPRITE_LIBRARY (fusionné) : utilisé par resolve_personnage_animations
|
||||
# pour la résolution RUNTIME (jeu jouable, tous rôles confondus — voir le
|
||||
# plan "Bibliothèque de sprites animaux CraftPix" : aucun filtrage par
|
||||
# rôle au runtime, seulement à la galerie/l'écriture). Les routes qui
|
||||
# alimentent la galerie de l'éditeur doivent utiliser PUBLIC_SPRITE_LIBRARY
|
||||
# ou ADMIN_SPRITE_LIBRARY selon le rôle, jamais SPRITE_LIBRARY directement.
|
||||
SPRITE_LIBRARY = {**PUBLIC_SPRITE_LIBRARY, **ADMIN_SPRITE_LIBRARY}
|
||||
ADMIN_ONLY_CHARACTER_SLUGS = set(ADMIN_SPRITE_LIBRARY)
|
||||
from .labels.sprite_gallery import sprite_gallery_families
|
||||
from .rendering.list_used_forge_characters import list_used_forge_characters
|
||||
|
||||
from .flow.constants import (
|
||||
TRIGGER_EVENTS, CONDITION_OPERATORS, CONDITION_OPERATOR_LABELS, CLICKED_ROW_ID, LAST_INSERTED_ROW_ID,
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
# Bibliothèque de sprites animaux CraftPix — RÉSERVÉE au rôle admin (voir
|
||||
# routes/screens/screen_edit.py, routes/scenes/scene_edit_view.py, et les
|
||||
# routes d'écriture screens/scene-objects "personnage"/"add"). Licence
|
||||
# CraftPix : interdiction de rendre ces sprites utilisables par un compte
|
||||
# "user" via l'application (voir le plan "Bibliothèque de sprites animaux
|
||||
# CraftPix"). Générée une fois par scripts/generate_animal_sprite_manifest.py
|
||||
# depuis assets/characters/animal_sprite/ (pack source, non versionné) vers
|
||||
# static/characters/animals/<animal>/<NN>/<pose><i>.png, avec un manifest
|
||||
# JSON décrivant le compte RÉEL de frames par pose/variante (variable selon
|
||||
# les packs — pas de table figée comme _SEQUENCE_ANIMATIONS pour Kenney).
|
||||
_MANIFEST_PATH = os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
|
||||
"static", "characters", "animals", "manifest.json",
|
||||
)
|
||||
|
||||
|
||||
def _load_manifest():
|
||||
try:
|
||||
with open(_MANIFEST_PATH, encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
except FileNotFoundError:
|
||||
# Poste de dev sans les assets animaux copiés (script pas encore
|
||||
# lancé), ou paquet exporté pour un jeu qui n'utilise aucun
|
||||
# personnage animal (voir publish/build_package.py, copie
|
||||
# sélective) : aucun personnage animal disponible, pas une erreur.
|
||||
return {}
|
||||
|
||||
|
||||
def _slug(animal, nn):
|
||||
return f"animal-{animal}-{nn}"
|
||||
|
||||
|
||||
def _label(family_label, nn):
|
||||
n = int(nn)
|
||||
return f"{family_label} (variante {n})" if n > 1 else f"{family_label} (de base)"
|
||||
|
||||
|
||||
def _animations_for(animal, nn, poses):
|
||||
return {
|
||||
pose: [f"/static/characters/animals/{animal}/{nn}/{pose}{i}.png" for i in range(count)]
|
||||
for pose, count in poses.items()
|
||||
}
|
||||
|
||||
|
||||
def _build_library():
|
||||
manifest = _load_manifest()
|
||||
library = {}
|
||||
for animal, pack in manifest.items():
|
||||
family_label = pack["label"]
|
||||
for nn, poses in pack["variants"].items():
|
||||
library[_slug(animal, nn)] = {
|
||||
"label": _label(family_label, nn),
|
||||
"animations": _animations_for(animal, nn, poses),
|
||||
"family": animal,
|
||||
"family_label": family_label,
|
||||
}
|
||||
return library
|
||||
|
||||
|
||||
ADMIN_SPRITE_LIBRARY = _build_library()
|
||||
@@ -0,0 +1,21 @@
|
||||
def sprite_gallery_families(sprite_library):
|
||||
"""Un tile par FAMILLE pour la galerie d'ajout/de changement de
|
||||
personnage — les personnages Kenney (pas de clé "family", voir
|
||||
screens/labels/sprite_library.py) sont chacun leur propre famille ;
|
||||
les animaux CraftPix (screens/labels/animal_sprite_library.py) sont
|
||||
représentés par leur variante "de base" (dict Python : ordre
|
||||
d'insertion garanti, "01" toujours vu en premier — voir
|
||||
_build_library()). Les autres variantes se choisissent depuis le
|
||||
panneau de propriétés (voir render_variant_gallery, templates/
|
||||
screen_edit.html) plutôt que d'encombrer cette galerie de 15 tiles
|
||||
quasi identiques par animal."""
|
||||
seen_families = set()
|
||||
families = []
|
||||
for slug, character in sprite_library.items():
|
||||
family = character.get("family")
|
||||
if family:
|
||||
if family in seen_families:
|
||||
continue
|
||||
seen_families.add(family)
|
||||
families.append((slug, character))
|
||||
return families
|
||||
@@ -48,7 +48,12 @@ def _animations_for(slug):
|
||||
return animations
|
||||
|
||||
|
||||
SPRITE_LIBRARY = {
|
||||
# Bibliothèque publique — visible par tous les comptes (CC0, voir
|
||||
# LICENSE-kenney.txt). Distincte de screens.ADMIN_SPRITE_LIBRARY (sprites
|
||||
# animaux CraftPix, réservés au rôle admin — voir
|
||||
# screens/labels/animal_sprite_library.py et le plan "Bibliothèque de
|
||||
# sprites animaux CraftPix").
|
||||
PUBLIC_SPRITE_LIBRARY = {
|
||||
slug: {"label": label, "animations": _animations_for(slug)}
|
||||
for slug, label in CHARACTERS.items()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import json
|
||||
|
||||
from ..labels.sprite_library import SPRITE_LIBRARY
|
||||
from ..labels.sprite_library import PUBLIC_SPRITE_LIBRARY
|
||||
from ..labels.animal_sprite_library import ADMIN_SPRITE_LIBRARY
|
||||
|
||||
# Résolution RUNTIME (jeu jouable) : le catalogue COMPLET, quel que soit le
|
||||
# rôle du créateur — voir le plan "Bibliothèque de sprites animaux
|
||||
# CraftPix" (aucun filtrage par rôle au runtime, seulement à la galerie de
|
||||
# l'éditeur/l'écriture, voir routes/screens/screen_edit.py et
|
||||
# routes/elements/element_add.py).
|
||||
_SPRITE_LIBRARY = {**PUBLIC_SPRITE_LIBRARY, **ADMIN_SPRITE_LIBRARY}
|
||||
|
||||
_DEFAULT_PERSONNAGE_DATA = {"source": "forge", "forge_character": "male-adventurer"}
|
||||
|
||||
@@ -26,7 +34,7 @@ def resolve_personnage_animations(el):
|
||||
data = resolve_personnage_data(el)
|
||||
if data.get("source") == "custom":
|
||||
return data.get("animations") or {}
|
||||
character = SPRITE_LIBRARY.get(data.get("forge_character"))
|
||||
character = _SPRITE_LIBRARY.get(data.get("forge_character"))
|
||||
return character["animations"] if character else {}
|
||||
|
||||
|
||||
|
||||
@@ -22,8 +22,9 @@ def add_scene_object(slug, scene_id, kind="personnage", forge_character=None):
|
||||
if kind == "personnage":
|
||||
data = dict(_DEFAULT_PERSONNAGE_DATA)
|
||||
if forge_character:
|
||||
from ..labels.sprite_library import SPRITE_LIBRARY
|
||||
if forge_character in SPRITE_LIBRARY:
|
||||
from ..labels.sprite_library import PUBLIC_SPRITE_LIBRARY
|
||||
from ..labels.animal_sprite_library import ADMIN_SPRITE_LIBRARY
|
||||
if forge_character in PUBLIC_SPRITE_LIBRARY or forge_character in ADMIN_SPRITE_LIBRARY:
|
||||
data["forge_character"] = forge_character
|
||||
attributes["_personnage_data"] = json.dumps(data)
|
||||
conn = db.connect(slug)
|
||||
|
||||
Reference in New Issue
Block a user