/** * Copyright (c) 2022 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "textbox.h" char *TEXTBOX_TEXT; uint8_t TEXTBOX_TEXT_LENGTH; uint8_t TEXTBOX_STATE; uint8_t TEXTBOX_SCROLL; uint8_t TEXTBOX_CHAR_POSITION; inline void conversationTextboxInit() { uint8_t i; uint8_t TEXTBOX_TILES[TEXTBOX_TILES_MAX]; // Reset textbox state TEXTBOX_TEXT = NULL; TEXTBOX_STATE = 0; TEXTBOX_TEXT_LENGTH = 0; TEXTBOX_SCROLL = 0; // Setup window data move_win(7, SCREENHEIGHT - (TEXTBOX_HEIGHT_IN_TILES * 8)); set_win_data(FONT_DATA_POSITION, FONT_IMAGE_TILES, FONT_IMAGE); set_win_data(BORDER_DATA_POSITION, BORDER_IMAGE_TILES, BORDER_IMAGE); // Corners TEXTBOX_TILES[0] = BORDER_TILE_TOP_LEFT; TEXTBOX_TILES[TEXTBOX_WIDTH_IN_TILES-1] = BORDER_TILE_TOP_RIGHT; TEXTBOX_TILES[TEXTBOX_TILES_MAX-1] = BORDER_TILE_BOTTOM_RIGHT; TEXTBOX_TILES[TEXTBOX_TILES_MAX-TEXTBOX_WIDTH_IN_TILES] = BORDER_TILE_BOTTOM_LEFT; // Edges for(i = 1; i < TEXTBOX_WIDTH_IN_TILES - 1; i++) { TEXTBOX_TILES[i] = BORDER_TILE_TOP_CENTER; TEXTBOX_TILES[TEXTBOX_TILES_MAX - 1 - i] = BORDER_TILE_BOTTOM_CENTER; } for(i = 1; i < TEXTBOX_HEIGHT_IN_TILES - 1; i++) { TEXTBOX_TILES[TEXTBOX_WIDTH_IN_TILES * i] = BORDER_TILE_CENTER_LEFT; TEXTBOX_TILES[TEXTBOX_WIDTH_IN_TILES * (i+1) - 1] = BORDER_TILE_CENTER_RIGHT; } // Setup tiles. set_win_tiles( 0, 0, TEXTBOX_WIDTH_IN_TILES, TEXTBOX_HEIGHT_IN_TILES, TEXTBOX_TILES ); } void conversationTextboxSetText(char *text, uint8_t length) { uint8_t i, j; uint8_t TEXTBOX_TILES[TEXTBOX_CHAR_ROWS * TEXTBOX_CHARS_PER_ROW]; // Reset textbox state TEXTBOX_TEXT = text; TEXTBOX_TEXT_LENGTH = length; TEXTBOX_STATE = TEXTBOX_STATE_VISIBLE; TEXTBOX_SCROLL = 0; TEXTBOX_CHAR_POSITION = 0; // Fill blank characters for(j = 0; j < TEXTBOX_CHAR_ROWS; j++) { for(i = 0; i < TEXTBOX_CHARS_PER_ROW ; i++) { TEXTBOX_TILES[i + (j * TEXTBOX_CHARS_PER_ROW)] = TEXTBOX_TILE_BLANK; } } set_win_tiles( 1, 1, TEXTBOX_WIDTH_IN_TILES - 0x02, TEXTBOX_HEIGHT_IN_TILES - 0x02, TEXTBOX_TILES ); // Show the window layer. SHOW_WIN; } inline void conversationTextboxUpdate() { uint8_t i; // Is the textbox visible? if(!(TEXTBOX_STATE & TEXTBOX_STATE_VISIBLE)) return; // Has the textbox finished scrolling? if(TEXTBOX_STATE & TEXTBOX_STATE_SCROLLED) { // Is the player attempting to close the textbox? if(INPUT_STATE & J_A) { TEXTBOX_STATE &= ~TEXTBOX_STATE_VISIBLE; HIDE_WIN; conversationQueueNext(); } return; } // Move to the next character. if(TEXTBOX_TEXT[TEXTBOX_SCROLL] == ' ') { // Whitespace, do nothing. TEXTBOX_CHAR_POSITION++; } else if(TEXTBOX_TEXT[TEXTBOX_SCROLL] == '\n') { // Newline character, move the next tile to the next row. TEXTBOX_CHAR_POSITION = ( (TEXTBOX_CHAR_POSITION / TEXTBOX_CHARS_PER_ROW) + 1 ) * TEXTBOX_CHARS_PER_ROW; } else { // Reveal the next character. TODO: I'll optimize this. set_win_tiles( 1 + (TEXTBOX_CHAR_POSITION % TEXTBOX_CHARS_PER_ROW), 1 + (TEXTBOX_CHAR_POSITION / TEXTBOX_CHARS_PER_ROW), 1, 1, TEXTBOX_TEXT + TEXTBOX_SCROLL ); TEXTBOX_CHAR_POSITION++; } TEXTBOX_SCROLL++; // Update state. TODO: I actually don't really need this state, it's just here // incase I want to check if the state has scrolled later on? Doubt it though. if(TEXTBOX_SCROLL == TEXTBOX_TEXT_LENGTH) { TEXTBOX_STATE |= TEXTBOX_STATE_SCROLLED; } }