mirror of
https://git.planet-casio.com/Slyvtt/Collab_RPG.git
synced 2024-12-29 13:03:43 +01:00
Merge pull request 'Gestion des événements.' (#46) from events into dev
Reviewed-on: https://git.planet-casio.com/Slyvtt/Collab_RPG/pulls/46
This commit is contained in:
commit
b98545a28f
11 changed files with 451 additions and 24 deletions
|
@ -26,6 +26,7 @@ set(SOURCES
|
||||||
src/game.c
|
src/game.c
|
||||||
src/dialogs.c
|
src/dialogs.c
|
||||||
src/npc.c
|
src/npc.c
|
||||||
|
src/events.c
|
||||||
# ...
|
# ...
|
||||||
)
|
)
|
||||||
# Shared assets, fx-9860G-only assets and fx-CG-50-only assets
|
# Shared assets, fx-9860G-only assets and fx-CG-50-only assets
|
||||||
|
|
257
design-document.md
Normal file
257
design-document.md
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
|
||||||
|
- Fcalva : J'ai condensé/commenté ce qui c'est dit dans le ticket #3 -
|
||||||
|
|
||||||
|
Pour le moment j'ai bien séparé entre les personnes car il n'y a pas vraiment eu de consensus, les noms entre parenthèses désignent l'auteur unique de la base/corps, et les [nom] : sont des commentaires. J'attends d'avoir des retours pour "unanimiser" quoi que ce soit.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* Gameplay / Mécaniques :
|
||||||
|
|
||||||
|
|
||||||
|
- Je (Fcalva) pense qu'il faudrait garder un équilibre de façon a ce que la seule "méta" est d'augmenter ses compétences ou d'utiliser des armes de plus haut niveau
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- Compétences primaires uniquement : Intelligence, Force, Agilité, Charisme, Rapidité, Courage (Et peut être d'autres ?) :
|
||||||
|
|
||||||
|
* L'intelligence influant sur la magie, le cooldown des sorts, leur quantité et niveau max. ..., et l'apprentissage : on pourrait avoir un bonus/malus de vitesse d’apprentissage suivant l'intelligence , surtout si la magie n'est pas si puissante.
|
||||||
|
*
|
||||||
|
|
||||||
|
* La Force influe sur les dégâts au corps a corps, peut être le nombre de slots d'inventaire si un système du genre est implémenté.
|
||||||
|
*
|
||||||
|
|
||||||
|
* L'Agilité permettrait "d'esquiver" des attaques (Les coups des ennemis ont une chance de ne pas faire de dégâts), la vitesse de mouvement
|
||||||
|
*
|
||||||
|
|
||||||
|
* La Personalité influerait grandement sur la persuasion, et sur les prix a un niveau moins élevé (Charisme), ansi que sur la résistance aux sorts (Volonté/courage)
|
||||||
|
*
|
||||||
|
|
||||||
|
* La Rapidité permet de savoir si le joueur a assez de temps pour faire une tâche où s'il se déplace assez vite. Peut être intéressant pour un système de combat en tour par tour.
|
||||||
|
*
|
||||||
|
|
||||||
|
* Le Courage peut avoir son utilité dans le cas d'un combat en tour par tour.
|
||||||
|
*
|
||||||
|
|
||||||
|
Fcalva : La rapidité est un peu redondante pour moi, et le Courage non nécéssaire ou pouvant être combiné avec le charisme en "Personalité"
|
||||||
|
|
||||||
|
J'ai combiné le courage et charisme en personalité
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-Pour l'amélioration des compétences principales, je (Fcalva) pense a un système de niveau "général" donnant une compétence a améliorer a chaque montée, c'est a voir.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- La magie serait de style "Classique" : Une pool de "mana" (nom a décider) et avec des sorts qui puisent dedans, des potions pour en redonner ainsi qu'une régénération naturelle. La quantité max de mana serait fixée par l'intelligence (def. à déterminer) du joueur et le port de vêtements enchantés.
|
||||||
|
|
||||||
|
Fcalva : Un système par cooldown serait peut être mieux pour favoriser la diversification
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- Des objets enchantés (Les candidats étant les capes, manteaux, gants, bottes, anneaux et amulettes) , uniquement trouvables/achetables, permettant d'avoir des bonus comme une augmentation de la régénération de mana (Liste a fixer)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Slyvtt : Je propose d'avoir un niveau de départ et un maximum pour chacune des propriétés. On peut imaginer aussi 2 personnages différents à choisir au départ pour mieux s'identifier (à voir si cohérent avec l'histoire par contre).
|
||||||
|
|
||||||
|
Fcalva : Il serait plus simple d'avoir un personnage avec une personalité "faible" et sans traits réelement distictifs (Comme Tintin) que d'avoir plusieurs personnages.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Structure du personnage à mettre en place en concordance:
|
||||||
|
|
||||||
|
{ .intelligence = xx;
|
||||||
|
|
||||||
|
.rapidite = yy;
|
||||||
|
|
||||||
|
.force = zz;
|
||||||
|
|
||||||
|
etc...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.hasBoot = true; //un pointeur vers/un autre struct serait mieux pour les items
|
||||||
|
|
||||||
|
.hasGloves = false;
|
||||||
|
|
||||||
|
.hasRing = true;
|
||||||
|
|
||||||
|
* etc...
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Note: matrice d’interaction à définir en parallèle pour savoir ce qui joue sur quoi si on garde l'idée.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* Univers :
|
||||||
|
Fcalva : Il faudrait aussi fixer la taille de la map, il y avait 20x10 maps je crois, ce qui ferait 480x480 tuiles. La taille des tuiles est pas véritablement fixée, mais pour des tuiles de 2m cela ferait presque 1x1km. Faire une map "a trous" serait aussi une possibilité, comme avoir une/des montagne ingravissable, qui est réelement juste un trou permettant d'augmenter les dimmensions de la carte
|
||||||
|
|
||||||
|
Note : trouver un nom pour le monde et pour la ou les ville(s).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(Shadow)
|
||||||
|
|
||||||
|
L'univers est entre le médiéval et l'antiquité fantastique. Une sorte d'époque médiévale qui aurait gardé les bons côtés de l'antiquité (avant l'arrivée en force des religions monothéistes) par exemple il y a l'eau courante, le niveau d'hygiène est assez bon, il y a des égoûts etc.
|
||||||
|
|
||||||
|
Il n'y a d'ailleurs pas de religions dans l'univers tel qu'il a été conçu.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
La religion serait abandonée dans une majorité du pays (Ou au moins de la région de jeu), ce que ça veut dire :
|
||||||
|
|
||||||
|
* - Autels/lieux de sacre/emblèmes religieuses a l'abandon
|
||||||
|
* - Conflits (Peut être a laisser vu que ça peut être un peu controvertiel)
|
||||||
|
* - Shamans/prêtres (Nomeclature a voir) exilés ou moins rejetés a travers la map
|
||||||
|
|
||||||
|
|
||||||
|
Une idée de lieux en ruines et certains villages (Ou peut être même régions si la map est assez grande) qui croient encore en des dieux (Des conflits dessus ?) tandis qu'une majorité n'y croient plus. Une sorte de conflit entre magie "humaine" et celle des dieux.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Pour le côté magie, dans les papiers, elle n'est pas omniprésente ni toute puissante, la scission magie blanche / magie noire n'a pas de sens. La Magie est vraiment vue comme un outil pratique utilisé plus ou moins selon les personnes qui ont été initié à elle. Par exemple, faire la vaisselle peut être fait grâce à la magie. C'est vraiment vu comme quelque chose de complètement commun et surtout de pratique.
|
||||||
|
|
||||||
|
Fcalva : Je pense qu'il faudrait donner a la magie une place dans le combat, et vu qu'elle est principalement utilitaire, il serait dur d'en apprendre pour le combat mais pouvant être très puissante car peu de défenses
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* Background du monde :
|
||||||
|
(Shadow)
|
||||||
|
|
||||||
|
L'intrigue se déroule dans un pays dirigé par un conseil élu par le peuple et dont les membres changent régulièrement. Cette jeune démocratie est née d'une révolution assez récente (moins d'un siècle) qui a renversé la dictature menée par une dizaine de personnages malheureusement intelligents.
|
||||||
|
|
||||||
|
De cette dictature, de bonnes choses en sont sorties (e.g. le tout-à-l'égoût à été mis en place durant cette période).
|
||||||
|
|
||||||
|
Mais lors de la révolution, les 10 dirigeants n'ont pas été capturés. De récents indices tendent à suggérer que certains d'entre eux sont encore vivants (le comment ont-ils fait pour survivre fait parties des grandes questions du scénario) et commencent à se manifester à travers des évènements inhabituels (e.g. saccage de villages dans les montagnes, disparitions de haut fonctionnaires, raid sur des caravanes etc) dont la puissance d'organisation ne correspond aux crimes habituels.
|
||||||
|
|
||||||
|
Fcalva : Il faudrait sans doute joindre ou reécrire cette partie et celle de l'intrigue qui se recoupent.
|
||||||
|
|
||||||
|
Je propose que l'histoire de fontaines de jouvence ne soit découverte qu'a un moment tardif dans le jeu. Pour que le jeu dure un peu, il faudrait qu'il y en aie 5-7 qui aient survécu. A voir. Pour moi la "Puissance d'organisation" ne suffirait pas pour deviner que c'est eux, mais bien d'autres indices.
|
||||||
|
|
||||||
|
#
|
||||||
|
|
||||||
|
* Personnage :
|
||||||
|
|
||||||
|
|
||||||
|
Il aurait au moins 17 où 18 ans. Dans l'ensemble, jeune et avec peu de compétences au départ
|
||||||
|
|
||||||
|
(A aller dans l'histoire, seulement pour noter) Il pourrait y avoir des sauts temporels, comme avec l'éclatement d'une guerre on se retrouverait plusieures années dans le futur, ou encore une mise en prison ou un exil. A la conclusion de l'histoire, il aurait vielli, mûri et surtout singnificativement monté en compétences (Requis pour battre le boss final de toute façon).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* Intrigue :
|
||||||
|
(Shadow)
|
||||||
|
|
||||||
|
Le personnage principal est aubergiste dans un village perdu au sud du pays. Son père, lequel était aubergiste aussi (avec un système de castes, ça peut très bien s'expliquer), vient de mourir légant au personnage principal son auberge (une gargote situé dans la capitale).
|
||||||
|
|
||||||
|
Le joueur se rend donc dans la gargote et découvre des indices que son père à laisser. Il avait manifestement prévu sa mort et les indices suggèrent qu'elle n'est pas dûe au hasard.
|
||||||
|
|
||||||
|
Le joueur remonte alors peu à peu les pistes laissées par son père, lesquelles s'arrêtent au niveau de l'ancienne forteresse qui servait de quartier général au conseil des 10 dictateurs.
|
||||||
|
|
||||||
|
Fcalva : Les traces pourraient plutôt remonter plus vaguement aux dictateurs et quelque chose comme une association ou organisation qui essaye de les faire revenir, menant alors a quelque chose comme j'avais suggéré de fontaine(s ?) de jouvence leur ayant permis de rester en vie malgré leur âge avancé, et lors de leur exil ont maitrisé une magie très puissante. Il faudrait donc s'assurer de leur neutralisation et détruire leurs découvertes (Mais peut être après en avoir appris quelque chose ?), en allant dans des dungeons /forteresses/autres ? a travers le pays, avec évidemment de péripéties entre.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Combats
|
||||||
|
|
||||||
|
-Le combat reste a décider entre du tour à tour et "temps réel", un vote pourrait être fait
|
||||||
|
|
||||||
|
(Mibi88)
|
||||||
|
|
||||||
|
Pour les combats je propose des batailles avec des soldats de l'état gouverné par les 10 dictateurs, dans une arène.
|
||||||
|
|
||||||
|
Fcalva : Pour moi le combat pourrait être libre, avec des encontres plus ou moins aléatoires et des forteresses/dungeons etc a "nettoyer" pour chaque dictateur.
|
||||||
|
|
||||||
|
SlyVTT: je suis assez pour un truc un en temps réel, perso j'ai du mal avec le tour par tour. Mais dans ce cas faut réfléchir à l'utilisation de la magie. On peut avoir un système mixte : pour les PNJs agressifs on est en temps réel (en gros on leur met leur race sur la carte directement) et pour les PNJs importants, on a un système au tour par tour afin d'approfondir les compétences de combat/magie à utiliser.
|
||||||
|
|
||||||
|
Je comprends pas trop le coup des "pnjs importants" (par exemple un des anciens dirigeants, le gardien d'une porte, ...) et "pnjs agressifs" (par exemple monstres croisés sur la map). Et un mélange temps réel/tour par tour est assez dur a faire correctement, surtout en gardant une consistence.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Combats temps réel : III
|
||||||
|
|
||||||
|
Combats tour par tour : II (Shadow avait l'ai pour mais n'a pas confirmé)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Map
|
||||||
|
|
||||||
|
[]C'est un open world :[]
|
||||||
|
|
||||||
|
Donc pas d'obstructions arbitraires a l'exploration de la map
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Pour le moment il y a une capitale, il y aurait des forteresses et des dongeons répartis et une chaîne de montagnes.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-Des passages a nettoyer d'ennemis pour aider la population (en échange de qqch) ou se faciliter la vie. Le même système pourrait être implémanté avec la plupart des zones d'ennemis (Ils ne réapparaissent pas après que la zone aie étée nettoyée).
|
||||||
|
|
||||||
|
-Des séparations claires entre zones, pour la cohérence
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Fcalva : Pour la carte malheureusement je n'ai de nouveau rien trouvé de mieux que d'embed une image :(
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Archive discussions :
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(combats)
|
||||||
|
|
||||||
|
Moi je suis pour des combats en temps réel
|
||||||
|
|
||||||
|
Fcalva : Moi aussi.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(univers)
|
||||||
|
|
||||||
|
Note (SlyVTT) si on garde le Tileset courant, il y a pas mal de tombes, ce qui fait explicitement référence à une forme de religion, au moins passée, donc il faut que cette partie soit un minimum prise en compte. Je propose de squeezer la religion en étant malin et dans la partie introductive, expliciter que "Les temps sont devenus sombres, les Dieux ont fui les humains, mais on trouve de nombreux vestiges passés de leur présence dans le monde "
|
||||||
|
|
||||||
|
Pour moi les tombes n'étaient qu'une coincidence, et de toute façon il va falloir étendre le tileset
|
||||||
|
|
||||||
|
OK, mais on peut profiter pour caler l'ambiance générale. Par exemple, "Désormais la magie est de retour. Autrefois l'apanage des seuls sorciers et érudits et pourchassée par le clergé, elle est maintenant présente dans la vie de tout un chacun ...". Ça fait un truc un peu "malin" qui permet d'avoir à la fois des trucs "historiques" orientés un peu religion (sans en parler) et d'avoir la magie dans le monde.
|
||||||
|
|
||||||
|
Et donc les différends autels seraient en ruines ? Faudrait peut être modifier le tileset ducoup
|
||||||
|
|
||||||
|
On pourrait avoir les deux, des trucs tout pétés d'ordre religieux et des trucs "neufs" pour les sorciers/shamans actuels. Idem, le tileset a des tombes cassées et d'autres récentes. On peut aussi annoncer que certains villages/habitants croient encore en un Dieu et poursuivent les rituels ancestraux. (Ce qui permet au passage d'avoir différents types de logiques à approfondir, par exemple dans un village "religieux" on a des gens qui offrent beaucoup, sont sympas, et dans un autre village, au contraire, il faut être "suffisamment magicien" pour débloquer des quêtes). bon c'est à développer.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(Map)
|
||||||
|
|
||||||
|
Créer des zones clairement séparées par une rivière, une forêt, un rempart pour créer de la cohérence (pourquoi cette zone est religieuse et l'autre non) et aussi pour avoir des points de passage "forcés" avec des quêtes à accomplir pour aller dans la zone suivante.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Des biomes pourraient aussi servir a délimiter les zones
|
||||||
|
|
||||||
|
Les points de passage forcés sont suboptimaux, a la limite un chemin plus rapide a "nettoyer" ou un pont a reconstruire pour les habitants, et/ou se simplifier la vie. Pour développer, avec un open world il n'y a pas besoin d'avoir des "niveaux" ou des zones côté gameplay
|
||||||
|
|
||||||
|
OK, l'idée est bonne MAIS: les interactions avec le décors ne sont pas prises en compte par le moteur et sur FX, on a pas la RAM adéquate pour stocker la map en mémoire et la rendre modifiable.
|
||||||
|
|
||||||
|
Un rechargement après une interaction ? Le moyen le plus simple serait de simplement faire une autre map mais en stockage c'est pas génial Le moyen le plus simple serait avec des bandits ou des monstres a éliminer, et qui ne respawn plus lorsque on les a nettoyés.
|
||||||
|
|
||||||
|
il faudrait avoir un système de patch local à appliquer, faisable mais chiant. Je propose de garder ça dans un coin pour le futur (la v2 l'année prochaine :D)
|
||||||
|
|
||||||
|
OK avec les items (bandits/monstres) c'est nettement plus réaliste, mais ça demande du "refactoring" de code important malgré tout. A mon avis faut être raisonnable pour le moment et garder cette option sous le coude pour la suite car je pense que c'est un gros gros boulot.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
PNJs / classes de personnages à intégrer
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* villageois (a priori pour dialogues et éventuellement lancer quêtes / donner des objets ou compétences)
|
||||||
|
* marchands (dialogues + acheter/vendre des objets)
|
||||||
|
* soldats/guerriers (dialogues ? + baston)
|
||||||
|
* bandits/monstres (baston directe ?)
|
||||||
|
* boss (par exemples les fameux 5/7 anciens dirigeants) (la totale : dialogues + baston + donner des objets ou compétence)
|
|
@ -5,6 +5,7 @@
|
||||||
#define USB_FEATURE 0
|
#define USB_FEATURE 0
|
||||||
#define DEBUGMODE 0
|
#define DEBUGMODE 0
|
||||||
|
|
||||||
|
#include <gint/display.h>
|
||||||
|
|
||||||
/* Enable GrayMode on either FX and FX_G3A targets */
|
/* Enable GrayMode on either FX and FX_G3A targets */
|
||||||
#if GINT_RENDER_MONO && defined(COLOR2BIT)
|
#if GINT_RENDER_MONO && defined(COLOR2BIT)
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
|
|
||||||
|
|
||||||
#if GINT_RENDER_RGB
|
#if GINT_RENDER_RGB
|
||||||
|
#warning "Ok!"
|
||||||
#define T_HEIGHT 16
|
#define T_HEIGHT 16
|
||||||
#define T_WIDTH 16
|
#define T_WIDTH 16
|
||||||
#define PXSIZE 2
|
#define PXSIZE 2
|
||||||
|
@ -21,6 +23,7 @@
|
||||||
#define P_WIDTH 16
|
#define P_WIDTH 16
|
||||||
#define P_HEIGHT 16
|
#define P_HEIGHT 16
|
||||||
#else
|
#else
|
||||||
|
#warning "Bad!"
|
||||||
#define T_HEIGHT 8
|
#define T_HEIGHT 8
|
||||||
#define T_WIDTH 8
|
#define T_WIDTH 8
|
||||||
#define PXSIZE 1
|
#define PXSIZE 1
|
||||||
|
@ -32,7 +35,7 @@
|
||||||
|
|
||||||
/* SPEED should NOT be 8 or bigger: it may cause bugs when handling
|
/* SPEED should NOT be 8 or bigger: it may cause bugs when handling
|
||||||
* collisions! */
|
* collisions! */
|
||||||
#define SPEED PXSIZE*2
|
#define SPEED (PXSIZE*2)
|
||||||
|
|
||||||
|
|
||||||
#define F_WIDTH (32*PXSIZE)
|
#define F_WIDTH (32*PXSIZE)
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "npc.h"
|
#include "npc.h"
|
||||||
|
#include "events.h"
|
||||||
|
|
||||||
|
|
||||||
#define BOX_HEIGHT (F_HEIGHT/PXSIZE+8)
|
#define BOX_HEIGHT (F_HEIGHT/PXSIZE+8)
|
||||||
|
@ -55,6 +56,7 @@ int showtext_opt(Game *game, bopti_image_t *face, char *text,
|
||||||
void for_each_screen(Game *game, unsigned int i),
|
void for_each_screen(Game *game, unsigned int i),
|
||||||
int line_duration, bool update_screen, unsigned int start_i,
|
int line_duration, bool update_screen, unsigned int start_i,
|
||||||
bool wait_continue) {
|
bool wait_continue) {
|
||||||
|
text = events_parse_string(&game->handler, text);
|
||||||
dfont(&FONT_USED);
|
dfont(&FONT_USED);
|
||||||
unsigned int i, n, y = PXSIZE, l = 0;
|
unsigned int i, n, y = PXSIZE, l = 0;
|
||||||
int line_max_chars, return_int = 0;
|
int line_max_chars, return_int = 0;
|
||||||
|
@ -213,11 +215,11 @@ unsigned int _i;
|
||||||
|
|
||||||
/* Get where I started drawing a dialog page, to be able to redraw the last page
|
/* Get where I started drawing a dialog page, to be able to redraw the last page
|
||||||
* for the end animation in _choice_call_before_end. */
|
* for the end animation in _choice_call_before_end. */
|
||||||
void _choice_screen_call(Game *game, unsigned int i) {
|
void _choice_screen_call( [[maybe_unused]] Game *game, unsigned int i) {
|
||||||
_i = i;
|
_i = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
int _choice_call_before_end(Game *game, unsigned int org_i) {
|
int _choice_call_before_end(Game *game, [[maybe_unused]] unsigned int org_i) {
|
||||||
int i, key;
|
int i, key;
|
||||||
/* Make a little animation because we looove little animations ;) */
|
/* Make a little animation because we looove little animations ;) */
|
||||||
for(i=0;i<DWIDTH/8+1;i++){
|
for(i=0;i<DWIDTH/8+1;i++){
|
||||||
|
|
123
src/events.c
Normal file
123
src/events.c
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "events.h"
|
||||||
|
|
||||||
|
void events_init_handler(EventHandler *handler) {
|
||||||
|
handler->vars = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int events_bind_variable(EventHandler *handler, int *var, char *name) {
|
||||||
|
if(handler->vars < MAX_VARIABLES){
|
||||||
|
handler->variables[handler->vars] = var;
|
||||||
|
handler->var_names[handler->vars++] = name;
|
||||||
|
}else{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char op_chars[OP_AMOUNT+1] = " =+-/*%";
|
||||||
|
|
||||||
|
int _op_null(int a, int b) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _op_set(int a, int b) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _op_add(int a, int b) {
|
||||||
|
return a+b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _op_sub(int a, int b) {
|
||||||
|
return a-b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _op_div(int a, int b) {
|
||||||
|
if(b == 0) return 0;
|
||||||
|
return a/b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _op_mul(int a, int b) {
|
||||||
|
return a*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _op_mod(int a, int b) {
|
||||||
|
if(b == 0) return 0;
|
||||||
|
return a%b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int (*_operations[OP_AMOUNT])(int, int) = {
|
||||||
|
_op_null,
|
||||||
|
_op_set,
|
||||||
|
_op_add,
|
||||||
|
_op_sub,
|
||||||
|
_op_div,
|
||||||
|
_op_mul,
|
||||||
|
_op_mod
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MIN(a, b) a < b ? a : b
|
||||||
|
|
||||||
|
char _message_buffer[MESSAGE_BUFFER_SZ];
|
||||||
|
char *events_parse_string(EventHandler *handler, char *message) {
|
||||||
|
size_t message_pos = 0;
|
||||||
|
char in_token = 0;
|
||||||
|
char var_name[TOKEN_MAX_SZ];
|
||||||
|
size_t name_pos = 0;
|
||||||
|
Operation var_op = OP_NULL;
|
||||||
|
char num[TOKEN_MAX_SZ];
|
||||||
|
size_t num_pos = 0;
|
||||||
|
Token tok_type = T_NULL;
|
||||||
|
char c;
|
||||||
|
size_t i, n;
|
||||||
|
int *var;
|
||||||
|
for(i=0;i<strlen(message);i++){
|
||||||
|
c = message[i];
|
||||||
|
if(c == '`'){
|
||||||
|
in_token = !in_token;
|
||||||
|
if(!in_token){
|
||||||
|
if(tok_type == T_VAR_EDIT){
|
||||||
|
/* Do the calculation */
|
||||||
|
var_name[MIN(name_pos, TOKEN_MAX_SZ)] = '\0';
|
||||||
|
num[MIN(num_pos, TOKEN_MAX_SZ)] = '\0';
|
||||||
|
for(n=0;n<handler->vars;n++){
|
||||||
|
if(!strcmp(var_name, handler->var_names[n])){
|
||||||
|
var = handler->variables[n];
|
||||||
|
if(var_op){
|
||||||
|
*var = _operations[var_op](*var, atoi(num));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Reset everything */
|
||||||
|
tok_type = T_NULL;
|
||||||
|
name_pos = 0;
|
||||||
|
var_op = OP_NULL;
|
||||||
|
num_pos = 0;
|
||||||
|
}
|
||||||
|
}else if(!in_token){
|
||||||
|
if(message_pos < TOKEN_MAX_SZ) _message_buffer[message_pos++] = c;
|
||||||
|
}
|
||||||
|
if(in_token && c != ' '){
|
||||||
|
if(tok_type == T_VAR_EDIT){
|
||||||
|
if(var_op != OP_NULL){
|
||||||
|
if(num_pos < TOKEN_MAX_SZ) num[num_pos++] = c;
|
||||||
|
}
|
||||||
|
if(strchr(op_chars, c)){
|
||||||
|
var_op = (Operation)(strchr(op_chars, c)-op_chars);
|
||||||
|
}
|
||||||
|
if(var_op == OP_NULL){
|
||||||
|
if(name_pos < TOKEN_MAX_SZ) var_name[name_pos++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(c == '$'){
|
||||||
|
tok_type = T_VAR_EDIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_message_buffer[MIN(message_pos, MESSAGE_BUFFER_SZ)] = '\0';
|
||||||
|
return _message_buffer;
|
||||||
|
}
|
35
src/events.h
Normal file
35
src/events.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef EVENTS_H
|
||||||
|
#define EVENTS_H
|
||||||
|
|
||||||
|
#define MAX_VARIABLES 32
|
||||||
|
#define MESSAGE_BUFFER_SZ 1024
|
||||||
|
#define TOKEN_MAX_SZ 1024
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int *variables[MAX_VARIABLES];
|
||||||
|
char *var_names[MAX_VARIABLES];
|
||||||
|
unsigned int vars;
|
||||||
|
} EventHandler;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
T_NULL,
|
||||||
|
T_VAR_EDIT,
|
||||||
|
T_AMOUNT
|
||||||
|
} Token;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
OP_NULL,
|
||||||
|
OP_SET,
|
||||||
|
OP_ADD,
|
||||||
|
OP_SUB,
|
||||||
|
OP_DIV,
|
||||||
|
OP_MUL,
|
||||||
|
OP_MOD,
|
||||||
|
OP_AMOUNT
|
||||||
|
} Operation;
|
||||||
|
|
||||||
|
void events_init_handler(EventHandler *handler);
|
||||||
|
int events_bind_variable(EventHandler *handler, int *var, char *name);
|
||||||
|
char *events_parse_string(EventHandler *handler, char *message);
|
||||||
|
|
||||||
|
#endif
|
10
src/game.c
10
src/game.c
|
@ -4,12 +4,14 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <gint/keyboard.h>
|
#include <gint/keyboard.h>
|
||||||
#include <gint/cpu.h>
|
#include <gint/cpu.h>
|
||||||
#include <gint/display.h>
|
#include <gint/display.h>
|
||||||
|
|
||||||
#include "npc.h"
|
#include "npc.h"
|
||||||
#include "stdlib.h"
|
|
||||||
|
|
||||||
extern bopti_image_t SignAction_img;
|
extern bopti_image_t SignAction_img;
|
||||||
|
|
||||||
|
@ -25,7 +27,7 @@ void game_logic(Game *game) {
|
||||||
update_npc( game );
|
update_npc( game );
|
||||||
|
|
||||||
/* we check if interactions are possible close to the player */
|
/* we check if interactions are possible close to the player */
|
||||||
for( int i=0; i<game->map_level->nbextradata; i++ )
|
for( uint32_t i=0; i<game->map_level->nbextradata; i++ )
|
||||||
{
|
{
|
||||||
/* simple distance check along X and Y axis */
|
/* simple distance check along X and Y axis */
|
||||||
/* Be careful to use world coordinates, not local (i.e.map) ones */
|
/* Be careful to use world coordinates, not local (i.e.map) ones */
|
||||||
|
@ -47,7 +49,7 @@ void game_logic(Game *game) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for( int i=0; i<nbNPC; i++ )
|
for( uint32_t i=0; i<nbNPC; i++ )
|
||||||
{
|
{
|
||||||
/* simple distance check along X and Y axis */
|
/* simple distance check along X and Y axis */
|
||||||
/* Be careful to use world coordinates, not local (i.e.map) ones */
|
/* Be careful to use world coordinates, not local (i.e.map) ones */
|
||||||
|
@ -96,6 +98,8 @@ void draw(Game *game) {
|
||||||
player_draw(game);
|
player_draw(game);
|
||||||
render_map_by_layer(game, FOREGROUND);
|
render_map_by_layer(game, FOREGROUND);
|
||||||
render_indicator( game );
|
render_indicator( game );
|
||||||
|
dprint(8, 8, C_BLACK, "Lifes: %d", game->player.life);
|
||||||
|
dprint(8, 16, C_BLACK, "Mana: %d", game->mana);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Key management */
|
/* Key management */
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include <gint/display.h>
|
#include <gint/display.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "events.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* The direction where the player is going to. */
|
/* The direction where the player is going to. */
|
||||||
|
@ -130,7 +132,8 @@ typedef struct {
|
||||||
* the world. */
|
* the world. */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Map *map_level; /* The level that the player is currently playing */
|
Map *map_level; /* The level that the player is currently playing */
|
||||||
Player player; /* The player data (see player.h). */
|
Player player; /* The player data. */
|
||||||
|
EventHandler handler; /* The event handler (see events.h). */
|
||||||
/* Some global variables */
|
/* Some global variables */
|
||||||
/* Set to true when asked for exit */
|
/* Set to true when asked for exit */
|
||||||
bool exittoOS;
|
bool exittoOS;
|
||||||
|
@ -145,6 +148,8 @@ typedef struct {
|
||||||
bool debug_map;
|
bool debug_map;
|
||||||
bool debug_player;
|
bool debug_player;
|
||||||
bool debug_extra;
|
bool debug_extra;
|
||||||
|
|
||||||
|
int mana; /* Only for testing events TODO: Remove this! */
|
||||||
} Game;
|
} Game;
|
||||||
|
|
||||||
/* (Mibi88) TODO: Describe what this function is doing. */
|
/* (Mibi88) TODO: Describe what this function is doing. */
|
||||||
|
|
15
src/main.c
15
src/main.c
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "npc.h"
|
#include "npc.h"
|
||||||
|
#include "events.h"
|
||||||
|
|
||||||
#if USB_FEATURE
|
#if USB_FEATURE
|
||||||
#include <gint/usb-ff-bulk.h>
|
#include <gint/usb-ff-bulk.h>
|
||||||
|
@ -36,10 +37,11 @@ extern Map *worldRPG[];
|
||||||
Game game = {
|
Game game = {
|
||||||
NULL,
|
NULL,
|
||||||
{12*PXSIZE, 36*PXSIZE, 0, 0, 12*PXSIZE, 36*PXSIZE, 100, SPEED, false, 0, false, false},
|
{12*PXSIZE, 36*PXSIZE, 0, 0, 12*PXSIZE, 36*PXSIZE, 100, SPEED, false, 0, false, false},
|
||||||
|
{{}, {}, 0},
|
||||||
false, false, false, 0
|
false, false, false, 0
|
||||||
|
|
||||||
/* debug variables*/
|
/* debug variables*/
|
||||||
, false, false, false
|
, false, false, false, 100
|
||||||
};
|
};
|
||||||
|
|
||||||
/* screen capture management code */
|
/* screen capture management code */
|
||||||
|
@ -100,6 +102,9 @@ int main(void) {
|
||||||
timer_start(timer);
|
timer_start(timer);
|
||||||
|
|
||||||
game.map_level = worldRPG[0];
|
game.map_level = worldRPG[0];
|
||||||
|
events_init_handler(&game.handler);
|
||||||
|
events_bind_variable(&game.handler, (int*)&game.player.life, "life");
|
||||||
|
events_bind_variable(&game.handler, &game.mana, "mana");
|
||||||
|
|
||||||
reload_npc(&game);
|
reload_npc(&game);
|
||||||
|
|
||||||
|
@ -115,14 +120,6 @@ int main(void) {
|
||||||
dgray(DGRAY_ON);
|
dgray(DGRAY_ON);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
showtext_dialog(&game, &player_face_img, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet.", true, true);
|
|
||||||
int in = showtext_dialog_ask(&game, &player_face_img, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet.", true, false, "Lorem\0Ipsum\0Dolor", 3, 0);
|
|
||||||
if(in==2) showtext_dialog(&game, &player_face_img, "You choosed Dolor", false, true);
|
|
||||||
else if(in==1) showtext_dialog(&game, &player_face_img, "You choosed Ipsum", false, true);
|
|
||||||
else showtext_dialog(&game, &player_face_img, "You choosed Lorem", false, true);
|
|
||||||
*/
|
|
||||||
|
|
||||||
do{
|
do{
|
||||||
/* clear screen */
|
/* clear screen */
|
||||||
dclear(C_WHITE);
|
dclear(C_WHITE);
|
||||||
|
|
14
src/map.c
14
src/map.c
|
@ -199,8 +199,8 @@ short int get_tile(Game *game, int x, int y, int l) {
|
||||||
Map *map_level = game->map_level;
|
Map *map_level = game->map_level;
|
||||||
|
|
||||||
/* Get the tile at (x, y) on layer l. Returns the tile ID or MAP_OUTSIDE if
|
/* Get the tile at (x, y) on layer l. Returns the tile ID or MAP_OUTSIDE if
|
||||||
* she's not found. */
|
* it's not found. */
|
||||||
return (x>=0 && x < map_level->w && y>=0 && y < map_level->h) ?
|
return (x>=0 && x < (int) map_level->w && y>=0 && y < (int) map_level->h) ?
|
||||||
map_level->layers[l][y * map_level->w + x] : MAP_OUTSIDE;
|
map_level->layers[l][y * map_level->w + x] : MAP_OUTSIDE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ short int get_walkable(Game *game, int x, int y) {
|
||||||
Map *map_level = game->map_level;
|
Map *map_level = game->map_level;
|
||||||
/* Get the tile at (x, y). Returns the tile ID or MAP_OUTSIDE if she's not
|
/* Get the tile at (x, y). Returns the tile ID or MAP_OUTSIDE if she's not
|
||||||
* found. */
|
* found. */
|
||||||
return (x>=0 && x < map_level->w && y>=0 && y < map_level->h) ?
|
return (x>=0 && x < (int) map_level->w && y>=0 && y < (int) map_level->h) ?
|
||||||
map_level->walkable[y * map_level->w + x] : MAP_OUTSIDE;
|
map_level->walkable[y * map_level->w + x] : MAP_OUTSIDE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,8 +217,8 @@ short int get_walkable(Game *game, int x, int y) {
|
||||||
Map *get_map_for_coordinates( Game *game, int x, int y )
|
Map *get_map_for_coordinates( Game *game, int x, int y )
|
||||||
{
|
{
|
||||||
/* check if the current map contains the point */
|
/* check if the current map contains the point */
|
||||||
if (x>=game->map_level->xmin && x<game->map_level->xmax &&
|
if (x>= (int)game->map_level->xmin && x< (int)game->map_level->xmax &&
|
||||||
y>=game->map_level->ymin && y<game->map_level->ymax)
|
y>= (int)game->map_level->ymin && y< (int)game->map_level->ymax)
|
||||||
return game->map_level;
|
return game->map_level;
|
||||||
|
|
||||||
/* else we check in worldRPG if there is a mal containing that point */
|
/* else we check in worldRPG if there is a mal containing that point */
|
||||||
|
@ -226,8 +226,8 @@ Map *get_map_for_coordinates( Game *game, int x, int y )
|
||||||
Map *current = worldRPG[i];
|
Map *current = worldRPG[i];
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (x>=current->xmin && x<current->xmax &&
|
if (x>= (int)current->xmin && x< (int)current->xmax &&
|
||||||
y>=current->ymin && y<current->ymax)
|
y>= (int)current->ymin && y< (int)current->ymax)
|
||||||
return current;
|
return current;
|
||||||
i++;
|
i++;
|
||||||
current = worldRPG[i];
|
current = worldRPG[i];
|
||||||
|
|
|
@ -22,7 +22,7 @@ float length( float x, float y )
|
||||||
return sqrtf( x*x+y*y );
|
return sqrtf( x*x+y*y );
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_npc(Game *game)
|
void update_npc( [[maybe_unused]] Game *game)
|
||||||
{
|
{
|
||||||
for( uint32_t u=0; u<nbNPC; u++ )
|
for( uint32_t u=0; u<nbNPC; u++ )
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue