#include #include "graphic.hpp" #include "coordinates.hpp" #include "font.hpp" #include "start_size.hpp" #include "control.hpp" #include "number.hpp" #include "pokemon_instance.hpp" struct dialog_border { static constexpr uint8_t corner_top_left = 105; static constexpr uint8_t corner_top_right = 106; static constexpr uint8_t corner_bottom_left = 107; static constexpr uint8_t corner_bottom_right = 108; static constexpr uint8_t vertical_end_cap = 109; static constexpr uint8_t vertical = 110; static constexpr uint8_t horizontal = 111; }; struct battle_border { static constexpr uint8_t horizontal = 96; static constexpr uint8_t corner_bottom_right = 97; static constexpr uint8_t arrow_right = 98; static constexpr uint8_t vertical = 99; static constexpr uint8_t corner_bottom_left = 100; static constexpr uint8_t three_dots = 101; static constexpr uint8_t vertical_end_cap = 102; static constexpr uint8_t level = 103; static constexpr uint8_t arrow_left = 104; }; struct hp_bar { static constexpr uint8_t hp = 0x50; static constexpr uint8_t start_cap = 0x51; static constexpr uint8_t seg0 = 0x52; static constexpr uint8_t seg1 = 0x53; static constexpr uint8_t seg2 = 0x54; static constexpr uint8_t seg3 = 0x55; static constexpr uint8_t seg4 = 0x56; static constexpr uint8_t seg5 = 0x57; static constexpr uint8_t seg6 = 0x58; static constexpr uint8_t seg7 = 0x59; static constexpr uint8_t seg8 = 0x5a; static constexpr uint8_t end_cap_nobar = 0x5b; static constexpr uint8_t end_cap_bar = 0x66; }; struct stats { static constexpr uint8_t id = 0x70; // ID static constexpr uint8_t no = 0x71; // No static constexpr uint8_t no_dot = 0x4e; // . }; #define S reinterpret_cast 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; } } } #undef S 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); } 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 #define S reinterpret_cast draw_text(base_pattern, S("STATUS/___"), 9, 6); } // bottom right border { draw_battle_border(base_pattern, {19, 9}, {12, 17}); for (int32_t ix = 0; ix < 2; ix++) { draw_text(base_pattern, S("TYPE"), 10, 9 + (2 * ix)); put_char(base_pattern, 15, 9 + (2 * ix), ascii_to_font('1' + ix)); put_char(base_pattern, 15, 9 + (2 * ix), ascii_to_font('/')); } } { // 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); #undef S 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(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++; } }