#include "dialogs.h" #include #include #include #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 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;i0; 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(lframe_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;iframe_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 ); } }