mirror of
https://git.planet-casio.com/Slyvtt/Collab_RPG.git
synced 2025-01-01 06:23:40 +01:00
382 lines
15 KiB
C
382 lines
15 KiB
C
#include "dialogs.h"
|
|
|
|
#include <gint/keyboard.h>
|
|
#include <gint/cpu.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "config.h"
|
|
#include "game.h"
|
|
#include "npc.h"
|
|
#include "events.h"
|
|
|
|
|
|
#define BOX_HEIGHT (F_HEIGHT/PXSIZE+8)
|
|
|
|
#define CHOICE_BOX_HEIGHT 10
|
|
#define CHOICE_BOX_PADDING_TOP 3
|
|
|
|
|
|
extern font_t fontRPG;
|
|
#define FONT_USED fontRPG
|
|
|
|
#if GRAYMODEOK
|
|
#include <gint/gray.h>
|
|
uint32_t *lightVRAMnext, *darkVRAMnext;
|
|
uint32_t *lightVRAMcurrent, *darkVRAMcurrent;
|
|
#endif //GRAYMODEOK
|
|
|
|
/* the color of the text to go to the next dialog phase */
|
|
/* it improves readability to have somathing lighter */
|
|
#if GRAYMODEOK || (defined(FXCG50) && !defined(COLOR1BIT))
|
|
#define NEXT_COLOR C_DARK
|
|
#else
|
|
#define NEXT_COLOR C_BLACK
|
|
#endif
|
|
|
|
|
|
void blit()
|
|
{
|
|
dupdate();
|
|
|
|
#if GRAYMODEOK
|
|
dgray_getvram( &lightVRAMnext, &darkVRAMnext );
|
|
dgray_getscreen( &lightVRAMcurrent, &darkVRAMcurrent );
|
|
|
|
memcpy( lightVRAMnext, lightVRAMcurrent, 256*sizeof( uint32_t) );
|
|
memcpy( darkVRAMnext, darkVRAMcurrent, 256*sizeof( uint32_t) );
|
|
#endif
|
|
}
|
|
|
|
|
|
int showtext_opt(Game *game, bopti_image_t *face, char *text,
|
|
int call_before_end(Game *game, unsigned int i),
|
|
bool start_anim,
|
|
bool end_anim,
|
|
void for_each_screen(Game *game, unsigned int i),
|
|
int line_duration, bool update_screen, unsigned int start_i,
|
|
bool wait_continue) {
|
|
text = events_parse_string(&game->handler, text);
|
|
dfont(&FONT_USED);
|
|
unsigned int i, n, y = PXSIZE, l = 0;
|
|
int line_max_chars, return_int = 0;
|
|
unsigned int max_lines_amount = (BOX_HEIGHT-2)*PXSIZE/
|
|
(FONT_USED.line_height+PXSIZE);
|
|
const char *c;
|
|
if(start_anim){
|
|
/* Run a little fancy animation. */
|
|
for(i=0;i<=BOX_HEIGHT;i++){
|
|
/* Redrawing the entire screen, because maybe there was no dialog
|
|
displayed before. */
|
|
update_npcs(game);
|
|
draw(game);
|
|
|
|
/* Fill the dialog box with white */
|
|
drect(0, 0, DWIDTH, i*PXSIZE, C_WHITE);
|
|
/* Draw a thick black line on the bottom of the dialog. */
|
|
drect(0, i*PXSIZE, DWIDTH, (i+1)*PXSIZE, C_BLACK);
|
|
|
|
/* Draw the part of the face of the player that can fit correctly in
|
|
* the dialog drawn. */
|
|
dsubimage(4*PXSIZE, 2*PXSIZE, face, 0, 0, F_WIDTH, (i-8)*PXSIZE,
|
|
DIMAGE_NONE);
|
|
|
|
blit();
|
|
|
|
while(game->frame_duration < 20) sleep();
|
|
game->frame_duration = 0;
|
|
}
|
|
}else{
|
|
/* Here I'm drawing the same as if start_anim is true, but whitout
|
|
* making an animation. */
|
|
draw(game);
|
|
drect(0, 0, DWIDTH, BOX_HEIGHT*PXSIZE, C_WHITE);
|
|
drect(0, BOX_HEIGHT*PXSIZE, DWIDTH, (BOX_HEIGHT+1)*PXSIZE, C_BLACK);
|
|
dimage(4*PXSIZE, 2*PXSIZE, face);
|
|
|
|
if(update_screen){
|
|
blit();
|
|
|
|
while(game->frame_duration < 20) sleep();
|
|
game->frame_duration = 0;
|
|
}
|
|
}
|
|
/* We should start to drawing the text on the x axis at BOX_HEIGHT to avoid
|
|
* drawing on the face. */
|
|
for(i=start_i;i<strlen(text);i++){
|
|
if(!l && for_each_screen) for_each_screen(game, i);
|
|
/* Get how many chars we can draw on screen with a padding on the left
|
|
* of BOX_HEIGHT px and on the right of 1 px. */
|
|
c = drsize(text+i, &FONT_USED, DWIDTH-(BOX_HEIGHT*PXSIZE+PXSIZE), NULL);
|
|
/* c is a pointer to the last char that can be drawn. So: */
|
|
line_max_chars = c-(text+i);
|
|
/* TODO: Handle lines that are longer than what I can draw and '\n'. */
|
|
/* Loop from the end to the start for word wrap. */
|
|
if(*c){
|
|
/* If we are not drawing the end of the text. */
|
|
for(n=line_max_chars; n>0; n--) {
|
|
/* If we found a space, we can draw this line and do the same
|
|
* for the next line. */
|
|
if(text[i+n] == ' '){
|
|
dtext_opt(BOX_HEIGHT*PXSIZE, y, C_BLACK, C_NONE, DTEXT_LEFT,
|
|
DTEXT_TOP, text+i, n); /* Draw everything. */
|
|
/* Increment y by the line height. */
|
|
y += FONT_USED.line_height+PXSIZE;
|
|
i += n; /* We drew everything to i+n */
|
|
l++; /* We drew one more line. */
|
|
break;
|
|
}
|
|
}
|
|
}else{
|
|
/* If it is the last line of the text. */
|
|
dtext_opt(BOX_HEIGHT*PXSIZE, y, C_BLACK, C_NONE, DTEXT_LEFT,
|
|
DTEXT_TOP, text+i, line_max_chars);
|
|
y += FONT_USED.line_height+PXSIZE;
|
|
i += line_max_chars;
|
|
l++;
|
|
}
|
|
if(l>=max_lines_amount-1){
|
|
/* We drew one entire screen, reset everything to draw the next one.
|
|
*/
|
|
/* Make a little animation :). */
|
|
if(update_screen) blit();
|
|
while(game->frame_duration < line_duration) sleep();
|
|
game->frame_duration = 0;
|
|
/* Ask the user to press SHIFT to continue. */
|
|
dtext(BOX_HEIGHT*PXSIZE, y, NEXT_COLOR, "[SHIFT] : suite...");
|
|
}
|
|
/* Make a little animation :). */
|
|
if(update_screen) blit();
|
|
if(l>=max_lines_amount-1){
|
|
/* If we drew one entire screen. */
|
|
/* Wait that the SHIFT key is pressed if we should. */
|
|
if(wait_continue) while(getkey_opt(GETKEY_DEFAULT & ~GETKEY_MOD_SHIFT & ~GETKEY_MOD_ALPHA, NULL).key != KEY_SHIFT) sleep();
|
|
/* Clear the text area. */
|
|
drect(BOX_HEIGHT*PXSIZE, 0, DWIDTH, (BOX_HEIGHT-1)*PXSIZE-2,
|
|
C_WHITE);
|
|
/* Reset y and l. */
|
|
y = PXSIZE;
|
|
l = 0;
|
|
}
|
|
else{
|
|
/* Else, wait a bit for the animation. */
|
|
while(game->frame_duration < line_duration) sleep();
|
|
game->frame_duration = 0;
|
|
}
|
|
}
|
|
if(l<max_lines_amount-1){
|
|
/* If we have not filled everthing with text at the end. */
|
|
/* Make a little animation :). */
|
|
if(update_screen) blit();
|
|
while(game->frame_duration < line_duration) sleep();
|
|
game->frame_duration = 0;
|
|
/* Ask the user to press SHIFT to continue. */
|
|
dtext(BOX_HEIGHT*PXSIZE, y, NEXT_COLOR, "[SHIFT] : suite...");
|
|
/* Update the screen and wait for SHIFT being pressed, if needed. */
|
|
if(update_screen) blit();
|
|
if(wait_continue) while(getkey_opt( GETKEY_DEFAULT & ~GETKEY_MOD_SHIFT & ~GETKEY_MOD_ALPHA, NULL).key != KEY_SHIFT) sleep();
|
|
}
|
|
if(call_before_end) return_int = call_before_end(game, i);
|
|
if(end_anim){
|
|
/* Run another little fancy animation if we should. */
|
|
for(i=BOX_HEIGHT;i>0;i--){
|
|
/* It is the same as the start animation. */
|
|
update_npcs(game);
|
|
draw(game);
|
|
drect(0, 0, DWIDTH, i*PXSIZE, C_WHITE);
|
|
drect(0, i*PXSIZE, DWIDTH, (i+1)*PXSIZE, C_BLACK);
|
|
dsubimage(4*PXSIZE, 2*PXSIZE, face, 0, 0, F_WIDTH, (i-8)*PXSIZE,
|
|
DIMAGE_NONE);
|
|
|
|
dupdate();
|
|
|
|
while(game->frame_duration < 20) sleep();
|
|
game->frame_duration = 0;
|
|
}
|
|
}
|
|
return return_int;
|
|
}
|
|
|
|
void showtext_dialog(Game *game, bopti_image_t *face, char *text,
|
|
bool dialog_start, bool dialog_end) {
|
|
/* Run showtext_opt with some default values. It makes it easier to use in
|
|
* simple dialogs. */
|
|
showtext_opt(game, face, text, NULL, dialog_start, dialog_end, NULL, 100,
|
|
true, 0, true);
|
|
}
|
|
|
|
|
|
/* Some variables and pointers used to get some arguments passed in
|
|
* showtext_dialog_ask in _choice_call_before_end. */
|
|
char *_choices, *_text;
|
|
int _choices_amount, _default_choice;
|
|
bopti_image_t *_face;
|
|
unsigned int _i;
|
|
|
|
/* 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. */
|
|
void _choice_screen_call( [[maybe_unused]] Game *game, unsigned int i) {
|
|
_i = i;
|
|
}
|
|
|
|
int _choice_call_before_end(Game *game, [[maybe_unused]] unsigned int org_i) {
|
|
int i, key;
|
|
/* Make a little animation because we looove little animations ;) */
|
|
for(i=0;i<DWIDTH/8+1;i++){
|
|
/* Fill the interaction box with white */
|
|
drect(0, (BOX_HEIGHT+1)*PXSIZE+1, i*(DWIDTH/8),
|
|
(BOX_HEIGHT+CHOICE_BOX_HEIGHT)*PXSIZE, C_WHITE);
|
|
/* Draw a thick border on the right of the box. */
|
|
drect(i*(DWIDTH/8), BOX_HEIGHT*PXSIZE, i*(DWIDTH/8)+PXSIZE-1,
|
|
(BOX_HEIGHT+CHOICE_BOX_HEIGHT+1)*PXSIZE, C_BLACK);
|
|
/* Draw a thick border on the bottom of the box. */
|
|
drect(0, (BOX_HEIGHT+CHOICE_BOX_HEIGHT)*PXSIZE, i*(DWIDTH/8),
|
|
(BOX_HEIGHT+CHOICE_BOX_HEIGHT+1)*PXSIZE, C_BLACK);
|
|
/* Show everyting on screen. */
|
|
blit();
|
|
/* Wait some ms so that the animation isn't too fast. */
|
|
while(game->frame_duration < 20) sleep();
|
|
game->frame_duration = 0;
|
|
}
|
|
/* Calculate the maximal size of a choice. */
|
|
const int choice_size = DWIDTH/_choices_amount;
|
|
/* arrow_width: The space taken by the arrow that shows the selected item.
|
|
* arrow_height: The height of the arrow used to show which item is choosen.
|
|
* Used to calculate the size of the rectangle used to remove
|
|
* him.
|
|
* selected: The selected item.
|
|
* pos: The position of the item we're drawing in the choice
|
|
* string. The choice string is not really a string, it is
|
|
* made of multiple '\0' terminated strings, that are in
|
|
* memory one after the other.
|
|
*/
|
|
int arrow_width, arrow_height, selected = _default_choice, pos = 0;
|
|
/* Calculate the size of the arrow. */
|
|
dsize(">", &FONT_USED, &arrow_width, &arrow_height);
|
|
/* Add the character spacing of the font to it. */
|
|
arrow_width += FONT_USED.char_spacing;
|
|
for(i=0;i<_choices_amount;i++){
|
|
dtext(i*choice_size+arrow_width+PXSIZE,
|
|
(BOX_HEIGHT+CHOICE_BOX_PADDING_TOP)*PXSIZE, C_BLACK,
|
|
_choices+pos);
|
|
pos += strlen(_choices+pos)+1;
|
|
}
|
|
do{
|
|
/* Display the diffrent choices. */
|
|
for(i=0;i<_choices_amount;i++){
|
|
if(i == selected) dtext(i*choice_size+PXSIZE,
|
|
(BOX_HEIGHT+CHOICE_BOX_PADDING_TOP)*PXSIZE,
|
|
C_BLACK, ">");
|
|
}
|
|
blit();
|
|
key = getkey_opt( GETKEY_DEFAULT & ~GETKEY_MOD_SHIFT & ~GETKEY_MOD_ALPHA, NULL).key;
|
|
/* If the player pressed the left arrow key and has not already selected
|
|
* the first possible choice. */
|
|
if(key == KEY_LEFT && selected > 0){
|
|
/* Remove the old arrow. */
|
|
drect(selected*choice_size+PXSIZE,
|
|
(BOX_HEIGHT+CHOICE_BOX_PADDING_TOP)*PXSIZE,
|
|
selected*choice_size+PXSIZE+arrow_width,
|
|
(BOX_HEIGHT+CHOICE_BOX_PADDING_TOP)*PXSIZE+arrow_height,
|
|
C_WHITE);
|
|
|
|
/* Move the selection arrow and update the selected item. */
|
|
selected--;
|
|
}
|
|
/* If the player pressed the right arrow key and has not already
|
|
* selected the last possible choice. */
|
|
else if(key == KEY_RIGHT && selected < _choices_amount-1){
|
|
/* Remove the old arrow. */
|
|
drect(selected*choice_size+PXSIZE,
|
|
(BOX_HEIGHT+CHOICE_BOX_PADDING_TOP)*PXSIZE,
|
|
selected*choice_size+PXSIZE+arrow_width,
|
|
(BOX_HEIGHT+CHOICE_BOX_PADDING_TOP)*PXSIZE+arrow_height,
|
|
C_WHITE);
|
|
|
|
/* Move the selection arrow and update the selected item. */
|
|
selected++;
|
|
}
|
|
/* If the user has not validated his choice by pressing SHIFT, we loop one
|
|
* more time. */
|
|
}while(key != KEY_SHIFT);
|
|
/* Make a little animation because we looove little animations ;) */
|
|
for(i=DWIDTH/8+1;i>0;i--){
|
|
/* I'm drawing the same box as on the start animation */
|
|
update_npcs(game);
|
|
draw(game);
|
|
showtext_opt(game, _face, _text, NULL, false, false, NULL, 0, false,
|
|
_i, false);
|
|
drect(0, (BOX_HEIGHT+1)*PXSIZE+1, i*(DWIDTH/8),
|
|
(BOX_HEIGHT+CHOICE_BOX_HEIGHT)*PXSIZE, C_WHITE);
|
|
drect(i*(DWIDTH/8), BOX_HEIGHT*PXSIZE, i*(DWIDTH/8)+PXSIZE-1,
|
|
(BOX_HEIGHT+CHOICE_BOX_HEIGHT+1)*PXSIZE, C_BLACK);
|
|
drect(0, (BOX_HEIGHT+CHOICE_BOX_HEIGHT)*PXSIZE, i*(DWIDTH/8),
|
|
(BOX_HEIGHT+CHOICE_BOX_HEIGHT+1)*PXSIZE, C_BLACK);
|
|
dupdate();
|
|
while(game->frame_duration < 20) sleep();
|
|
game->frame_duration = 0;
|
|
}
|
|
/* Return the selected item because he'll also be returned by showtext_opt.
|
|
*/
|
|
return selected;
|
|
}
|
|
|
|
int showtext_dialog_ask(Game *game, bopti_image_t *face, char *text, bool start,
|
|
bool end, char *choices, int choices_amount,
|
|
int default_choice) {
|
|
/* Put some arguments in global pointers and variables to make them
|
|
* accessible by _choice_call_before_end. */
|
|
_choices = choices;
|
|
_choices_amount = choices_amount;
|
|
_default_choice = default_choice;
|
|
_face = face;
|
|
_text = text;
|
|
/* Run showtext_opt and return his return value (the return value of
|
|
*_choice_call_before_end) */
|
|
return showtext_opt(game, face, text, _choice_call_before_end, start, end,
|
|
_choice_screen_call, 100, true, 0, true);
|
|
}
|
|
|
|
|
|
|
|
|
|
void initiate_dialog_sequence(Game *game, bopti_image_t *face, uint32_t dialogNumber )
|
|
{
|
|
Dialog *currentDiag = &game->map_level->dialogs[ dialogNumber ];
|
|
|
|
/* we collect the information */
|
|
char *text = currentDiag->dialog;
|
|
|
|
char *choices = currentDiag->choices ;
|
|
char *conclusion1 = currentDiag->conclusion1;
|
|
int next1 = currentDiag->next1;
|
|
char *conclusion2 = currentDiag->conclusion2;
|
|
int next2 = currentDiag->next2;
|
|
int nextOther = currentDiag->nextOther;
|
|
int isQuestion = currentDiag->isQuestion;
|
|
|
|
/* we treat the action - i.e. we show a dialog */
|
|
if (isQuestion == 1) /* we have to manage a question */
|
|
{
|
|
int answer = showtext_dialog_ask( game, face, text, true, true, choices, 2, 0 );
|
|
|
|
/* TO DO we need to split the strings conclusion1 and conclusion2 */
|
|
/* to extract the "gift" part */
|
|
|
|
if (answer==0)
|
|
{
|
|
showtext_dialog( game, face, conclusion1, true, true );
|
|
if (next1!=-1) initiate_dialog_sequence( game, face, next1 );
|
|
}
|
|
else
|
|
{
|
|
showtext_dialog( game, face, conclusion2, true, true );
|
|
if (next2!=-1) initiate_dialog_sequence( game, face, next2 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
showtext_dialog( game, face, text, true, true );
|
|
if (nextOther!=-1) initiate_dialog_sequence( game, face, nextOther );
|
|
}
|
|
}
|