298 lines
8.7 KiB
C++
298 lines
8.7 KiB
C++
#include <cstdint>
|
|
|
|
#include "graphic.hpp"
|
|
#include "coordinates.hpp"
|
|
#include "font.hpp"
|
|
#include "start_size.hpp"
|
|
#include "control.hpp"
|
|
#include "number.hpp"
|
|
|
|
#include "gen/pokemon/types.hpp"
|
|
|
|
#include "pokemon_instance.hpp"
|
|
#include "ailment.hpp"
|
|
|
|
#define S reinterpret_cast<const uint8_t *>
|
|
|
|
void draw_text(const uint32_t base_pattern,
|
|
const uint8_t * text,
|
|
const int32_t x, const int32_t y)
|
|
{
|
|
uint8_t i = 0, x_o = 0;
|
|
uint8_t c;
|
|
|
|
auto draw_ligature = [&](const uint8_t * l_text)
|
|
{
|
|
uint8_t j = 0;
|
|
uint8_t d;
|
|
while ((d = l_text[j++]) != 0)
|
|
put_char(base_pattern, x + x_o++, y, ascii_to_font(d));
|
|
};
|
|
while ((c = text[i++]) != 0) {
|
|
switch (c) {
|
|
case ligatures_t::pkmn:
|
|
{
|
|
static const uint8_t * pkmn = S("\xb2\xb3");
|
|
draw_ligature(pkmn);
|
|
}
|
|
break;
|
|
case ligatures_t::poke:
|
|
{
|
|
static const uint8_t * poke = S("POK\xe9");
|
|
draw_ligature(poke);
|
|
}
|
|
break;
|
|
case ligatures_t::trainer:
|
|
{
|
|
static const uint8_t * trainer = S("TRAINER");
|
|
draw_ligature(trainer);
|
|
}
|
|
break;
|
|
default:
|
|
put_char(base_pattern, x + x_o++, y, ascii_to_font(c));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void draw_box_border(const uint32_t base_pattern,
|
|
const screen_cell_t& top_left, const screen_cell_t& bottom_right)
|
|
{
|
|
// place corners
|
|
put_char(base_pattern, top_left.x, top_left.y, dialog_border::corner_top_left);
|
|
put_char(base_pattern, bottom_right.x, top_left.y, dialog_border::corner_top_right);
|
|
put_char(base_pattern, bottom_right.x, bottom_right.y, dialog_border::corner_bottom_right);
|
|
put_char(base_pattern, top_left.x, bottom_right.y, dialog_border::corner_bottom_left);
|
|
|
|
for (int32_t y = top_left.y + 1; y < bottom_right.y; y++) {
|
|
// left vertical bar
|
|
put_char(base_pattern, top_left.x, y, dialog_border::vertical);
|
|
// right vertical bar
|
|
put_char(base_pattern, bottom_right.x, y, dialog_border::vertical);
|
|
}
|
|
|
|
|
|
for (int32_t x = top_left.x + 1; x < bottom_right.x; x++) {
|
|
// top horizontal bar
|
|
put_char(base_pattern, x, top_left.y, dialog_border::horizontal);
|
|
|
|
// bottom horizontal bar
|
|
put_char(base_pattern, x, bottom_right.y, dialog_border::horizontal);
|
|
}
|
|
}
|
|
|
|
void draw_box_background(const uint32_t base_pattern,
|
|
const screen_cell_t& top_left, const screen_cell_t& bottom_right)
|
|
{
|
|
for (int32_t x = top_left.x + 1; x < bottom_right.x; x++) {
|
|
for (int32_t y = top_left.y + 1; y < bottom_right.y; y++) {
|
|
put_char(base_pattern, x, y, ascii_to_font(' '));
|
|
}
|
|
}
|
|
}
|
|
|
|
void draw_battle_border(const uint32_t base_pattern,
|
|
const screen_cell_t& top_corner, const screen_cell_t& bottom_corner)
|
|
{
|
|
// draw the vertical line
|
|
for (int32_t y = top_corner.y; y < bottom_corner.y; y++)
|
|
put_char(base_pattern, top_corner.x, y, battle_border::vertical);
|
|
|
|
// decide which direction we are drawing
|
|
const int32_t increment = bottom_corner.x <= top_corner.x ? 1 : -1;
|
|
|
|
// draw the horizontal line
|
|
for (int32_t x = bottom_corner.x + increment; x != top_corner.x; x += increment)
|
|
put_char(base_pattern, x, bottom_corner.y, battle_border::horizontal);
|
|
|
|
// draw the end cap and corner piece
|
|
const uint8_t end_cap = increment == 1 ? battle_border::arrow_left
|
|
: battle_border::arrow_right;
|
|
const uint8_t corner = increment == 1 ? battle_border::corner_bottom_right
|
|
: battle_border::corner_bottom_left;
|
|
put_char(base_pattern, bottom_corner.x, bottom_corner.y, end_cap);
|
|
put_char(base_pattern, top_corner.x , bottom_corner.y, corner );
|
|
}
|
|
|
|
constexpr inline uint8_t hp_seg(const int32_t hp_48, const int32_t ix)
|
|
{
|
|
const int32_t hp_ix = hp_48 - (ix * 8);
|
|
|
|
if (hp_ix < 0) return hp_bar::seg0;
|
|
|
|
switch (hp_ix) {
|
|
default: [[fallthrough]];
|
|
case 8: return hp_bar::seg8;
|
|
case 7: return hp_bar::seg7;
|
|
case 6: return hp_bar::seg6;
|
|
case 5: return hp_bar::seg5;
|
|
case 4: return hp_bar::seg4;
|
|
case 3: return hp_bar::seg3;
|
|
case 2: return hp_bar::seg2;
|
|
case 1: return hp_bar::seg1;
|
|
case 0: return hp_bar::seg0;
|
|
}
|
|
}
|
|
|
|
void draw_hp_bar(const uint32_t base_pattern,
|
|
const screen_cell_t& top_left,
|
|
const uint8_t end_cap,
|
|
const int32_t hp_48)
|
|
{
|
|
put_char(base_pattern, top_left.x + 0, top_left.y, hp_bar::hp);
|
|
put_char(base_pattern, top_left.x + 1, top_left.y, hp_bar::start_cap);
|
|
|
|
for (int32_t ix = 0; ix < 6; ix++) {
|
|
const uint8_t seg = hp_seg(hp_48, ix);
|
|
put_char(base_pattern, top_left.x + 2 + ix, top_left.y, seg);
|
|
}
|
|
|
|
put_char(base_pattern, top_left.x + 8, top_left.y, end_cap);
|
|
}
|
|
|
|
const uint8_t * status_string(const pokemon_instance_t& pokemon_instance)
|
|
{
|
|
if (pokemon_instance.current_hit_points == 0)
|
|
return S("FNT");
|
|
else
|
|
return ailments[pokemon_instance.ailment].name;
|
|
}
|
|
|
|
void draw_stats1(const uint32_t base_pattern,
|
|
const pokemon_instance_t& pokemon_instance)
|
|
{
|
|
// white out the entire screen
|
|
draw_box_background(base_pattern, {-1, -1}, {20, 18});
|
|
|
|
// top status battle box
|
|
{
|
|
draw_battle_border(base_pattern, {19, 1}, {8, 7});
|
|
|
|
// hp
|
|
{
|
|
const int32_t hp_48 = (pokemon_instance.current_hit_points * 48) / pokemon_instance.stat_values.hit_points;
|
|
draw_hp_bar(base_pattern, {11, 3}, hp_bar::end_cap_bar, hp_48);
|
|
|
|
draw_number_right_align(base_pattern, {12, 4},
|
|
pokemon_instance.current_hit_points,
|
|
3, ascii_to_font(' ')); // width, fill
|
|
|
|
put_char(base_pattern, 15, 4, ascii_to_font('/'));
|
|
|
|
draw_number_right_align(base_pattern, {16, 4},
|
|
pokemon_instance.stat_values.hit_points,
|
|
3, ascii_to_font(' ')); // width, fill
|
|
}
|
|
|
|
// name
|
|
draw_text(base_pattern, pokemon[pokemon_instance.species].name, 9, 1);
|
|
|
|
// level
|
|
put_char(base_pattern, 14, 2, battle_border::level);
|
|
draw_number_left_align(base_pattern, {15, 2},
|
|
pokemon_instance.level,
|
|
3); // width
|
|
|
|
draw_text(base_pattern, S("STATUS/"), 9, 6);
|
|
draw_text(base_pattern, status_string(pokemon_instance), 16, 6);
|
|
}
|
|
|
|
// bottom right border
|
|
{
|
|
draw_battle_border(base_pattern, {19, 9}, {12, 17});
|
|
|
|
int32_t distinct_types = (pokemon[pokemon_instance.species].types[0]
|
|
!= pokemon[pokemon_instance.species].types[1]) + 1;
|
|
|
|
for (int32_t ix = 0; ix < distinct_types; ix++) {
|
|
const enum type_t::type type = pokemon[pokemon_instance.species].types[ix];
|
|
draw_text(base_pattern, S("TYPE"), 10, 9 + (2 * ix));
|
|
put_char(base_pattern, 14, 9 + (2 * ix), ascii_to_font('1' + ix));
|
|
put_char(base_pattern, 15, 9 + (2 * ix), ascii_to_font('/'));
|
|
draw_text(base_pattern, types[type].name, 11, 10 + (2 * ix));
|
|
}
|
|
}
|
|
|
|
{ // bottom left dialog box
|
|
draw_box_border(base_pattern, {0, 8}, {9, 17});
|
|
|
|
draw_text(base_pattern, S("ATTACK"), 1, 9 + 0);
|
|
draw_text(base_pattern, S("DEFENSE"), 1, 9 + 2);
|
|
draw_text(base_pattern, S("SPEED"), 1, 9 + 4);
|
|
draw_text(base_pattern, S("SPECIAL"), 1, 9 + 6);
|
|
|
|
draw_number_right_align(base_pattern, {6, 10 + 0},
|
|
pokemon_instance.stat_values.attack,
|
|
3, ascii_to_font(' ')); // width, fill
|
|
|
|
draw_number_right_align(base_pattern, {6, 10 + 2},
|
|
pokemon_instance.stat_values.defense,
|
|
3, ascii_to_font(' ')); // width, fill
|
|
|
|
draw_number_right_align(base_pattern, {6, 10 + 4},
|
|
pokemon_instance.stat_values.speed,
|
|
3, ascii_to_font(' ')); // width, fill
|
|
|
|
draw_number_right_align(base_pattern, {6, 10 + 6},
|
|
pokemon_instance.stat_values.special,
|
|
3, ascii_to_font(' ')); // width, fill
|
|
} // end bottom left dialog box
|
|
|
|
// pokedex number
|
|
{
|
|
put_char(base_pattern, 1, 7, stats::no);
|
|
put_char(base_pattern, 2, 7, stats::no_dot);
|
|
|
|
const int32_t pokedex_number = static_cast<int32_t>(pokemon_instance.species) + 1;
|
|
draw_number_right_align(base_pattern,
|
|
{3, 7},
|
|
pokedex_number,
|
|
3, // width
|
|
ascii_to_font('0') // fill
|
|
);
|
|
}
|
|
}
|
|
|
|
void dialog_t::draw(const uint32_t base_pattern,
|
|
const start_size_t& text)
|
|
{
|
|
draw_box_border(base_pattern, top_left, bottom_right);
|
|
draw_box_background(base_pattern, top_left, bottom_right);
|
|
|
|
uint32_t ix = 0;
|
|
int32_t x = top_left.x + 1;
|
|
int32_t y = top_left.y + 2;
|
|
// ignore C-string null terminator
|
|
while (ix < (text.size - 1)) {
|
|
const uint8_t c = text.start[ix];
|
|
switch (c) {
|
|
case control_t::text: break;
|
|
case control_t::done: break;
|
|
case control_t::prompt: break;
|
|
case control_t::page: break;
|
|
|
|
case control_t::next: [[fallthrough]];
|
|
case control_t::line:
|
|
while (x < bottom_right.x) {
|
|
put_char(base_pattern, x, y, ascii_to_font(' '));
|
|
x++;
|
|
}
|
|
x = top_left.x + 1;
|
|
y += 2;
|
|
break;
|
|
case control_t::para: [[fallthrough]];
|
|
case control_t::cont:
|
|
// fixme:
|
|
ix = text.size;
|
|
break;
|
|
default:
|
|
put_char(base_pattern, x, y, ascii_to_font(c));
|
|
x += 1;
|
|
break;
|
|
}
|
|
ix++;
|
|
}
|
|
}
|
|
|
|
#undef S
|