draw stats page 2

This commit is contained in:
Zack Buhman 2023-08-04 18:45:50 +00:00
parent c71a7b7b59
commit 3fffdab78d
11 changed files with 375 additions and 172 deletions

View File

@ -39,6 +39,7 @@ SRC += menu.cpp
SRC += number.cpp
SRC += pokemon_instance.cpp
SRC += ailment.cpp
SRC += menu/stats.cpp
DEP = $(patsubst %.cpp,%.cpp.d,$(SRC))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -7,11 +7,6 @@
#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,
@ -134,7 +129,7 @@ constexpr inline uint8_t hp_seg(const int32_t hp_48, const int32_t ix)
}
}
void draw_hp_bar(const uint32_t base_pattern,
static inline 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)
@ -150,148 +145,22 @@ void draw_hp_bar(const uint32_t base_pattern,
put_char(base_pattern, top_left.x + 8, top_left.y, end_cap);
}
const uint8_t * status_string(const pokemon_instance_t& pokemon_instance)
void draw_hp_bar_with_numbers(const uint32_t base_pattern,
const pokemon_instance_t& pokemon_instance,
const screen_cell_t& pos)
{
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_hp_bar(base_pattern, pos, hp_bar::end_cap_bar, hp_48);
draw_number_right_align(base_pattern, {12, 4},
draw_number_right_align(base_pattern, {pos.x + 1, pos.y + 1},
pokemon_instance.current_hit_points,
3, ascii_to_font(' ')); // width, fill
put_char(base_pattern, 15, 4, ascii_to_font('/'));
put_char(base_pattern, pos.x + 4, pos.y + 1, ascii_to_font('/'));
draw_number_right_align(base_pattern, {16, 4},
draw_number_right_align(base_pattern, {pos.x + 5, pos.y + 1},
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

View File

@ -1,5 +1,7 @@
#pragma once
#include <cstdint>
#include "coordinates.hpp"
#include "start_size.hpp"
#include "render_map.hpp" // for cell_offset
@ -54,6 +56,8 @@ struct hp_bar {
struct stats {
static constexpr uint8_t id = 0x70; // ID
static constexpr uint8_t no = 0x71; // No
static constexpr uint8_t to = 0x4c; // to
static constexpr uint8_t p = 0x4d; // p (bold)
static constexpr uint8_t no_dot = 0x4e; // .
};
@ -79,14 +83,6 @@ void draw_box_background(const uint32_t base_pattern,
void draw_battle_border(const uint32_t base_pattern,
const screen_cell_t& top_corner, const screen_cell_t& bottom_corner);
void draw_stats1(const uint32_t base_pattern,
const pokemon_instance_t& pokemon_instance);
struct dialog_t
{
static constexpr screen_cell_t top_left = { 0, 12};
static constexpr screen_cell_t bottom_right = {19, 17};
static void draw(const uint32_t base_pattern,
const start_size_t& text);
};
void draw_hp_bar_with_numbers(const uint32_t base_pattern,
const pokemon_instance_t& pokemon_instance,
const screen_cell_t& pos);

View File

@ -29,6 +29,8 @@
#include "pokemon.hpp"
#include "pokemon_instance.hpp"
#include "menu/stats.hpp"
static int32_t pokemon_raw_index = 0;
struct draw_t {
@ -166,7 +168,7 @@ uint32_t pokemon_sprite_dimension(const uint32_t size)
}
void render_pokemon(const uint32_t ix, const enum pokemon_t::pokemon pokemon_id,
const screen_cell_t& screen_cell)
const screen_cell_t& screen_cell, bool horizontal_inversion)
{
const uint32_t base_pattern = state.draw.base_pattern.pokemon[pokemon_id].front;
const uint32_t character_address = (base_pattern * 16) / 8;
@ -181,7 +183,9 @@ void render_pokemon(const uint32_t ix, const enum pokemon_t::pokemon pokemon_id,
case 56: x_offset = 1; y_offset = 0; break;
}
vdp1.vram.cmd[ix].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__NORMAL_SPRITE;
uint16_t dir = horizontal_inversion ? CTRL__DIR__INVERTED_HORIZONTALLY : 0;
vdp1.vram.cmd[ix].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__NORMAL_SPRITE | dir;
vdp1.vram.cmd[ix].LINK = 0;
// The "end code" is 0xf, which is being used in the mai sprite palette. If
// both transparency and end codes are enabled, it seems there are only 14
@ -209,7 +213,8 @@ void render_sprites(const offset_t& offset)
ix++;
render_pokemon(ix, static_cast<enum pokemon_t::pokemon>(pokemon_raw_index),
{0, 0});
{0, 0},
true); // invert horizontally
ix++;
const object_t& obj = map_objects[state.map];
@ -470,11 +475,13 @@ void check_sign()
const bool position_match = event.position.x == coord.x && event.position.y == coord.y;
if (position_match && event.sign_id != 0xff) {
const start_size_t& text = map.text_pointers[event.sign_id];
dialog_t::draw(state.draw.base_pattern.font, text);
}
}
}
static uint32_t stats_page = 1;
static uint8_t level = 70;
void update()
{
state.player.tick();
@ -486,14 +493,18 @@ void update()
else if (event::cursor_right()) collision_move(maps[state.map], actor_t::right);
else if (event::cursor_up() ) collision_move(maps[state.map], actor_t::up);
else if (event::cursor_down() ) collision_move(maps[state.map], actor_t::down);
else if (event::button_a() ) check_sign();
*/
if (event::cursor_left() ) pokemon_raw_index = (pokemon_raw_index - 1);
else if (event::cursor_right()) pokemon_raw_index = (pokemon_raw_index + 1);
else if (event::cursor_up() ) level++;
else if (event::cursor_down() ) level--;
else if (event::button_a() ) stats_page = !stats_page;
if (pokemon_raw_index < 0) pokemon_raw_index = pokemon_t::count - 1;
else if (pokemon_raw_index >= pokemon_t::count) pokemon_raw_index = 0;
//else if (event::button_a() ) check_sign();
}
static uint32_t frame = 0;
@ -523,14 +534,23 @@ void render()
pokemon_instance.species = static_cast<enum pokemon_t::pokemon>(pokemon_raw_index);
pokemon_instance.stat_experience = {0, 0, 0, 0, 0};
pokemon_instance.determinant_values.dvs = 0b1110'0101'1000'0110;
pokemon_instance.level = 70;
pokemon_instance.level = level;
pokemon_instance.determine_stats();
pokemon_instance.learn_all_moves();
if (frame % 4 == 0)
pokemon_instance.current_hit_points++;
if (pokemon_instance.current_hit_points > pokemon_instance.stat_values.hit_points)
pokemon_instance.current_hit_points = 0;
switch (stats_page) {
default:
case 0:
draw_stats1(state.draw.base_pattern.font, pokemon_instance);
break;
case 1:
draw_stats2(state.draw.base_pattern.font, pokemon_instance);
break;
}
}
extern "C"

43
menu/dialog.cpp Normal file
View File

@ -0,0 +1,43 @@
void draw(const uint32_t base_pattern,
const start_size_t& text)
{
static constexpr screen_cell_t top_left = { 0, 12};
static constexpr screen_cell_t bottom_right = {19, 17};
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++;
}
}

229
menu/stats.cpp Normal file
View File

@ -0,0 +1,229 @@
#include "../font.hpp"
#include "../number.hpp"
#include "../graphic.hpp"
#include "../gen/pokemon/types.hpp"
#include "../gen/pokemon/moves.hpp"
#include "../pokemon_instance.hpp"
#include "../ailment.hpp"
#define S reinterpret_cast<const uint8_t *>
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;
}
static void draw_stats_top_right_border_with_name(const uint32_t base_pattern,
const pokemon_instance_t& pokemon_instance)
{
draw_battle_border(base_pattern, {19, 1}, {8, 7});
// name
draw_text(base_pattern, pokemon[pokemon_instance.species].name, 9, 1);
}
static void draw_stats_pokedex_number(const uint32_t base_pattern,
const pokemon_instance_t& pokemon_instance)
{
// 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 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});
// front picture label
{
draw_stats_pokedex_number(base_pattern, pokemon_instance);
}
// top status battle box
{
// name and border
draw_stats_top_right_border_with_name(base_pattern, pokemon_instance);
// level
put_char(base_pattern, 14, 2, battle_border::level);
draw_number_left_align(base_pattern, {15, 2},
pokemon_instance.level,
3); // width
// hp
draw_hp_bar_with_numbers(base_pattern, pokemon_instance, {11, 3});
// status
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
}
void draw_stats_move(const uint32_t base_pattern,
const pokemon_instance_t& pokemon_instance,
const int32_t _ix)
{
const int32_t ix = _ix & 0b11;
const int32_t y_offset = 2 * ix;
constexpr int32_t name_x = 2;
constexpr int32_t name_y = 9;
constexpr int32_t pp_x = 11;
constexpr int32_t pp_y = 10;
// move name
const move_instance_t& move_instance = pokemon_instance.move_instances[ix];
if (move_instance.type != move_t::no_move) {
const move_t& move = moves[move_instance.type];
// name
draw_text(base_pattern, move.name, name_x, name_y + y_offset);
// pp
put_char(base_pattern, pp_x + 0, pp_y + y_offset, stats::p);
put_char(base_pattern, pp_x + 1, pp_y + y_offset, stats::p);
draw_number_right_align(base_pattern, {pp_x + 3, pp_y + y_offset},
move_instance.pp,
2, ascii_to_font(' ')); // width, fill
put_char(base_pattern, pp_x + 5, pp_y + y_offset, ascii_to_font('/'));
draw_number_right_align(base_pattern, {pp_x + 6, pp_y + y_offset},
moves[move_instance.type].pp,
2, ascii_to_font(' ')); // width, fill
} else {
// name
put_char(base_pattern, name_x, name_y + y_offset, ascii_to_font('-'));
// pp
put_char(base_pattern, pp_x + 0, pp_y + y_offset, ascii_to_font('-'));
put_char(base_pattern, pp_x + 1, pp_y + y_offset, ascii_to_font('-'));
}
}
void draw_stats2(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});
// front picture label
{
draw_stats_pokedex_number(base_pattern, pokemon_instance);
}
// top status battle box
{
// name and border
draw_stats_top_right_border_with_name(base_pattern, pokemon_instance);
// exp points
draw_text(base_pattern, S("EXP POINTS"), 9, 3);
draw_number_right_align(base_pattern,
{13, 4},
pokemon_instance.total_experience,
6, // width
ascii_to_font(' ') // fill
);
// level up
draw_text(base_pattern, S("LEVEL UP"), 9, 5);
put_char(base_pattern, 14, 6, stats::to);
put_char(base_pattern, 16, 6, battle_border::level);
const int32_t next_level = pokemon_instance.level < 100
? (pokemon_instance.level + 1)
: 100;
int32_t exp_to = 1234;
if (next_level < 100) {
draw_number_left_align(base_pattern,
{17, 6},
next_level,
2 // width
);
} else {
draw_number_left_align(base_pattern,
{16, 6},
next_level,
3 // width
);
}
// exp
draw_number_right_align(base_pattern,
{9, 6},
exp_to,
5, // width
ascii_to_font(' ') // fill
);
}
// bottom move list
{
draw_box_border(base_pattern, {0, 8}, {19, 17});
draw_stats_move(base_pattern, pokemon_instance, 0);
draw_stats_move(base_pattern, pokemon_instance, 1);
draw_stats_move(base_pattern, pokemon_instance, 2);
draw_stats_move(base_pattern, pokemon_instance, 3);
}
}
#undef S

