Vastly improve UI
This commit is contained in:
@ -8,6 +8,17 @@
|
||||
#include "color.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
const color4f_t COLOR4F_COLOR_MAP[COLOR_COUNT] = {
|
||||
COLOR4F_BLACK,
|
||||
COLOR4F_WHITE,
|
||||
COLOR4F(0.8f, 0.0f, 0.0f, 1.0f),
|
||||
COLOR4F(0.0f, 0.5f, 0.0f, 1.0f),
|
||||
COLOR4F(0.0f, 0.0f, 0.8f, 1.0f),
|
||||
COLOR4F(0.8f, 0.8f, 0.0f, 1.0f),
|
||||
COLOR4F(0.8f, 0.0f, 0.8f, 1.0f),
|
||||
COLOR4F(0.0f, 0.8f, 0.8f, 1.0f)
|
||||
};
|
||||
|
||||
void color4fCopy(const color4f_t src, color4f_t dest) {
|
||||
memcpy(dest, src, sizeof(color4f_t));
|
||||
}
|
||||
}
|
@ -8,6 +8,9 @@
|
||||
#pragma once
|
||||
#include "dawn.h"
|
||||
|
||||
typedef float_t color3f_t[3];
|
||||
typedef float_t color4f_t[4];
|
||||
|
||||
// Simple colors, used by the frame system.
|
||||
#define COLOR_BLACK 0x00
|
||||
#define COLOR_WHITE 0x01
|
||||
@ -18,10 +21,7 @@
|
||||
#define COLOR_MAGENTA 0x06
|
||||
#define COLOR_CYAN 0x07
|
||||
|
||||
typedef float_t color3f_t[3];
|
||||
typedef float_t color4f_t[4];
|
||||
|
||||
typedef color4f_t color_t;
|
||||
#define COLOR_COUNT COLOR_CYAN+1
|
||||
|
||||
#define COLOR3F(r,g,b) ((color3f_t){ r, g, b })
|
||||
#define COLOR3F_RED COLOR3F(1, 0, 0)
|
||||
@ -68,6 +68,8 @@ typedef color4f_t color_t;
|
||||
#define COLOR4F_TRANSPARENT_WHITE COLOR4F(1, 1, 1, 0)
|
||||
#define COLOR4F_TRANSPARENT COLOR4F_TRANSPARENT_BLACK
|
||||
|
||||
extern const color4f_t COLOR4F_COLOR_MAP[COLOR_COUNT];
|
||||
|
||||
/**
|
||||
* Copies a color.
|
||||
*
|
||||
|
@ -17,28 +17,7 @@ uint8_t FRAME_COLOR[FRAME_HEIGHT * FRAME_WIDTH];
|
||||
|
||||
void frameInit() {
|
||||
memset(FRAME_BUFFER, ' ', FRAME_HEIGHT * FRAME_WIDTH);
|
||||
|
||||
// Draw UI top and bottom borders
|
||||
for(uint16_t x = 0; x < FRAME_WIDTH; x++) {
|
||||
char_t c = x == 0 || x == FRAME_WIDTH-1 ? '+' : '-';
|
||||
int32_t i = (FRAME_HEIGHT - 1) * FRAME_WIDTH + x;
|
||||
FRAME_BUFFER[i] = c;
|
||||
FRAME_COLOR[i] = COLOR_WHITE;
|
||||
|
||||
i = (FRAME_HEIGHT - FRAME_BOTTOM_UI_HEIGHT) * FRAME_WIDTH + x;
|
||||
FRAME_BUFFER[i] = c;
|
||||
FRAME_COLOR[i] = COLOR_WHITE;
|
||||
}
|
||||
|
||||
for(uint16_t y = FRAME_HEIGHT - FRAME_BOTTOM_UI_HEIGHT + 1; y < FRAME_HEIGHT - 1; y++) {
|
||||
int32_t i = y * FRAME_WIDTH;
|
||||
FRAME_BUFFER[i] = '|';
|
||||
FRAME_COLOR[i] = COLOR_WHITE;
|
||||
|
||||
i = y * FRAME_WIDTH + FRAME_WIDTH - 1;
|
||||
FRAME_BUFFER[i] = '|';
|
||||
FRAME_COLOR[i] = COLOR_WHITE;
|
||||
}
|
||||
frameUIReset();
|
||||
}
|
||||
|
||||
void frameUpdate() {
|
||||
@ -74,21 +53,41 @@ void frameUpdate() {
|
||||
FRAME_BUFFER[i++] = symbolGetCharByTile(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UI Area.
|
||||
if(textboxIsOpen()) {
|
||||
const char_t *text = TEXTBOX.text;
|
||||
for(uint16_t x = 1; x < FRAME_WIDTH - 1; x++) {
|
||||
if(*text == '\0') break;
|
||||
FRAME_BUFFER[(FRAME_HEIGHT - 2) * FRAME_WIDTH + x] = *text++;
|
||||
FRAME_COLOR[(FRAME_HEIGHT - 2) * FRAME_WIDTH + x] = COLOR_WHITE;
|
||||
}
|
||||
FRAME_BUFFER[(FRAME_HEIGHT * FRAME_WIDTH) - 4] = ' ';
|
||||
FRAME_BUFFER[(FRAME_HEIGHT * FRAME_WIDTH) - 3] = '>';
|
||||
FRAME_BUFFER[(FRAME_HEIGHT * FRAME_WIDTH) - 2] = ' ';
|
||||
} else {
|
||||
FRAME_BUFFER[(FRAME_HEIGHT * FRAME_WIDTH) - 4] = '-';
|
||||
FRAME_BUFFER[(FRAME_HEIGHT * FRAME_WIDTH) - 3] = '-';
|
||||
FRAME_BUFFER[(FRAME_HEIGHT * FRAME_WIDTH) - 2] = '-';
|
||||
void frameUIReset() {
|
||||
memset(
|
||||
FRAME_BUFFER + (FRAME_HEIGHT - FRAME_BOTTOM_UI_HEIGHT) * FRAME_WIDTH,
|
||||
' ',
|
||||
FRAME_WIDTH * FRAME_BOTTOM_UI_HEIGHT
|
||||
);
|
||||
|
||||
memset(
|
||||
FRAME_COLOR + (FRAME_HEIGHT - FRAME_BOTTOM_UI_HEIGHT) * FRAME_WIDTH,
|
||||
COLOR_WHITE,
|
||||
FRAME_WIDTH * FRAME_BOTTOM_UI_HEIGHT
|
||||
);
|
||||
|
||||
// Draw UI top and bottom borders
|
||||
for(uint16_t x = 0; x < FRAME_WIDTH; x++) {
|
||||
char_t c = x == 0 || x == FRAME_WIDTH-1 ? '+' : '-';
|
||||
int32_t i = (FRAME_HEIGHT - 1) * FRAME_WIDTH + x;
|
||||
FRAME_BUFFER[i] = c;
|
||||
|
||||
i = (FRAME_HEIGHT - FRAME_BOTTOM_UI_HEIGHT) * FRAME_WIDTH + x;
|
||||
FRAME_BUFFER[i] = c;
|
||||
}
|
||||
|
||||
// Draw UI left and right borders
|
||||
for(
|
||||
uint16_t y = FRAME_HEIGHT - FRAME_BOTTOM_UI_HEIGHT + 1;
|
||||
y < FRAME_HEIGHT - 1;
|
||||
y++
|
||||
) {
|
||||
int32_t i = y * FRAME_WIDTH;
|
||||
FRAME_BUFFER[i] = '|';
|
||||
|
||||
i = y * FRAME_WIDTH + FRAME_WIDTH - 1;
|
||||
FRAME_BUFFER[i] = '|';
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
#define FRAME_WIDTH FRAME_PIXEL_WIDTH/FRAME_CHAR_SIZE
|
||||
#define FRAME_HEIGHT FRAME_PIXEL_HEIGHT/FRAME_CHAR_SIZE
|
||||
|
||||
#define FRAME_BOTTOM_UI_HEIGHT 6
|
||||
#define FRAME_BOTTOM_UI_HEIGHT 8
|
||||
|
||||
extern char_t FRAME_BUFFER[FRAME_HEIGHT * FRAME_WIDTH];
|
||||
extern uint8_t FRAME_COLOR[FRAME_HEIGHT * FRAME_WIDTH];
|
||||
@ -27,4 +27,9 @@ void frameInit();
|
||||
/**
|
||||
* Updates the terminal frame.
|
||||
*/
|
||||
void frameUpdate();
|
||||
void frameUpdate();
|
||||
|
||||
/**
|
||||
* Resets the UI area of the frame.
|
||||
*/
|
||||
void frameUIReset();
|
@ -28,7 +28,6 @@ char_t symbolGetCharByTile(const tile_t tile) {
|
||||
return '#';
|
||||
|
||||
case TILE_ID_WATER:
|
||||
// Take remainder
|
||||
int32_t j = (int32_t)(TIME.time * 2) % 4;
|
||||
switch(j) {
|
||||
case 0: return '/';
|
||||
|
@ -31,6 +31,8 @@ uint8_t gameUpdate(const float_t delta) {
|
||||
timeUpdate(delta);
|
||||
inputUpdate();
|
||||
|
||||
textboxUpdate();
|
||||
|
||||
if(GAME.currentMap) {
|
||||
mapUpdate(GAME.currentMap);
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ void playerUpdate(entity_t *entity) {
|
||||
return entityWalk(entity, ENTITY_DIRECTION_EAST);
|
||||
}
|
||||
|
||||
if(inputIsDown(INPUT_BIND_ACCEPT)) {
|
||||
if(inputWasPressed(INPUT_BIND_ACCEPT)) {
|
||||
// Check what the player is trying to interact with
|
||||
uint16_t x = entity->x, y = entity->y;
|
||||
entityDirectionOffsetAdd(entity->direction, &x, &y);
|
||||
@ -45,7 +45,7 @@ void playerUpdate(entity_t *entity) {
|
||||
|
||||
target->state = ENTITY_STATE_TALKING;
|
||||
entity->state = ENTITY_STATE_TALKING;
|
||||
textboxSetText("Hello Player");
|
||||
textboxSetText("NPC", "Hello Player");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
#include "textbox.h"
|
||||
#include "assert/assert.h"
|
||||
#include "input.h"
|
||||
#include "time.h"
|
||||
|
||||
textbox_t TEXTBOX;
|
||||
|
||||
@ -14,14 +16,92 @@ void textboxInit() {
|
||||
memset(&TEXTBOX, 0, sizeof(textbox_t));
|
||||
}
|
||||
|
||||
void textboxSetText(const char_t *text) {
|
||||
void textboxSetText(
|
||||
const char_t *title,
|
||||
const char_t *text
|
||||
) {
|
||||
assertNotNull(text, "Text cannot be NULL.");
|
||||
|
||||
// Setup text copies
|
||||
size_t len = strlen(text);
|
||||
assertTrue(len < TEXTBOX_TEXT_MAX - 1, "Text is too long.");
|
||||
assertTrue(len < TEXTBOX_TEXT_MAX, "Text is too long.");
|
||||
strcpy(TEXTBOX.text, text);
|
||||
TEXTBOX.textLength = len;
|
||||
|
||||
// Setup title
|
||||
if(title) {
|
||||
len = strlen(title);
|
||||
assertTrue(len < TEXTBOX_TITLE_MAX, "Title is too long.");
|
||||
strcpy(TEXTBOX.title, title);
|
||||
} else {
|
||||
TEXTBOX.title[0] = '\0';
|
||||
}
|
||||
|
||||
// Reset UI.
|
||||
frameUIReset();
|
||||
|
||||
// Add > indicator
|
||||
FRAME_BUFFER[(FRAME_HEIGHT * FRAME_WIDTH) - 4] = ' ';
|
||||
FRAME_BUFFER[(FRAME_HEIGHT * FRAME_WIDTH) - 3] = '>';
|
||||
FRAME_BUFFER[(FRAME_HEIGHT * FRAME_WIDTH) - 2] = ' ';
|
||||
|
||||
// Add title
|
||||
len = strlen(TEXTBOX.title);
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
FRAME_BUFFER[
|
||||
(FRAME_HEIGHT - FRAME_BOTTOM_UI_HEIGHT) * (FRAME_WIDTH) + 2 + i
|
||||
] = TEXTBOX.title[i];
|
||||
}
|
||||
|
||||
// Prepare for scrolling
|
||||
TEXTBOX.nextChar = (1.0f / TEXTBOX_CHARS_PER_SECOND);
|
||||
TEXTBOX.textIndex = 0;
|
||||
TEXTBOX.open = true;
|
||||
}
|
||||
|
||||
bool_t textboxIsOpen() {
|
||||
return TEXTBOX.open;
|
||||
}
|
||||
|
||||
void textboxUpdate() {
|
||||
if(!textboxIsOpen()) return;
|
||||
|
||||
int32_t j;
|
||||
|
||||
// Have we finished scrolling?
|
||||
if(TEXTBOX.textIndex >= TEXTBOX.textLength) {
|
||||
// Wait for input
|
||||
if(inputWasPressed(INPUT_BIND_ACCEPT)) {
|
||||
TEXTBOX.open = false;
|
||||
frameUIReset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Blinking cursor
|
||||
j = (int32_t)(TIME.time * TEXTBOX_BLINKS_PER_SECOND) % 2;
|
||||
if(j == 0) {
|
||||
FRAME_BUFFER[(FRAME_HEIGHT * FRAME_WIDTH) - 3] = '>';
|
||||
FRAME_BUFFER[(FRAME_HEIGHT * FRAME_WIDTH) - 2] = ' ';
|
||||
} else {
|
||||
FRAME_BUFFER[(FRAME_HEIGHT * FRAME_WIDTH) - 3] = ' ';
|
||||
FRAME_BUFFER[(FRAME_HEIGHT * FRAME_WIDTH) - 2] = '>';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Update scrolling
|
||||
TEXTBOX.nextChar -= TIME.delta;
|
||||
if(inputIsDown(INPUT_BIND_ACCEPT)) TEXTBOX.nextChar -= TIME.delta;
|
||||
if(TEXTBOX.nextChar > 0.0f) return;
|
||||
|
||||
// Scroll text
|
||||
uint16_t x = TEXTBOX.textIndex % (FRAME_WIDTH - 2);
|
||||
uint16_t y = TEXTBOX.textIndex / (FRAME_WIDTH - 2);
|
||||
FRAME_BUFFER[
|
||||
(FRAME_HEIGHT - FRAME_BOTTOM_UI_HEIGHT + 1 + y) *
|
||||
(FRAME_WIDTH) + 1 + x
|
||||
] = TEXTBOX.text[TEXTBOX.textIndex];
|
||||
TEXTBOX.textIndex++;
|
||||
TEXTBOX.nextChar = (1.0f / TEXTBOX_CHARS_PER_SECOND);
|
||||
}
|
@ -7,12 +7,21 @@
|
||||
|
||||
#pragma once
|
||||
#include "dawn.h"
|
||||
#include "display/frame.h"
|
||||
|
||||
#define TEXTBOX_TEXT_MAX 256
|
||||
#define TEXTBOX_TITLE_MAX 16
|
||||
#define TEXTBOX_TEXT_MAX ((FRAME_BOTTOM_UI_HEIGHT-2) * (FRAME_WIDTH - 2))
|
||||
#define TEXTBOX_CHARS_PER_SECOND 15
|
||||
#define TEXTBOX_BLINKS_PER_SECOND 2
|
||||
|
||||
typedef struct {
|
||||
char_t text[TEXTBOX_TEXT_MAX];
|
||||
char_t title[TEXTBOX_TITLE_MAX+1];
|
||||
char_t text[TEXTBOX_TEXT_MAX+1];
|
||||
bool_t open;
|
||||
|
||||
size_t textIndex;
|
||||
size_t textLength;
|
||||
float_t nextChar;
|
||||
} textbox_t;
|
||||
|
||||
extern textbox_t TEXTBOX;
|
||||
@ -25,13 +34,22 @@ void textboxInit();
|
||||
/**
|
||||
* Sets the text of the textbox, and opens it.
|
||||
*
|
||||
* @param title Title to set.
|
||||
* @param text Text to set.
|
||||
*/
|
||||
void textboxSetText(const char_t *text);
|
||||
void textboxSetText(
|
||||
const char_t *title,
|
||||
const char_t *text
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns whether the textbox is open.
|
||||
*
|
||||
* @return Whether the textbox is open.
|
||||
*/
|
||||
bool_t textboxIsOpen();
|
||||
bool_t textboxIsOpen();
|
||||
|
||||
/**
|
||||
* Updates the textbox.
|
||||
*/
|
||||
void textboxUpdate();
|
@ -37,42 +37,12 @@ void displayUpdate() {
|
||||
vec4_t uvs;
|
||||
vec4_t positions;
|
||||
uint8_t color;
|
||||
color4f_t glColor;
|
||||
for(uint32_t x = 0; x < FRAME_WIDTH; x++) {
|
||||
for(uint32_t y = 0; y < FRAME_HEIGHT; y++) {
|
||||
char_t c = FRAME_BUFFER[y * FRAME_WIDTH + x];
|
||||
if(c == '\0' || c == ' ') continue;
|
||||
|
||||
color = FRAME_COLOR[y * FRAME_WIDTH + x];
|
||||
switch(color) {
|
||||
case COLOR_BLACK:
|
||||
color4fCopy(COLOR4F_BLACK, glColor);
|
||||
break;
|
||||
case COLOR_RED:
|
||||
color4fCopy(COLOR4F_RED, glColor);
|
||||
break;
|
||||
case COLOR_GREEN:
|
||||
color4fCopy(COLOR4F_GREEN, glColor);
|
||||
break;
|
||||
case COLOR_BLUE:
|
||||
color4fCopy(COLOR4F_BLUE, glColor);
|
||||
break;
|
||||
case COLOR_YELLOW:
|
||||
color4fCopy(COLOR4F_YELLOW, glColor);
|
||||
break;
|
||||
case COLOR_MAGENTA:
|
||||
color4fCopy(COLOR4F_MAGENTA, glColor);
|
||||
break;
|
||||
case COLOR_CYAN:
|
||||
color4fCopy(COLOR4F_CYAN, glColor);
|
||||
break;
|
||||
case COLOR_WHITE:
|
||||
color4fCopy(COLOR4F_WHITE, glColor);
|
||||
break;
|
||||
default:
|
||||
assertUnreachable("Unknown color: %d", color);
|
||||
break;
|
||||
}
|
||||
fontCharGetUV(c, uvs);
|
||||
|
||||
positions[0] = x * 8;
|
||||
@ -86,7 +56,7 @@ void displayUpdate() {
|
||||
(vec2_t){positions[2], positions[3]},
|
||||
(vec2_t){uvs[0], uvs[1]},
|
||||
(vec2_t){uvs[2], uvs[3]},
|
||||
glColor,
|
||||
COLOR4F_COLOR_MAP[color],
|
||||
quadIndex * PRIMITIVE_QUAD_VERTICE_COUNT,
|
||||
quadIndex * PRIMITIVE_QUAD_INDICE_COUNT
|
||||
);
|
||||
|
Reference in New Issue
Block a user