The current URL is datacrystal.tcrf.net.
Shadow Dancer/FrenchNotes
Objets graphiques
Généralités
Objets et Stances
Un objet graphique est un objet qui s'affiche par-dessus la carte (à l'exception du HUD). Par exemple, Musashi, son chien, les ennemis, mais aussi les projectiles, les colonnes de feu, les capsules 1-up, la flèche de fin de niveau, etc.
Chaque objet possède plusieurs étapes d'animation, ou stances.
Patterns, Sprites et Multisprites
Un pattern est une graphisme carré de 8 pixels de côté (unité de base du graphisme sur MD), indépendant de sa palette. Numérotés de 0 à 0x7FF.
Un sprite est un assemblage rectangulaire de patterns, de 1 à 4 patterns dans les deux dimensions. Il peut avoir quelques attributs :
- sa position
- la palette (de 0 à 3)
- s'il est flippé horizontalement ou verticalement
- le premier pattern qui le compose : celui dans le coin haut-gauche. Les patterns suivants sont ceux dont les numéros suivent, ils remplssent le sprite colonne par colonne (et pas ligne par ligne comme souvent)
Comme un sprite fait au maximum 32*32 pixels, le moteur du jeu implémente des Multisprites, formés comme un ensemble de sprites. Ils partagent la même palette, la position de chaque sprite est donnée par un offset à partir d'une position de référence appelée hotspot.
Objets dynamiques et objets statiques
- les objets dynamiques ont leur patterns chargés en VRAM au moment où l'objet apparaît à l'écran. Seuls les patterns utiles à la stances courantes sont chargés ; dès que la stance change, les patterns de la nouvelle stance remplacent ceux de l'ancienne.
Par exemple, Musashi, son chien, et les ennemis classiques (mais pas les boss) sont des objets dynamiques.
Les objets dynamiques nécessitent 3 structures de données :
- un buffer contenant tous les patterns de toutes les stances (parfois compressé, parfois pas)
- une pattern_loading_table, dont les entrées codent les patterns à charger en VRAM pour une stance donnée
- une multisprite_table, dont les entrées permettent de construire le multisprite associé à une stance.
- les objets statiques ont leur patterns chargés en même temps que les graphismes de la carte, au début de la mission. Tous les patterns de toutes les stances sont chargés en VRAM. Par exemple, les boss, les colonnes de feu.
Par rapport aux objets dynamiques, ceux-ci ne nécessitent que le buffer de patterns et la multisprite_table
Structure des tables
Les pattern_loading_tables et les multisprite_table sont des tables à entrées de tailles variables. Elles commencent toutes par une liste d'offsets (w) du début de la table vers le début de l'entrée.
entry_addr = table_addr + source.read_w(table_addr + 2*entry_id)
Joe Musashi
Données graphiques
C'est un objet dynamique.
Ses patterns sont nemesis-compressés, en ROM 0x23540. Ils sont décompressés en RAM 0xFF9300.
La pattern_loading_table est en 0x1AD36. Chaque entrée est de la forme :
- (w) number_of_groups : nombre de groupes de patterns à copier en VRAM
- Puis number_of_groups séquences de 14 octets :
93XX 94XX 95XX 96XX 97XX CCCCCCCC
Ils sont envoyés au VDP_control_port. Les 5 premiers mots mettent à jour les registres $13 à $17 du VDP, en particulier, ils contiennent l'adresse en RAM à partir de laquelle on copie les patterns, et le nombre de mots à copier. cccccccc est la commande qui enclenche la copie DMA. En particulier, on peut en extraire l'adresse en VRAM où ils sont copiés. Dans le cas de Musashi, les patterns sont chargés à partir de la position 0x7A0.
La multisprite_table est en 0x69370. Le format d'une entrée est le même que pour toutes les multisprite_tables :
- un octet de la forme :
mddnnnnn
où :
- m est le mode (long si 1, court si 0)
- d a un rapport (à étudier) avec des informations complémentaires sur le multisprite (en particulier, collisions). Ces informations sont stockées immédiatement après l'octet courant, et avant la description des sprites du multisprite.
- si d = 0, il faut sauter les 6 prochains octets avant d'atteindre la première description de sprite
- si d = 1, il faut sauter les 12 prochains octets
- si d = 2, il faut sauter les 16 prochains octets
- si d = 3, il ne faut rien sauter
- n est le nombre de sprites formant le multisprite
- ensuite, on trouve n séquences de 6 (mode court) ou 8 (mode long) octets :
- X : octet (mode court) ou mot (mode long) signé
- Y : octet (mode court) ou mot (mode long) signé : ce sont les offsets depuis le hotspot jusqu'au coin haut-gauche du sprite
- P : mot dont la forme est :
????hhvv ????????
où hh et vv sont les dimensions du sprite en nombre de patterns - 1 ;
- T : mot dont la forme est :
????fttt tttttttt
où f vaut 1 si le sprite doit être flippé horizontalement, et ttt tttttttt le numéro du premier pattern (>= 0x7A0 pour Musashi).
Voici un extrait de code Python pour rendre tout un peu plus clair :
Attributs
Les attributs concernant Joe Musashi sont en RAM 0xFF1358
Le chien
Données graphiques
C'est un objet dynamique. Le principe est similaire à Musashi.
Les patterns sont en 0x21792, ils ne sont pas compressés.
La pattern_loading_table est en 0x1BAD4, sa structure est identique à celle de Musashi. Les patterns sont chargés en VRAM à partir du pattern 0x7DA.
La multisprite_table est en 0x69B36.
Attributs
Les attributs sont en RAM 0xFF13F0.
Shurikens
Autres objets
Les autres objets graphiques sont caractérisés par un object_id (de 0x00 à 0x79, voir ici). Des objets différents peuvent partager les mêmes graphismes, mais des AI différentes (par exemple les ninjas, shieldmen, gunmen).
Les ennemis basiques sont dynamiques. Les autres objets sont statiques.
Ennemis basiques
Ce sont des objets dynamiques. En plus de leur object_id, ils possèdent un patterns_set (de 0x00 à 0x0E), qui permet de déterminer la pattern_loading_table.
- 00 : ?
- 01 : punching soldier
- 02 : punching soldier
- 03 : soldier with knife
- 04 : soldierwith staff
- 05 : shieldman
- 06 : rolling ninja with claw
- 07 : gunman
- 08 : breaking window (from where rolling ninjas appear)
- 09 : falling stone
- 0A : shield
- 0B : dying ennemy explosion
- 0C : ninja
- 0D : falling stone
- 0E : lizardman
Les patterns sont non-compressés dans la ROM. On récupère l'adresse par l'initialisation des registres VDP $15, $16 et $17.
Les adresses des pattern_loading_tables sont codées à partir de 0x8774. Le format d'une entrée est :
93XX 94XX 95XX 96XX 97XX TTTT
où TTTT est le numéro de la tile à charger, en partant de 0.
Les multisprite_tables sont hardcodées (voir [Tables des Objets].
Autres objets
Objets statiques, dont les patterns sont chargés en général au début du stage (ce qui les rend difficile à tracer).
Les multisprite_tables sont hardcodées (voir [Table des Objets]).
Collisions
À creuser. Candidats dans la multisprite_table.
Cartes
Ennemis
On trouve deux tables d'ennemis par niveau :
Table A
Chargée en RAM 0xFF3080. Terminée par un 0xFFFF, chaque entrée fait 8 octets :
- 0 (b) : flags ?
- 1 (b) : compteur
- 2 (b) : object_id
- 3 (b) : v ?
- 4 (w) : x
- 6 (w) : y
Elle est construite à partir d'une table en ROM 0x7F0C2. Terminée par 0xFFFF, chaque entrée sur 4 octets de la forme :
iiii iiiu uvvv xxxx xxxx xyyy yyyy y000
où :
- iiiiiii : object_id
- uu : vraisemblablement un marqueur de difficulté
- vv : 1er nibble de v ?
- xxxx xxxx x000 : x
- yyy yyyy y000 : y
Table B
Chargée en RAM 0xFF3280. Terminée par 0xFFFF, chaque entrée fait 8 octets :
- 0 (b) : flags (bit1 = ennemi tué) ?
- 1 (b) : compteur
- 2 (b) : object_id
- 3 (b) : u ?
- 4 (w) : x
- 6 (w) : y
Construite par 11D2E, à partir d'une table en RAM 7F0D6. Terminée par FFFF, chaque entrée fait 6 octets :
??uu u??? ???? ???? iiii iiii ??xx xxxx xxxx yyyy yyyy yy??
où :
- uuu : u ?
- iiiiiiii : object_id
- x xxxx xxxx x000 : x
- y yyyy yyyy y000 : y