11
menu/stats.hpp Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include <cstdint>
#include "../pokemon_instance.hpp"
void draw_stats1(const uint32_t base_pattern,
const pokemon_instance_t& pokemon_instance);
void draw_stats2(const uint32_t base_pattern,
const pokemon_instance_t& pokemon_instance);

View File

@ -59,13 +59,41 @@ pokemon_instance_t::learn_move(enum move_t::move move, int32_t index)
case 1: [[fallthrough]];
case 2: [[fallthrough]];
case 3:
moves[index] = move;
move_instances[index].type = move;
move_instances[index].pp = moves[move].pp;
break;
default:
moves[0] = moves[1];
moves[1] = moves[2];
moves[2] = moves[3];
moves[3] = move;
{
int32_t ix = 0;
while (move_instances[ix].type != move_t::no_move) {
if (move_instances[ix].type == move) return; // do not double-learn move_instances
if (++ix == 4) {
move_instances[0] = move_instances[1];
move_instances[1] = move_instances[2];
move_instances[2] = move_instances[3];
ix--;
break;
}
}
move_instances[ix].type = move;
move_instances[ix].pp = moves[move].pp;
}
break;
}
}
void pokemon_instance_t::learn_all_moves()
{
move_instances[0].type = move_t::no_move;
move_instances[1].type = move_t::no_move;
move_instances[2].type = move_t::no_move;
move_instances[3].type = move_t::no_move;
const level_move_t * level_moves = pokemon[species].by_level.moves;
for (int32_t ix = 0; ix < pokemon[species].by_level.length; ix++) {
if (level_moves[ix].level <= level)
learn_move(level_moves[ix].move, -1);
else
break;
}
}

View File

@ -64,6 +64,11 @@ struct stat_values_t {
static_assert((sizeof (stat_values_t)) == 10);
struct move_instance_t {
enum move_t::move type;
uint8_t pp;
};
struct pokemon_instance_t {
uint8_t nickname[12];
enum pokemon_t::pokemon species;
@ -72,7 +77,7 @@ struct pokemon_instance_t {
determinant_values_t determinant_values;
stat_values_t stat_values;
stat_experience_t stat_experience;
enum move_t::move moves[4];
move_instance_t move_instances[4];
uint16_t current_hit_points;
enum ailment_t::ailment ailment;
@ -84,4 +89,5 @@ struct pokemon_instance_t {
void determine_stats();
void learn_move(enum move_t::move move, int32_t index);
void learn_all_moves();
};

View File

@ -51,7 +51,7 @@ def struct_move_t():
_move_constants_str = (f"{s.lower()}," for s in _move_constants)
return [
"struct move_t {",
"const uint8_t * name;"
"const uint8_t * name;",
"uint8_t animation;",
"uint8_t effect;",
"uint8_t power;",