![]() |
![]() |
#1 |
Administrateur
|
![]() Intégrer le langage d'extension interprète LUA dans son application GBA Auteur : moechofe 1. c'est quoi ? C’est un langage de scripts que l'on intègre dans ses sources C. Ou encore, cela permet d'interprétés des scripts à partir du programme hôte. 2. pourquoi faire ? Imaginé un jeu d'avenue ou un RPG, dans lequel le héros doit interagirent avec des PNJ. Ces PNJ doivent être capable de réagir différemment en fonction des actions du héros tout au long de la partie, ces PNJ doivent aussi avoir une vie a eux, bref il leur faut une intelligence artificielle. ne partez pas tout de suite. l'intelligence artificielle n'est pas difficile en elle-même, ce qui est dur c'est de la rendre parfaite. Alice ou le Prolog sont des exemples de simplicité qui permettent d'effectuer des choses remarquables. On trouve LUA dans des produits commerciaux comme Homeworld 2, World of Warcraft ou encore Far Cry. 3. comment ça marche ? Il faut intégrer la librairie ou simplement compiler les sources avec votre application, déclarer toute une série de fonctions utilisables à partir des scripts LUA (scroller la map selon des trajectoires, déplacer les protagonistes, faire fondre les couleures de la palette, écrire du texte dans une console), intégrer les scripts LUA dans votre application (en chargent des fichiers, en lisant directement dans la rom), et exécuter ces derniers. 4. quoi d'autre ? LUA est un langage procédural offrant une syntaxe simple, proche de celle du pascal. il utilise un garbage collector qui s'occupe de s'occuper de votre mémoire et de vos variables. LUA est le plus rapide des langages d'extension interprète grâce au fait qu'il pre-compile le script en un bytecode indépendant de la plateforme (comme java). LUA est compatible ansi c. il existe d'autres distributions qui rajoutent des fonctionnalités, comme CaLUA et ToLua, a vous de voir. 5. comment on fait, alors ? 5.1. Récupérer les sources de LUA sur le site http://www.lua.org/. Pour ma pars c'était lua-5.0.2.tar.gz. Il existe des versions déjà compilées qui offre la possibilité de tester vos scripts dans une console, et aussi de pré-compile les scripts avant de les intégrer dans votre application (c'est très pratique, on gagne un temps fou, mais je n'ai pas encore teste) pour l'instant j'en suis resté aux sources directement intégrées dans mon projet (c'est parce que j'ai pas réussi a compiler la librairie en faite) 5.2. commencer un nouveau projet. (Et puis pour faire plus simple, je vais parler a la première personne). donc je créer un dossier lua_in_my_game/ pour démarrer un nouveau projet, et puis comme je suis sympa, je vais le faire avec HAM. j'estime que ceux qui compilent avec DEVKITADV n'ont pas de souci avec leur makefile. 5.3. Je décompresse l'archive dans le dossier, j'obtiens un lua-5.0.2/, dedans il y a un dossier src/, je copie tous les fichiers *.h et *.c qui s'y trouvent dans un dossier a la racine de mon projet que j'ai nomme lua/, je retourne dans lua-5.0.2/ il y a un dossier include/ dans lequel je prends les fichiers lua.h, lauxlib.h et lualib.h, (d'après ce que j'ai pu lire, lauxlib.h est très bien si vous souhaiter créer votre propre librairie base sur LUA, car c'est aussi quelques choses de très intéressantes, question performance), je les copie dans le dossier lua/ aussi. 5.4. Je vais compiler tout ça. Pour ça, je ne vais pas m'embêter a intégrer tous les fichiers un par un dans la variable OFILES de mon makefile, je vais le faire automatiquement : Code:
# # Set a list of files you want to compile # OFILES += main.o LUASRC = $(wildcard lua/*.c) OFILES += $(LUASRC:.c=.o) Code:
lib:lapi.c lcode.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltests.c ltm.c lundump.c lvm.c lzio.c Code:
lauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c ltablib.c lstrlib.c loadlib.c interpreter:core lib, standard lib, lua.c compiler:core lib, standard lib, luac.c print.c and also lopcodes.c (with LUA_OPNAMES defined) from core. 5.5. On va affiche une connerie a l'écran : Code:
int main(void) { ham_Init(); ham_InitText(0); ham_DrawText( 0, 0, "%s", "un connerie" ); while(TRUE); return 0; } 5.6. On va voir si LUA fonctionne un peu, je rajoute l'inclusion suivante après <mygba.h> : Code:
#include "lua/lua.h" Je rajoute avant le while infini : CODE // créer un nouvel état lua lua_State *L = lua_open(); // ferme l'etat void lua_close( lua_State *L ); 5.7. Je créer une fonction qui sera utilisable par LUA : Code:
static void script_texte( lua_State *L ) { // récupère le nombre de paramètres dans la pile unsigned short n = lua_gettop(L); // scanne tous les paramètres unsigned short i; for( i=1; i<=n; i++ ) { // si le paramètre est une chaîne de caractères if( lua_isstring(L,i) ) ham_DrawText( 0, 1, "%s", lua_tostring(L,i) ); // si le paramètre est nul else if( lua_isnil(L,i) ) ham_DrawText( 0, 1, "nil" ); // si le paramètre est un booléen else if( lua_isboolean(L,i) ) ham_DrawText( 0, 1, lua_toboolean(L,i) ? "true" : "false" ); } } 5.8. J'informe LUA que j'ai créé une fonction pour lui, j'ajoute pendant que mon état existe : Code:
// enregistre une fonction à destination de LUA lua_register(L,"texte",script_texte); Code:
static int script_texte( lua_State *L ) 5.9. Interlude. Pour tester la fonction. Il y a deux manières de procéder, soit en utilisant un script LUA, soit directement via le code C. cette dernière méthode est une opération plutôt délicate, mais obligatoire pour bien comprendre le fonctionnement. LUA utilise un systeme de pile pour transvaser les variables, les donnes, mais aussi : tout. exemple : lua_register n'est pas une fonction, mais une macro, et voila a quoi elle ressemble : Code:
#define lua_register(L,n,f) \ (lua_pushstring(L, n), \ lua_pushcfunction(L, f), \ lua_settable(L, LUA_GLOBALSINDEX)) // lua_State *L; // const char *n; // lua_CFunction f; .html. Elle va dépiler les deux paramètres que l'on vient d'empiler, et en lui passant le paramètre LUA_GLOBALSINDEX, elle va créer une fonction dans le contexte global. Schématiquement : le contexte global est un gros tableau a clefs associatives. lua_settable ajoute dans ce tableau une clef qui est le nom de la fonction et, la valeur associe a cette clef qui est l'adresse de la fonction dans la mémoire. Le truc c'est que le contexte global se trouve lui aussi dans la pile. Cette fonction mérite beaucoup d'attention, mais pas tout de suite. Tout est histoire d'empilage et de dépilage. La pile est de type FIFO. CQP (comprendront qui pourront) (first in, first out) (premier entre, premier sortie) Je peux donc maintenant vous expliquer ce que fait la fonction script_texte que j'ai créer. lua_gettop récupère le nombre de paramètres ajouter dans la pile. (On ne sait pas encore comment de paramètre a été ajoute dans la pile : en faite c'est la fonction lua_call qui la fait, mais ça on ne la pas encore vu.) lua_isstring, lua_isnil, lua_isboolean (y'en a d'autre) vérifie que le paramètre i dans la pile est bien du type voulu. lua_tostring, lua_toboolean (y' en a d'autre) retourne le paramètre i dans le type voulu. (J’ai pas encore testé, mais d'âpres ce que j'ai lu, LUA est très peu typé, on peut donc facilement convertir n'importe quoi en n'importe quoi.) 5.10. Interlude. la liste des types LUA ! Code:
LUA_TNONE, LUA_TNIL, LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, LUA_TLIGHTUSERDATA Code:
int lua_type (lua_State *L, int index); int lua_isnil (lua_State *L, int index); int lua_isboolean (lua_State *L, int index); int lua_isnumber (lua_State *L, int index); int lua_isstring (lua_State *L, int index); int lua_istable (lua_State *L, int index); int lua_isfunction (lua_State *L, int index); int lua_iscfunction (lua_State *L, int index); int lua_isuserdata (lua_State *L, int index); int lua_islightuserdata (lua_State *L, int index); Code:
int lua_toboolean (lua_State *L, int index); lua_Number lua_tonumber (lua_State *L, int index); const char *lua_tostring (lua_State *L, int index); size_t lua_strlen (lua_State *L, int index); lua_CFunction lua_tocfunction (lua_State *L, int index); void *lua_touserdata (lua_State *L, int index); lua_State *lua_tothread (lua_State *L, int index); void *lua_topointer (lua_State *L, int index); Code:
void lua_pushboolean (lua_State *L, int b); void lua_pushnumber (lua_State *L, lua_Number n); void lua_pushlstring (lua_State *L, const char *s, size_t len); void lua_pushstring (lua_State *L, const char *s); void lua_pushnil (lua_State *L); void lua_pushcfunction (lua_State *L, lua_CFunction f); void lua_pushlightuserdata (lua_State *L, void *p); Code:
@param lua_State *L : est un État LUA @param int index : est l'index du paramètre dans la pile Code:
// tente d'appeler directement une fonction LUA lua_pushstring(L,"returnvalue"); lua_pushstring(L,"texte"); lua_gettable(L, LUA_GLOBALSINDEX); lua_pushstring(L,"hello world"); lua_call(L,1,1); lua_settable(L, LUA_GLOBALSINDEX); Code:
returnvalue = texte("hello world") lua_pushstring ajoute un paramètre de type chaîne dans la pile. lua_gettable qui fait l'inverse de lua_settable. (Et vous n'avez normalement toujours pas compris. Ça va venir.) lua_call appelle une fonction avec un nombre de paramètre en envoye, et un nom de nombre paramètre en retour (eh oui, voici un des rares langages qui autorisent les fonctions a retourner plusieurs valeurs.) Et on recommence les explications : lua_pushstring(L,"returnvalue"); place dans la pile le nom de la variable LUA (pas une variable utilisable dans le code C. du moins pas encore). Cette variable recevra le résultat de l'appel a ma fonction script_texte (qui est 0 (zéro)). lua_pushstring(L,"texte"); place dans la pile le nom de la fonction. lua_gettable(L, LUA_GLOBALSINDEX); dépile le paramètre fraîchement envoyé texte et récupère dans le contexte global (le gros tableau associatif) la valeur pour laquelle la clef est texte. cette valeur c'est l'adresse-mémoire de la fonction LUA texte, qui n'est autre que l'adresse-mémoire de la fonction C script_texte. Et c'est pas tout, la fonction lua_gettable finis par ajouter dans la pile cette adresse. lua_pushstring(L,"hello world"); empile le premier et l'unique paramètre (si vous avez suivi, notre fonction script_texte peut prendre un infinité de paramètres). lua_call(L,1,1); indique qu'il y a un paramètre a envoyer et un parametre a attendre de la fonction dont le nom se trouve après tout ça (pfuuuu). Un petit schéma : Code:
o---------------------------------o---------o---------------o |XXXXXje_suis_la_pileXXXXXXXXXXXXX| "texte" | "hello world" | o---------------------------------o---------o---------------o | | | | | ^---------------> Le seul et unique paramètre (le pauvre). | | Vous pouvez en ajouter pour tester. | ^-------------------------> Le nom de la fonction ou plutôt, | l'adresse-mémoire de la fonction. ^------------------------------------------------------------> Ça, c'est la pile 6. et our aller plus loin ? Une documentation en français http://lyrian.obnix.com/lua/ qui vous permettera de comprendre le fonctionnement générale, et de découvrir les possiblités. C'est du LUA en version 3, donc ne vous fiez pas au code donnée. La doc interne de LUA en anglais qui se trouve dans lua-5.0.2/doc/manual.html. Elle vous expliquera toutes les fonctions de l'API ainsi que la syntaxe de LUA. Un exemple simple pour implementer, une fonction LUA et une méthode de récuperation des scripts LUA dans un source C se trouve dans le fichier lua-5.0.2/etc/min.c Et puis ya google aussi. Ajouter des scripts LUA externe dans son projet via GBFS. 6. Pourquoi faire ? Parceque sinon xFlash, il aurait fait un tutoriel pour rien. 7. Par ou commencer ? A lire le tutoriel de xFlash sur playeradvance et tenter de tout comprendre. Et bien sur, de reprendre les sources obtenues après le premier tutoriel sur LUA 7.1. Je récupère les fichiers gbfs.h et libgbfs.c et je les ajoute dans un dossier gbfs/ a la racine du projet. Puis j'ajoute dans le fichier main.c dans la déclaration des fichiers à inclures : Code:
#include "gbfs/gbfs.h" Code:
OFILES += gbfs/libgbfs.o Code:
# # Set a list of files you want to compile # OFILES += main.o LUASRC = $(wildcard lua/*.c) OFILES += $(LUASRC:.c=.o) OFILES += gbfs/libgbfs.o 7.2. Interlude. Je rapelle qu'il existe deux façons d'éxecuter une fonction LUA : soit en utilisant l'API de LUA directement en code C, ou bien utiliser des fichiers scripts LUA externe, que l'on insère dans la ROM, et que l'on charge par la suite grace à l'API de LUA. Cette première solution est expliquer dans le premier tutoriel, elle est compliquée à mettre en oeuvre mais importante pour comprendre le système de pile qui est l'une des choses les plus présentes au fond de tout système bas niveau. La deuxième méthode est autrement plus efficasse et bien plus simple à mettre en oeuvre. Elle permet par exemple, d'éxecuter plusieurs instruction à la suite (di donc), chose que la première méthode ne fait pas. Pour lire des scripts LUA à partir de votre code C, il y a là aussi deux manières : lire les scripts directement dans la ROM grace à leur adresse, ou utiliser le système de fichier que Damian Yerrick nous met a disposition. (et même que xFlash il a écrit un tutoriel pour ça). Je n'expliquerai que cette dernière méthode, qui reviens en faite au même que la première, a savoir : pointer sur l'adresse du fichier, et dire à LUA qu'il faut lire x données. 7.3. Interlude. L'implementation du systeme de fichier GBFS est relativement simple, il faut utiliser le programme gbfs.exe pour empiler les scripts ou les fichiers que l'on veux. Il faut ensuite effectuer une operation sur la ROM compiler pour que sa taille atteigne un multiple de 256 octes. Dernier étape, il faut joindre la ROM avec les fichiers empilés (retourné par gbfs.exe). Pour ça, on utilise la commande DOS copy (je n'est pas trouvé l'équivalent sur linux), qui permet de copier plusieurs fichiers dans un seul. 7.4. Mais je vais d'abord commencer par créer un script LUA qui va se charger d'honorer ma belle fonction C script_texte. (Je rappele au passage que la fonction C script_texte s'appele texte en LUA), (c'est fait exprès pour vous embrouiller), (non, c'est fait exprès pour ne pas confondre). Je créer un beau dossier script, dans le lequel je créer un fichier test.lua, dans lequel j'ajoute ceci : Code:
texte("Bonjour le monde") Code:
GBFSFILES = $(wildcard script/*.lua) Code:
all : $(PROGNAME).$(EXT) $(PROGNAME).gbfs clean Code:
# empile les scripts LUA $(PROGNAME).gbfs: tools/gbfs.exe $(PROGNAME).gbfs $(GBFSFILES) Une fois le fichier de donnée obtenu, je le join au binaire compilé, pour cela, je remodifie la cible all comme ceci : Code:
########################################################################################## # Standard Makefile targets start here ########################################################################################## all : $(PROGNAME).$(EXT) $(PROGNAME).gbfs clean tools/padbin 256 $(PROGNAME).$(EXT) copy /y $(PROGNAME).$(EXT) /b + $(PROGNAME).gbfs /b $(PROGNAME).$(EXT) 7.6. Maintenant je vais ecrire le bout de code qui me permettra de reperer mon script grace â GBFS. Comme j'ai decidé que je n'allais utiliser qu'un seul fichier à la fois, je déclare les quelques variables suivantes en globale : Code:
// le nombre de fichiers present dans la ROM unsigned long nombrefichiers; // le nom du dernier fichier pointé char nomfichier[24]; // la taille du dernier fichier pointé unsigned long taillefichier; // pointeur sur le systeme de fichier const GBFS_FILE * systemefichier = NULL; // pointeur sur le debut du fichier courant const char * fichiercourant; Code:
// pointe sur la TOC systemefichier = find_first_gbfs_file(find_first_gbfs_file); // pour le fun, je ne m'en sert pas dans ce tutoriel nombrefichiers = gbfs_count_objs(systemefichier); TOC = Table Of Content, Table des matières en français. Code:
// pointeur sur le fichier choisi fichiercourant = gbfs_get_obj( systemefichier, "test.lua", &taillefichier ); Code:
// j'affiche brut, le contenu ham_DrawText( 0, 2, "%s", fichiercourant ); 7.7. Interlude. Les possiblitées offerte par LUA pour récupérer du code de l'exterieur on beaucoup évolué depuis les premières versions. Toujours de plus en plus simple. La dernière méthode permet en une fonction de charger soit des scripts LUA, soit des scripts pre-compile LUA. Pour ma part je n'ai pas encore réussi a faire fonctionner des scripts pré-compilé. 7.8. Interlude. La fonction lu_load() permet de charger un script LUA en utilisant une fonction de callback (j'ai pas de traduction). en gros, on fourni à lua_load() un pointeur sur quelquechose qui peut être un fichier ou un buffer d'entrée comme stdin, et a chaque fois que lua_load() en a besoin, elle fera appel à une fonction de callback qu'il faudra écrire. Cette fonction doit renvoyer une portion du script que l'on veux charger ainsi que la taille de cette portion. Si vous desirer connaître la raison de ce fonctionnement étrange, j'ai deux bonnes raisons : plus de souplesse quand à la possiblité de recupérer des donnees depuis n'importe quelle type de source, et, des traitements possible à la volé de ces sources comme par exemple de la décompression. Pour cette derniere raison, les possiblites ne sont pas énorme puisque les accès aux fonctions LUA ne sont pas autorisé, du moins pas avec la version 5.0.2. Ce référer à la documentation pour plus d'informations lua_5.0.2/doc.manual.htm : 3.10 - Loading Lua Chunks. 7.9. Interlude. Cette fonction : callback est donc appelé plusieurs fois, minimum deux. Pour le dernier appel, la fonction de callback devra renvoyer NULL, cela indique à lua_load() qu'il faut arreter d'appeler la fonction callback. Pour mes tests, je n'ai pas eu besoin d'envoyer les scripts en plusieurs morceaux, donc deux appels on suffit. Un cas ou il serait nescessaire d'effectuer plusieurs passes serait dans le cas d'une utilisation d'un buffer. Dans mon cas, la ROM ne risque pas de ce barrer, je peux donc accélérer les choses. 7.10. Voici donc deux nouvelles fonctions a étudier : Code:
/* recupere_fichier {{{ * * fonction callback pour lua_load() * * @param lua_State *L un état LUA (toujours a null) * @param void *data un pointeur sur les donnees * @param size_t *size la taille des donnees (a retourner) */ static const char * recupere_fichier( lua_State *L, void *data, size_t *size ) { // permet de vérifier si le fichier a déjà été envoyés static const char * fichierprecedent = NULL; // si le script a déjà été envoyé if( fichierprecedent == fichiercourant ) { // renvoie null *size = 0; return NULL; } // sinon else { // renvoie un pointeur sur le script *size = taillefichier; // envoie le fichier en une seule partie fichierprecedent = fichiercourant; // j'aurai pu mettre ici : return data; return fichiercourant; } } // recupere_fichier */ /* charge_script {{{ * * charge un script LUA et l'éxecute * * @param lua_State *L un état LUA * @param char * nomfichier le nom du fichier a charger */ static const char * charge_script( lua_State *L, char * nomfichier ) { // pointe sur le fichier fichiercourant = gbfs_get_obj( systemefichier, nomfichier, &taillefichier ); // appel la fonction de chargement des script LUA if( lua_load( L, recupere_fichier, (void*) fichiercourant, nomfichier ) // et dans la foulé, éxecute le script || lua_pcall( L, 0, 0, 0 ) ) // gestion des erreurs ham_DrawText( 0, 4, "%s",lua_tostring(L,-1)); // ca, c'est pour la suite return fichiercourant; } // charge_script lua_pcall fait la même chose que lua_call a savoir : appeler une fonction, mais elle permet en plus de récupérer les erreurs. L'appel a la fonction lua_load() nescessite l'adresse de la fonction de callback que j'ai appelé recupere_fichier. Cette dernière recoie trois paramêtres : un état LUA toujours a NULL (du moins pour la version 5), un pointeur sur mon script LUA, et une variable qu'il faudra renseigner. C'est un exemple grandement simplifié. Pour plus d'information vous pouvez ouvrir le fichier lua_5.0.2/etc/min.c qui est exemple d'utilisation de lua_load, et aussi la source de ce tutoriel. La fonction lua_tostring() dont je vous ai déjà parler prend pour la première fois un paramêtre numérique négatif. La documentation en anglais explique très bien tout ceci lua_5.0.2/doc/manual.htm[/i] : [u]3.2 - The Stack and Indices. Lors de l'appel d'une fonction, les paramêtre sont envoyer a cette fonction dans un ordre. Pour récuperer un paramêter dans le même ordre, il faut utiliser un index positif. Pour récuperer un paramtre depuis le haut de la pile, il faut utiliser un index négatif. Code:
o-------------------------o---o----o----o----o---o |XXXje_suis_la_pileXXXXXXX| | p1 | p2 | p3 | 3 | o-------------------------o---o----o----o----o---o | | | | | | | | | | | ^-> le nombre de paramêtres | | | | | | | | | ^------> le dernier paramêtre | | | | accessible depuis : | | | | l'index 3 ou | | | | l'index -2 | | | | | | | ^-----------> index 2 ou -3 | | ^----------------> index 1 ou -4 | | | ^--------------------> l'appel à la fonction | ^-----------------------------------------------> c'est la pile 7.11. Interlude. Petit précision pour ceux qui ont repéré la confusion. lua_call() permet d'appeler une fonction, or dans mon exemple, je n'appel pas une fonction mais directement un script. En fait on appele quelquechose qui se trouve à une adresse, quelque soit sont type, fonction ou pas fonction. Cela touche à une des souplesses agréable qu'ajoute les languages de scripts par rapport aux languages compiler. L'un des languages de scripts que je trouve le plus impressionant dans le domaine c'est le REBOL : il n'y a pas de notion de variable ou de fonction, tout est : mot (word). Et un mot peut aussi bien contenir des données numérique ou alphabetique que du code. Résultat, on peut donc avec des tableaux imbriquer, avoir des entiers dans du code dans un tableau dans du code dans une chaine de caractere. Le lisp est aussi un très bon exemple puisque 90% du LISP est écrit en LISP. LUA est plus modeste mais aussi bien plus rapide. Il peut quand même simuler tout comme REBOL, la plupart des comportements de la POO : programmation orientée objets. Et ça, c'est vachement bien. 7.12. Pour la suite je vais enlever de mon code mes précédents exemples et en ajouter un nouveau, avec un appel à la fonction fraichement écrite charge_script() : Code:
int main(void) { ham_Init(); ham_InitText(0); // creer un nouveau etat lua lua_State *L = lua_open(); // enregistre une fonction a destination de lua lua_register(L,"texte",script_texte); // pointe sur la TOC systemefichier = find_first_gbfs_file(find_first_gbfs_file); // pour le fun, je ne m'en sert pas dans ce tutoriel nombrefichiers = gbfs_count_objs(systemefichier); // charge et execute un script LUA charge_script( L, "test.lua" ); // ferme l'etat void lua_close( lua_State *L ); while(TRUE); return 0; } 8. Et maintenant ? Tout le nescessaire minimum est présent, à pars peut-être la possiblité d'intégrer des scripts pré-compilé, et puis aussi de connaitre le language même LUA. (Les habitués des languages de script seront avantagé). les sources du tutoriel lua_in_my_game.2.zip Pour ce qui est d'insérer des script pré-compilé, vous avez le droit de m'aider. Pour ce qui est d'apprendre LUA la documentation officiel n'est pas ce qu'il y a de mieux, mais il reste les examples. Bon code, ou bon deboggage. |
![]() |
![]() |
Publicité |
![]() |
#2 |
Membre confirmé
Date d'inscription: 10/11/2005
Messages: 1 037
|
![]() Et bien, c'est long ! Beau boulot.
|
![]() |
![]() |
![]() |
#3 |
Administrateur
|
![]() bah c'est moechofe qu'il faut remercier, s'il nous lit encore
__________________
Projets Abandonnés: [Arcomage Advance] [Puzznic] [PA Card Games] [Blob Runner] Projet en cours: [Ne plus abandonner de projet...] |
![]() |
![]() |
![]() |
#4 |
Bitchy Little Girl...
Date d'inscription: 29/10/2005
Messages: 3 201
|
![]() Encore !
__________________
>> On garde :: On améliore :: On kill << |
![]() |
![]() |
![]() |
#5 | |
Administrateur
|
![]() Citation:
__________________
Projets Abandonnés: [Arcomage Advance] [Puzznic] [PA Card Games] [Blob Runner] Projet en cours: [Ne plus abandonner de projet...] |
|
![]() |
![]() |
![]() |
#6 | |
Bitchy Little Girl...
Date d'inscription: 29/10/2005
Messages: 3 201
|
![]() Citation:
__________________
>> On garde :: On améliore :: On kill << |
|
![]() |
![]() |
![]() |
#7 |
Messages: n/a
|
![]() Je testerais ce soir si ca fonctionne pour la PSP, mais il n'y a pas de raison que non vu que lua est codé avec du C très standard.
en tout cas bravo! |
![]() |
![]() |
#8 |
Modérateur
Date d'inscription: 10/11/2005
Localisation: Mad Monster Mansion
Messages: 3 755
|
![]() Bien sympa Dr Vince!
Faut que je me penche là-dessus!
__________________
News GBA/DS: Portable DEV Colorer un jeu GB? Débutant - Avancé - Expert Projets : Banjo Advance - The Last Quest - Klungo's Brain School Site des jeux : BanjoKazooie.Free.Fr Web Site ![]() "La Vie n'est pas aussi simple qu'un Jeu Vidéo..." |
![]() |
![]() |
![]() |
Liens sociaux |
Publicité |
Utilisateurs regardant la discussion actuelle : 1 (0 membre(s) et 1 invité(s)) | |
Outils de la discussion | |
Modes d'affichage | |
|
|