Asci : un moteur pour jeux de rôles en Python
Posté le 13/08/2021 11:36
Bonjour à tous !
Avec la Graph 90 et son Python très… limité pas simple de faire un jeu de rôle… Asci est un moteur conçu pour vous simplifier la vie !
En terme d'utilisation, Asci est assez ergonomique avec notamment des choses plutôt agréable comme la détection des entités par leur nom, ou la gestion de Tiled qui permet de créer des cartes très simplement. Mais si vous voulez exploiter le moteur au maximum, il y a quelques points un peu plus délicat…
Concrètement, à quoi sert ce moteur ? Asci a avant tout pour but de rendre plus simple la création de jeux de rôles en Python. Ainsi, le moteur va se charger en grande partie de l'affichage, de l'avancée du scénario et de toute l'interface utilisateur. Vous de votre côté, il faudra que vous fournissiez une carte et un scénario sous forme d'évènements.
>> Documentation complète <<
>> Télécharger Asci <<
>> Le dépôt gitea <<
La carte du monde
- c'est à vous de la faire dans une variable à part, mais il faut respecter quelques règles
- vous évoluez dans le monde façon pokémon : la carte bouge tout autour de vous
Les mécaniques
- les combats sont laissés au développeur du jeu
- une liste des données du moteur est modifiable, sauf si vous savez ce que vous faites, n'y touchez pas, ce n'est, en général, pas nécessaire.
- une liste de statistique est laissée libre, vous pouvez l'utiliser comme vous voulez. Ces statistiques peuvent être modifiée par les dialogues, bien que ce ne soit pas toujours la solution la plus élégante
- les touches 1, 2, 3 et 5 sont prises pour les déplacement, vous ne pouvez pas y associer de fonctions.
La mise en place du scénario
- les évèvements se découpent en catégories, à chaque catégorie correspond une fonction dans le code et un (ou des) symbole(s) qui déclenche(nt) ces évènements lorsque le joueur le(s) rencontre
- les évènements ne sont pas très dur à prendre en main, il s'agit de liste : [XP_gagnée, "texte"].
- vous pouvez tout à fait mettre en place des dialogues complexes dans lesquels le joueur choisit sa réplique dans une liste
Les entités
- il s'agit d'une liste de tuples stockée avec la carte ; chaque carte a sa liste d'entités
- toutes les entités sont repérées par un identifiant (à vous de faire en sorte qu'il soit unique)
- les entités peuvent être animées et vous pouvez modifier les entités comme vous le souhaitez
Citer : Posté le 13/08/2021 11:59 | #
Superbe ! Une bonne bibliothèque Python sera un début vraiment excellent, comme pour celle de Farhi. N'oublie pas d'inclure des exemples de code pour permettre aux intéressés de copier/coller un peu et de se lancer rapidement !
Citer : Posté le 13/08/2021 12:47 | #
Pour les exemples, Asci (le jeu de rôle) en constituera un beau, mais le moteur ne sera pas tout à fait compatible avec ^^'
Je me note dans un coin d'en faire avec sans doute en petit tuto ou un truc du genre
Ajouté le 13/08/2021 à 15:16 :
Bon, j'ai décidé de me lancer à corps perdu dans la bataille avec le moteur, sans m'occuper des autres opus (oui le choix est discutable mais j'avais envie de faire un truc généraliste xD)
Quelques points sont déjà bien plus clair et d'autres se sont obscurcis xD
La map
Les mécaniques
Que dire d'autre ? La sauvegarde sera très probablement sous forme d'une liste. Je sais pas trop ce que je pourrais ajouter, ça peut faire peur peut-être vu de l'extérieur, je pense déjà pouvoir vous dire que le plus dur, ça va être les évènements et la gestion de l'XP. La map c'est du ASCII-Art pur et simple. Une fois que votre progression de l'XP est bien nette, il faut juste écrire un petit : game = Asci() ; game.mainloop() et c'est terminé.
Citer : Posté le 13/08/2021 15:26 | #
Question : pourquoi est-ce qu'on ne pourrait pas avoir une fonction asci_init_map() à laquelle on donne la chaîne qu'on veut ? Si tu donnes au moteur le contrôle des fichiers les choses se compliquent un peu dans le sens où on est plus vite limité par le moteur (eg. on est limités à une seule world_map) que si les choses se passent via une API.
Est-ce que tu ne peux pas avoir un dictionnaire pour les stats et laisser le jeu qui utilise ton moteur décider de leurs noms et rôles ?
En gros, si tu veux, peut-être que ton moteur peut être un peu plus petit et laisser de la marge au jeu ! Ce serait dommage si on ne peut coder que les mécaniques de Asci Opus 1 et 2 non ?
Citer : Posté le 13/08/2021 15:35 | #
Pour la map, ça doit pouvoir se faire assez facilement effectivement
Pour les stats, c'est un peu plus délicat, elles sont stockés à côté des coordonnées, j'ai essayé de faire en sorte que ce soit simplement modifiable… mais reste un problème, les combats sont normalement géré par le moteur… après je peux ne pas les gérer ? 'fin si je ne connais a priori pas les stats, je ne peux plus gérer les combats ^^'
Citer : Posté le 13/08/2021 15:53 | #
Pour les combats, tu peux implémenter la GUI mais laisser le jeu spécifier ce que les actions font. Par exemple dans un Pokémon tu as 4 attaques toutes uniques (pas juste attaquer/défendre/etc). Si ça peut t'aider donne-toi quelques idées de jeux (Asci-Opus 1 et 2, Pokémon, Final Fantasy) et essaie de construire chaque partie d'une façon qui permette de coder les 3 à la fois.
Il est absolument certain que plus tu laisses de liberté au jeu, moins tu peux faire de choses toi-même (et donc plus tu laisses de travail au jeu). Toute la subtilité c'est d'identifier quelles sont les parties communes à tous les jeux, ou que les auteurs des jeux n'ont pas envie de coder eux-mêmes, et de mettre ça dans le moteur. À toi de voir où tu veux te placer entre «le moteur code très peu : liberté maximale mais beaucoup de travail» et «le moteur code tout : trivial à utiliser mais très limité» !
Citer : Posté le 13/08/2021 16:01 | #
Je vois les limitations
Du coup je vais partir sur une sorte de compromis… d'un côté j'ai l'XP, les PV les coordonnées (dont j'ai besoin) et de l'autre j'ai une variable "stat" laissée vierge
De même pour les combats, la fonction sera vide… peut-être passer en argument la fonction de combat ?
J'ai pas mal avancé !
Un premier tuto est sur le README :
Utilisation
Vous devez commencer par copier le fichier asci_lib.py dans le répertoire de votre jeu. Créez ensuite un fichier qui va correspondre à votre jeu. Nommez-le comme vous voulez, le nom ne présente pas d'importance pour le moteur.
Ici, notre fichier s'appellera sample.py.
Notre fichier va se découper en plusieurs parties :
- l'importation de la bibliothèque asci_lib.py
- la création de la carte
- la création des dialogues
- la création de la fonction qui correspond à votre jeu et finalisation
La carte est un grand tuple qui est de la forme :
carte_monde = (
<carte_du_monde>,
(<carte_maison_1>, (x_entree1, y_entree1), (x_sortie1, y_sortie1)),
(<carte_maison_2>, (x_entree2, y_entree2), (x_sortie2, y_sortie2)),
...)
la <carte_du_monde> et les <carte_maison_X> sont des r-docstrings. Vous pouvez mettre à peu près n'importe quoi, veillez à bien respecter la légende :
- @ : caractère réservé au joueur (ne pas utiliser)
- ^ : porte de maison
- * : PnJ
- $ : adversaire
Dans le cas des maisons, le premier tuple correspond aux coordonnées de la porte de la maison *dans la carte du monde*. Le second tuple correspond aux coordonnées de la porte de la maison *dans la carte de la maison*.
Sur votre carte fraîchement créée, vous avez mis des PnJ (si ce n'est pas le cas, mettez-en, la suite sera plus intéressante ). L'idée est assez simple, il va falloir créer une fonction qui va prendre en argument l'expérience, les points de vie, l'id de la carte (l'index de la carte dans le tuple des maps, 0 : carte du monde, 1 : première maison etc), les coordonnées du joueur et les stat (une liste qui peut contenir des variables nécessaires aux mécaniques de votre jeu). Nous avons donc déjà :
def dialogue(xp, pv, carte_actuelle, x, y, stat):
Cette fonction va renvoyer un dictionnaire ou une liste.
C'est assez important, car, si vous renvoyez un dictionnaire, le dialogue sera lu en fonction des points d'expériences du joueur. Si vous renvoyez une liste, c'est le dialogue de la liste qui sera lu.
Le seul impératif que vous devez absolument respecter est la forme du dictionnaire et des listes.
Le dictionnaire est de la forme :
dialogues = {
xp_1: [...],
xp_2: [...],
...
"base": [...]
}
xp_X correspond au nombre de points d'expérience à avoir pour déclencher ce dialogue.
"base" est le dialogue lancé par défaut si aucun autre cas ne marche.
Les listes sont, elles, de la forme :
[xp_gagne, pv_gagne, "le texte du dialogue", booleen, ...]
xp_gagne correspond aux nombres de points d'expérience gagné lors de la lecture de ce dialogue.
pv_gagne même principe qu'avec l'XP, mais avec les points de vie.
booleen détermine s'il s'agit d'un monologue du PnJ ou si vous pouvez répondre au PnJ.
... correspond à des modificateurs des stats. Vous pouvez tout à fait les oublier en première utilisation
La question maintenant est de savoir comment relier les dialogues au PnJ. Vous êtes libre de mettre en place un système d'ID, Nous vous proposons un système peut-être plus simple : les coordonnées des PnJ. Nous allons ainsi avoir une fonction qui va ressembler à :
def dialogue(xp, pv, carte_actuelle, x, y, stat):
# Pour des raisons de clareté, on déclare un tuple avec les coordonnées
coords = (x, y)
# Si nous sommes en extérieur
if carte_actuelle == 0:
if coords == (X1, Y1):
return {...}
elif coords == (X2, Y2):
return {...}
# Si nous sommes dans la première maison de la liste
elif carte_actuelle == 1:
...
# Si le PnJ est bien sur la map, mais n'a aucun dialogue d'assigné :
return [0, 0, "Hmm ?", False]
Vous pouvez également créer des dialogues. Pour cela, il vous suffit de mettre le booleen sur True et de mettre les réponses possibles dans le corps du dialogue, par exemple : [0, 0, "Ceci est une question ? 1. Réponse 1 2. Réponse 2", True]. Le numéro de la réponse correspond au nombre de point d'expérience qu'elle rapporte, cela vous permet de gérer les différents cas dans al suite du dialogue.
Il reste à faire une petite fonction qui va créer un "modèle" de jeu de rôle vierge, il faudra lui donner la carte, la fonction des dialogues et ce sera fini !
La fonction est vraiment triviale :
def mon_jeu():
rpg_python = Asci(carte_monde, dialogue, 10, [])
rpg_python.mainloop()
Les deux premiers arguments carte_monde et dialogue ont déjà été vu. Le 10 correspond aux nombres de points d'expérience au bout duquel le programme s'arrête, il s'agit de la fin de la partie si vous voulez. La liste passée en dernier argument correspondent aux stats.
Exemples et astuces
Pour commencer simplement, voici une carte assez banale (ne pas oublier la virgule à la fin !) :
carte_monde = (
r"""
_ ###
/o\__ #####
| <>\ ###
|____| /_\
*
|==|==|==|==|==|==|==|""",)
Nous n'avons pas de maisons, juste un PnJ
Nous allons faire parler notre PnJ ! Et comme on est chaud, on va directement faire un petit dialogue. Pour bien séparer les réaction à la question du reste, je met un niveau d'indentation supplémentaire, ça ne change rien au comportement du code.
def dialogue(xp, pv, carte_actuelle, x, y, stat):
coords = (x, y)
if carte_actuelle == 0:
if coords == (2, 5): return {
0: [0, 0, "Coucou ! Comment ca va ? 1. Ca va, et toi ? 2. Bof... 3. Je t'emmerde.", True],
1: [3, 0, "Je vais bien, merci !", False],
2: [3, 0, "Ow, desole...", False],
3: [4, 0, "He, reviens quand tu sera de meilleure humeur !", False],
4: [2, 0, "Bon et bien, je crois bien que cette premiere carte s'est bien passee !", False],
5: [1, 0, "Je vais y aller, appelle moi si tu as besoin ;)", False],
6: [1, 0, "A pluche o/", False],
"base": [0, 0, "Oui ?", False]
}
return [0, 0, "Hmm ?", False]
Pour mettre en place vos dialogues (parce que j'espère que vous aurez un peu plus qu'un seul PnJ) faire un arbre de progression de l'XP peut être une bonne idée je vais essayer de le faire en ASCII-art pour vous montrer, mais avec une feuille et un stylo c'est plus simple.
1 4
-=----=---
0 / 2 5 \ 6 7
------=----=------=--=
\ 3 /
-=------------
Il reste la petite fonction à faire :
def mon_jeu():
rpg_python = Asci(carte_monde, dialogue, 7, [])
rpg_python.mainloop()
Nous avons le fichier complet :
from asci_lib import *
carte_monde = (
r"""
_ ###
/o\__ #####
| <>\ ###
|____| /_\
*
|==|==|==|==|==|==|==|""",)
def dialogue(xp, pv, carte_actuelle, x, y, stat):
coords = (x, y)
if carte_actuelle == 0:
if coords == (2, 5): return {
0: [0, 0, "Coucou ! Comment ca va ? 1. Ca va, et toi ? 2. Bof... 3. Je t'emmerde.", True],
1: [3, 0, "Je vais bien, merci !", False],
2: [3, 0, "Ow, desole...", False],
3: [4, 0, "He, reviens quand tu sera de meilleure humeur !", False],
4: [2, 0, "Bon et bien, je crois bien que cette premiere carte s'est bien passee !", False],
5: [1, 0, "Je vais y aller, appelle moi si tu as besoin ;)", False],
6: [1, 0, "A pluche o/", False],
"base": [0, 0, "Oui ?", False]
}
return [0, 0, "Hmm ?", False]
def mon_jeu():
rpg_python = Asci(carte_monde, dialogue, 7, [])
rpg_python.mainloop()
Ajouté le 14/08/2021 à 23:20 :
Mon précédent message est maintenant complètement obsolète
Je vais éviter de trop surcharger le forum avec le mode d'emploi, mais pour faire vite :
- un tuto d'utilisation et des exemples sont disponibles sur le README du dépôt
- le projet est susceptible d'avoir quelques fonctionnalités qui changent, mais rien de majeur n'est prévu.
Le moteur est téléchargeable ici.
Citer : Posté le 26/08/2021 00:22 | # | Fichier joint
Version 1.4.0
J'ai refondu le système d'évènements et de touches pour permettre quelque chose de vraiment généraliste.
Maintenant vous pouvez diviser vos évènements en catégories, chaque catégorie a sa fonction (qui renvoie un dictionnaire d'évènements ou un évènements) et cette fonction est appelée lorsque le joueur atteint un symbole que vous précisez.
De même pour les touches, les touches 4, 6, 7 et 8 ne sont pas utilisées par le moteur, vous pouvez donc les associer à des fonctions et en faire ce que vous voulez
Quels avantages ?
Cette nouvelle version est plus légère que la précédente avec 300 octets de gagné
Plus besoin de déclarer une fonction avec un def ... pass parce que le moteur voulait une fonction dont vous n'avez pas l'utilité pour votre projet : économisez de la place au sein de vos jeux.
Le code est plus aéré : si vous avez beaucoup d'évènements dans des circonstances différentes, vous pouvez séparer les cas plus simplement.
si les noms des fonctions sont bien choisis, le code devient extrêmement clair : evenements : {"*": pnj, "?": point_interet} avec pnj et point_interet des fonctions. De même pour les touches : touches = {7: affichage_stat, 8: inventaire} le mapping apparaît explicitement.
Comme dans la version précédente vous pouvez faire correspondre plusieurs symbole à la même fonction évènementielle. Par exemple si mes PnJ sont des * ou des ?, je peux écrire : evenements = {"*?": pnj}
Toute la documentation a été mise à jour, les exemples aussi
@RDP
Citer : Posté le 30/08/2021 15:36 | # | Fichier joint
Version 1.4.2
Petit changelog
Possibilité de modifier les statistiques au sein d'un évènement. Il suffit d'ajouter à la fin de la liste, un (ou plusieurs) tuple(s) de la forme (index, valeur). Cela va ajouter valeur à stat[index].
Les statistiques étaient déjà modifiables par effet de bord, la liste data qui est essentiel au fonctionnement du moteur l'est aussi. Attention toutefois, des modifications un peu hasardeuse du contenu de cette liste peut tout faire planter. (mais c'est de votre faute )
Vous pouvez maintenant vous amuser à faire des maps tordues avec des pièces cachées dans des maisons, le moteur sait faire !
Bon, en terme de poids, on a encore perdu 100 octets…
@RDP
Ajouté le 03/10/2021 à 15:27 :
Une toute petite mise à jour mineure. Elle ne concerne pas directement Asci, mais le script Python qui permet de convertir les fichiers tmx en carte pour Asci le truc chiant était de relever les coordonnées des portes à la main pour ensuite faire les points de passages. C'est maintenant géré par le script de conversion
Pour être plus clair, le script vous retourne un fichier .py avec la carte et les coordonnées des portes trouvées il vous reste plus qu'à remplir les trois derniers paramètres qui sont l'indice de la carte d'arrivée, et les coordonnées d'arrivées.
La syntaxe à donc légèrement changé : il faut préciser le symbole "porte" :
$ python converter ma_carte.tmx ^ ~
(avec ^ et ~ les portes)
Ajouté le 04/10/2021 à 17:19 :
C'est une idée qui m'a traversée l'esprit et pour l'instant j'ai ni vraiment le temps de m'y consacrer, ni beaucoup d'idée pour la commencer, mais peut-être un jour, il sera possible pour le développeur de modifier la map pendant l'exécution du jeu. J'entend par là, avoir un ennemi qui bouge, un PnJ qui suit le joueur, etc…
Pour l'instant j'essaye de voir de quoi j'ai besoin pour mettre ça en place, mais ça pose pas mal de problème ^^' il faudrait que le développeur connaisse les coordonnées à l'avance, ou alors fasse une fonction qui renvoie les coordonnées. Se pose aussi le problème du rafraîchissement. Mettre à jour les coordonnées à chaque fois que le joueur bouge me semble être la seule possibilité… Sans compter qu'a priori, y a pas de raison que le joueur ne puisse pas interagir avec le PnJ / ennemi / whatever qui bouge, donc il faudrait revoir le système qui gère ça et ne plus uniquement se fier aux coordonnées (qui vont changer au cours du temps).
'fin voila, j'ai pas d'idée de comment le faire, ça va un peu foutre la merde dans ce qui existe, et je manque un peu de temps pour faire ça d'un coup, mais ça me semble rigolo d'avoir un PnJ qui suit le joueur x)
Citer : Posté le 04/10/2021 19:04 | #
Effectivement modifier la map au fur et à mesure ça pose quelques soucis, après tu peux toujours ne t'occuper que de l'affichage et déléguer au programme utilisateur la tâche d'assurer que les objets ne se rentrent pas dedans.
Citer : Posté le 04/10/2021 20:39 | #
Mouaip, après les collisions, ça me semble pas être le plus dur à gérer… Il suffit juste de faire une vérification, et j'ai tout pour la faire
J'essaye de garder ça dans un coin de ma tête, si j'ai plus d'idée concrète sur la mise en place, je m'y pencherai de manière plus sérieuse
Ajouté le 31/10/2021 à 21:37 :
Attention, ça va être rapide. Petite mise à jour mineure avec l'ajout d'une fonction de routine exécutée à chaque tour de boucle.
(je vous avait prévenu )
Ajouté le 11/11/2021 à 15:12 :
Version 1.5.2
Quelques changements mineurs
@RDP
Citer : Posté le 11/11/2021 17:16 | # | Fichier joint
Juste pour @RDP qui n'était pas passé ^^'
Ajouté le 20/11/2021 à 13:33 :
Heyy ! C'est pour un petit sondage.
Lors des dialogues, il est parfois possible de choisir sa réplique. (le joueur entre alors le numéro de sa réplique).
Le plus naturel serait :
1. de lancer le dialogue suivant directement
2. d'attendre que le joueur reparle au PnJ en question pour avoir la suite (système actuel)
Voila, c'est tout
Merci d'avance de vos réponses o/
(Vous pouvez répondre dans #projets)
Citer : Posté le 20/11/2021 13:43 | #
Pour moi il faut que le dialogue continue immédiatement
Citer : Posté le 20/11/2021 13:47 | #
C'est effectivement ce que je me dis aussi
L'idée est venue d'un ami qui testait IDK et il a eu l'air surpris quand il a compris qu'il fallait reparler au personnage pour continuer le dialogue. En plus c'est vraiment juste une ligne à changer… xD
Ajouté le 21/11/2021 à 16:20 :
Version 1.5.3
Quelques changements mineurs, notamment :
Citer : Posté le 02/01/2022 17:59 | # | Fichier joint
Version 1.5.4
Toujours pas de gros changements majeurs. Jusqu'alors le fichier à inclure s'appelait asci_lib.py, raccourci en asci.py à partir de cette version.
Et autre changement, depuis la version 1.5.2, Asci est fourni avec une fonction, print_text qui permet d'afficher du texte formaté pour l'écran, cette même fonction peut dorénavant prendre trois arguments supplémentaires pour gérer les choix multiples. Ainsi vous pouvez préciser la valeur minimale que le joueur peut rentrer, la valeur maximale, et si la valeur n'est pas conforme à ce que vous voulez (pas dans l'intervalle, ou pas transtypable vers un int), la fonction vous renvoie la valeur par défaut.
Citer : Posté le 08/01/2022 22:59 | # | Fichier joint
Version 1.6.0
Une mise à jour avec une nouvelle fonctionnalité : les quêtes parallèles.
Journal des modifications :
La librairie est en fichier joint, la page de téléchargement est à jour, ainsi que le dépôt git, et la documentation aussi.
@RDP
Citer : Posté le 26/01/2022 18:53 | # | Fichier joint
Version 1.6.3
Après quelques fix de bugs, je sort la version 1.6.3 qui ne compote grosso modo qu'une seule mise à jour notable : l'implémentation des déplacement multiples. Lorsque vous êtes dans un jeu, vous pouvez entrez le symbole . suivi d'une liste de déplacement à faire. Le moteur va ainsi traiter les déplacements dans l'ordre. Toute forme d'interaction sera ignorée (pas de dialogue, pas de changement de cartes etc).
Le symbole peut être customisé lors du développement du jeu. En théorie, cela fonctionne pour toutes les actions, mais je pense le restreindre aux seuls déplacements dans une prochaine mise à jour (sinon cela implique de faire attention aux "touches" composées de plusieurs caractères).
Citer : Posté le 28/01/2022 23:31 | # | Fichier joint
Version 1.7.0
Il y a pas mal de chose à dire, utiliser le moteur est incontestablement devenu plus fin, il y a pas mal de chose qui sont pas trop documentable, typiquement des objets comme "Entity" ou "Screen" n'ont pas été conçus pour une utilisation publique, donc y a parfois des arguments un peu bizarres qui se baladent, et c'est pas trop justifiable (typiquement pour récupérer le contenu d'une cellule faut donner 3 arguments (les décalages monde / écran + coordonnées de la case ciblée). Donc si vous voulez utiliser le moteur à fond, je pense que le code est compréhensible, plus les exemples et avec la doc, ça doit être faisable, mais s'il vous reste des questions : n'hésitez pas.
Notez que normalement, y a pas trop besoin de mettre les mains dans le cambouis avec les fonctionnalités de bases, ça marche bien (normalement)
Chaque entité a un tuple qui contient son identifiant, son symbole (le caractère affiché à l'écran), ses coordonnées, son comportement et des arguments optionnels utiles pour certains types de comportements.
Il est possible de programmer des comportements dans des fonctions à part puis de rajouter ces comportements à ceux gérés de bases. Il y a pour l'instant 4 types de comportement :
- stand by (l'entité ne bouge pas)
- follow (l'entité suit le joueur)
- walk (l'entité se déplace entre plusieurs cases données dans l'argument facultatif)
@RDP
Citer : Posté le 29/01/2022 11:44 | # | Fichier joint
Version 1.7.1
Une toute petite mise à jour par rapport à la précédente :
Après quelques tests sur IDK, le moteur est bien fonctionnel, on peut noter que la calculatrice charge un peu à chaque frame (on voit le logo de chargement s'annuler), mais il n'y a pas de lag.
Citer : Posté le 30/01/2022 13:55 | # | Fichier joint
Version 1.7.2
Désolé pour les mises à jour très rapprochées ^^'
Celle-ci est assez importante malgré le fait qu'il ne s'agisse que de patch.
@RDP (à fusionner avec les deux changelogs précédents siouplaît )
Citer : Posté le 21/02/2022 11:37 | #
Avec une version pour calculette monochrome ça serait parfait !
(Je préfère clairement le Python au Casio Basic)
libMicrofx : https://www.planet-casio.com/Fr/forums/topic17259-2-libmicrofx-remplacez-fxlib-pour-faire-des-add-ins-tres-legers.html !
Racer3D : https://www.planet-casio.com/Fr/programmes/programme4444-1-racer3d-mb88-jeux-add-ins.html