/** * Copyright (c) 2022 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "textbox.h" char TEXTBOX_TEXTS[TEXTBOX_SCROLL_ROWS_MAX * TEXTBOX_CHARS_PER_ROW]; uint8_t TEXTBOX_ROW_COUNT; uint8_t TEXTBOX_ROW_TILE_LAST; uint8_t TEXTBOX_ROW_CURRENT; uint8_t TEXTBOX_STATE; uint8_t TEXTBOX_SCROLL; inline void conversationTextboxInit() { // Reset textbox state TEXTBOX_STATE = 0; } void conversationTextboxSetText(char *text) { uint8_t i, j, k, rowStart, stateFlags; char c, c2; // Begin by filling the textbox text chars with whitespace to begin. // TODO: I'm pretty sure I can move this lower and optimize. for(i = 0; i < TEXTBOX_SCROLL_ROWS_MAX * TEXTBOX_CHARS_PER_ROW; i++) { TEXTBOX_TEXTS[i] = ' '; } // Reset textbox state TEXTBOX_STATE = TEXTBOX_STATE_VISIBLE; TEXTBOX_SCROLL = 0; TEXTBOX_ROW_COUNT = 0; TEXTBOX_ROW_COUNT = 1; // Copy source text to buffer, also determine wordwrapping here. i = 0, j = 0, rowStart = 0, stateFlags = 0; while((c = text[i]) != '\0') {// "For each char in string" if(c == ' ') { // Scan ahead and look at how many chars remain to the next space. k = i + 1; while( (c2 = text[k]) != '\0' && c2 != '\n' && c2 != ' ' && (k - rowStart) < TEXTBOX_CHARS_PER_ROW ) k++; // IF that number is less than the remaining chars on the current row, // then treat this space like a newline. if(k >= (rowStart + TEXTBOX_CHARS_PER_ROW + 1)) { stateFlags |= 1 << 0; } } else if(c == '\n') { stateFlags |= 1 << 0; } // Do we need to insert newline where we are currently? if((stateFlags & (1 << 0)) != 0) { stateFlags &= ~(1 << 0);// Remove newline flag i++; rowStart = i;// Update the row start (Should this be i+1?) //TODO: can I optimize the next line by using rowStart somehow? j = ((j / TEXTBOX_CHARS_PER_ROW)+1) * TEXTBOX_CHARS_PER_ROW;// Update destination character position. TEXTBOX_ROW_COUNT++; continue; } // Insert the character TEXTBOX_TEXTS[j] = c; i++; j++; } // Now we have organized the string nicely we can prep for rendering. Fill the // tiles with blank chars. textboxFillBlank(); WY_REG = TEXTBOX_WINDOW_Y * TILE_HEIGHT; SHOW_WIN; } inline void textboxFillBlank() { uint8_t tiles[TEXTBOX_WIDTH_IN_TILES * TEXTBOX_HEIGHT_IN_TILES]; spriteBorderBufferEdges( tiles, TEXTBOX_WIDTH_IN_TILES, TEXTBOX_HEIGHT_IN_TILES, SPRITE_TILESET_WHITE_HIGH ); spriteBufferWindow( 0x00, TEXTBOX_Y, TEXTBOX_WIDTH_IN_TILES, TEXTBOX_HEIGHT_IN_TILES, tiles ); } inline void conversationTextboxUpdate() { uint8_t i, tile; char c; // Is the textbox visible? if((TEXTBOX_STATE & TEXTBOX_STATE_VISIBLE) == 0) return; // Have we finished scrolling? if(TEXTBOX_STATE & TEXTBOX_STATE_SCROLLED) { // Is the user trying to go to the next line? if(INPUT_PRESSED & J_A) { // First, lets figure out if there's any more text to reveal or not. if((TEXTBOX_ROW_COUNT - TEXTBOX_ROW_CURRENT) < TEXTBOX_TILES_ROWS) { TEXTBOX_STATE &= ~TEXTBOX_STATE_VISIBLE; HIDE_WIN; LCDC_REG |= LCDCF_BG8000; conversationQueueNext(); return; } TEXTBOX_STATE &= ~TEXTBOX_STATE_SCROLLED; TEXTBOX_SCROLL = 0; TEXTBOX_ROW_CURRENT += TEXTBOX_TILES_ROWS; textboxFillBlank(); } return; } // Move to the next character. i = TEXTBOX_ROW_CURRENT * TEXTBOX_CHARS_PER_ROW; c = TEXTBOX_TEXTS[i+TEXTBOX_SCROLL]; if(TEXTBOX_SCROLL == TEXTBOX_CHARS_MAX) { TEXTBOX_STATE |= TEXTBOX_STATE_SCROLLED; } else { tile = spriteFontTileFromChar(c); spriteBufferWindow( 0x01 + (TEXTBOX_SCROLL % TEXTBOX_CHARS_PER_ROW), TEXTBOX_Y + 0x01 + (TEXTBOX_SCROLL / TEXTBOX_CHARS_PER_ROW), 1, 1, &tile ); TEXTBOX_SCROLL++; // Skip spaces while(TEXTBOX_SCROLL < TEXTBOX_CHARS_MAX && TEXTBOX_TEXTS[i+TEXTBOX_SCROLL] == ' ') TEXTBOX_SCROLL++; } }