graphic: display nearly-complete stats page #1
Type is not displayed because the type names have no parsers/generators yet.
This commit is contained in:
parent
e88135d4e3
commit
abe7bde678
2
Makefile
2
Makefile
@ -35,6 +35,8 @@ SRC += vram.cpp
|
|||||||
SRC += font.cpp
|
SRC += font.cpp
|
||||||
SRC += graphic.cpp
|
SRC += graphic.cpp
|
||||||
SRC += menu.cpp
|
SRC += menu.cpp
|
||||||
|
SRC += number.cpp
|
||||||
|
SRC += pokemon_instance.cpp
|
||||||
|
|
||||||
DEP = $(patsubst %.cpp,%.cpp.d,$(SRC))
|
DEP = $(patsubst %.cpp,%.cpp.d,$(SRC))
|
||||||
|
|
||||||
|
BIN
derived/font.png
BIN
derived/font.png
Binary file not shown.
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 18 KiB |
95
graphic.cpp
95
graphic.cpp
@ -5,6 +5,9 @@
|
|||||||
#include "font.hpp"
|
#include "font.hpp"
|
||||||
#include "start_size.hpp"
|
#include "start_size.hpp"
|
||||||
#include "control.hpp"
|
#include "control.hpp"
|
||||||
|
#include "number.hpp"
|
||||||
|
|
||||||
|
#include "pokemon_instance.hpp"
|
||||||
|
|
||||||
struct dialog_border {
|
struct dialog_border {
|
||||||
static constexpr uint8_t corner_top_left = 105;
|
static constexpr uint8_t corner_top_left = 105;
|
||||||
@ -44,6 +47,12 @@ struct hp_bar {
|
|||||||
static constexpr uint8_t end_cap_bar = 0x66;
|
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<const uint8_t *>
|
#define S reinterpret_cast<const uint8_t *>
|
||||||
|
|
||||||
void draw_text(const uint32_t base_pattern,
|
void draw_text(const uint32_t base_pattern,
|
||||||
@ -184,23 +193,95 @@ void draw_hp_bar(const uint32_t base_pattern,
|
|||||||
put_char(base_pattern, top_left.x + 8, top_left.y, end_cap);
|
put_char(base_pattern, top_left.x + 8, top_left.y, end_cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t hp = 0;
|
void draw_stats1(const uint32_t base_pattern,
|
||||||
|
const pokemon_instance_t& pokemon_instance)
|
||||||
void draw_stats1(const uint32_t base_pattern)
|
|
||||||
{
|
{
|
||||||
// white out the entire screen
|
// white out the entire screen
|
||||||
draw_box_background(base_pattern, {-1, -1}, {20, 18});
|
draw_box_background(base_pattern, {-1, -1}, {20, 18});
|
||||||
|
|
||||||
// top status battle box
|
// top status battle box
|
||||||
|
{
|
||||||
draw_battle_border(base_pattern, {19, 1}, {8, 7});
|
draw_battle_border(base_pattern, {19, 1}, {8, 7});
|
||||||
draw_hp_bar(base_pattern, {11, 4}, hp_bar::end_cap_bar, (hp >> 3) % 48);
|
|
||||||
hp++;
|
|
||||||
|
|
||||||
// bottom border
|
// 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<const uint8_t *>
|
||||||
|
draw_text(base_pattern, S("STATUS/___"), 9, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom right border
|
||||||
|
{
|
||||||
draw_battle_border(base_pattern, {19, 9}, {12, 17});
|
draw_battle_border(base_pattern, {19, 9}, {12, 17});
|
||||||
|
|
||||||
// bottom left dialog box
|
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_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<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,
|
void dialog_t::draw(const uint32_t base_pattern,
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include "render_map.hpp" // for cell_offset
|
#include "render_map.hpp" // for cell_offset
|
||||||
#include "vdp2.h"
|
#include "vdp2.h"
|
||||||
|
|
||||||
|
#include "pokemon_instance.hpp"
|
||||||
|
|
||||||
static inline void put_char(const uint32_t base_pattern,
|
static inline void put_char(const uint32_t base_pattern,
|
||||||
const int32_t x, const int32_t y, // in cells
|
const int32_t x, const int32_t y, // in cells
|
||||||
const uint8_t c)
|
const uint8_t c)
|
||||||
@ -27,7 +29,8 @@ void draw_box_background(const uint32_t base_pattern,
|
|||||||
void draw_battle_border(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);
|
const screen_cell_t& top_corner, const screen_cell_t& bottom_corner);
|
||||||
|
|
||||||
void draw_stats1(const uint32_t base_pattern);
|
void draw_stats1(const uint32_t base_pattern,
|
||||||
|
const pokemon_instance_t& pokemon_instance);
|
||||||
|
|
||||||
struct dialog_t
|
struct dialog_t
|
||||||
{
|
{
|
||||||
|
31
main.cpp
31
main.cpp
@ -27,6 +27,7 @@
|
|||||||
#include "menu.hpp"
|
#include "menu.hpp"
|
||||||
|
|
||||||
#include "pokemon.hpp"
|
#include "pokemon.hpp"
|
||||||
|
#include "pokemon_instance.hpp"
|
||||||
|
|
||||||
static int32_t pokemon_raw_index = 0;
|
static int32_t pokemon_raw_index = 0;
|
||||||
|
|
||||||
@ -171,6 +172,15 @@ void render_pokemon(const uint32_t ix, const enum pokemon_t::pokemon pokemon_id,
|
|||||||
const uint32_t character_address = (base_pattern * 16) / 8;
|
const uint32_t character_address = (base_pattern * 16) / 8;
|
||||||
const uint32_t dimension = pokemon_sprite_dimension(pokemon[pokemon_id].pic.front.size);
|
const uint32_t dimension = pokemon_sprite_dimension(pokemon[pokemon_id].pic.front.size);
|
||||||
|
|
||||||
|
int32_t x_offset;
|
||||||
|
int32_t y_offset;
|
||||||
|
switch (dimension) {
|
||||||
|
default:
|
||||||
|
case 40: x_offset = 2; y_offset = 2; break;
|
||||||
|
case 48: x_offset = 1; y_offset = 1; break;
|
||||||
|
case 56: x_offset = 1; y_offset = 0; break;
|
||||||
|
}
|
||||||
|
|
||||||
vdp1.vram.cmd[ix].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__NORMAL_SPRITE;
|
vdp1.vram.cmd[ix].CTRL = CTRL__JP__JUMP_NEXT | CTRL__COMM__NORMAL_SPRITE;
|
||||||
vdp1.vram.cmd[ix].LINK = 0;
|
vdp1.vram.cmd[ix].LINK = 0;
|
||||||
// The "end code" is 0xf, which is being used in the mai sprite palette. If
|
// The "end code" is 0xf, which is being used in the mai sprite palette. If
|
||||||
@ -181,8 +191,8 @@ void render_pokemon(const uint32_t ix, const enum pokemon_t::pokemon pokemon_id,
|
|||||||
| COLR__COLOR_BANK__TYPE0__PR(0);
|
| COLR__COLOR_BANK__TYPE0__PR(0);
|
||||||
vdp1.vram.cmd[ix].SRCA = character_address;
|
vdp1.vram.cmd[ix].SRCA = character_address;
|
||||||
vdp1.vram.cmd[ix].SIZE = SIZE__X(dimension) | SIZE__Y(dimension);
|
vdp1.vram.cmd[ix].SIZE = SIZE__X(dimension) | SIZE__Y(dimension);
|
||||||
vdp1.vram.cmd[ix].XA = (cell_offset::x * 8) + screen_cell.x * 8;
|
vdp1.vram.cmd[ix].XA = (cell_offset::x * 8) + (screen_cell.x + x_offset) * 8;
|
||||||
vdp1.vram.cmd[ix].YA = (cell_offset::y * 8) + screen_cell.y * 8;
|
vdp1.vram.cmd[ix].YA = (cell_offset::y * 8) + (screen_cell.y + y_offset) * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
void render_sprites(const offset_t& offset)
|
void render_sprites(const offset_t& offset)
|
||||||
@ -486,8 +496,12 @@ void update()
|
|||||||
//else if (event::button_a() ) check_sign();
|
//else if (event::button_a() ) check_sign();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t frame = 0;
|
||||||
|
|
||||||
void render()
|
void render()
|
||||||
{
|
{
|
||||||
|
frame++;
|
||||||
|
|
||||||
const offset_t offset = state.player.offset();
|
const offset_t offset = state.player.offset();
|
||||||
|
|
||||||
render_sprites(offset);
|
render_sprites(offset);
|
||||||
@ -505,7 +519,18 @@ void render()
|
|||||||
cursor_t cursor = { 1, 1 };
|
cursor_t cursor = { 1, 1 };
|
||||||
draw_menu_cursor(state.draw.base_pattern.font, fight_menu, cursor);
|
draw_menu_cursor(state.draw.base_pattern.font, fight_menu, cursor);
|
||||||
|
|
||||||
draw_stats1(state.draw.base_pattern.font);
|
static pokemon_instance_t pokemon_instance;
|
||||||
|
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.determine_stats();
|
||||||
|
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;
|
||||||
|
|
||||||
|
draw_stats1(state.draw.base_pattern.font, pokemon_instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
|
68
number.cpp
Normal file
68
number.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "graphic.hpp"
|
||||||
|
#include "font.hpp"
|
||||||
|
|
||||||
|
// left-align (with fill)
|
||||||
|
// right-align
|
||||||
|
|
||||||
|
struct bcd_t {
|
||||||
|
uint32_t digits;
|
||||||
|
int32_t length;
|
||||||
|
|
||||||
|
auto operator<=>(const bcd_t&) const = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline constexpr bcd_t int_to_bcd(int32_t n)
|
||||||
|
{
|
||||||
|
if (n < 0) n = 0; // are there negative numbers in pokemon?
|
||||||
|
if (n == 0) return { 0, 1 };
|
||||||
|
uint32_t bcd = 0;
|
||||||
|
|
||||||
|
int32_t index = 0;
|
||||||
|
while (n != 0) {
|
||||||
|
bcd |= (n % 10) << (4 * index);
|
||||||
|
n /= 10;
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
return { bcd, index };
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(int_to_bcd(0) == (bcd_t){0x0, 1});
|
||||||
|
static_assert(int_to_bcd(9) == (bcd_t){0x9, 1});
|
||||||
|
static_assert(int_to_bcd(20) == (bcd_t){0x20, 2});
|
||||||
|
static_assert(int_to_bcd(512) == (bcd_t){0x512, 3});
|
||||||
|
|
||||||
|
void draw_number_right_align(const uint32_t base_pattern,
|
||||||
|
const screen_cell_t& pos,
|
||||||
|
const int32_t n,
|
||||||
|
const int32_t width,
|
||||||
|
const uint8_t fill)
|
||||||
|
{
|
||||||
|
const bcd_t bcd = int_to_bcd(n);
|
||||||
|
|
||||||
|
int32_t index = 0;
|
||||||
|
while (index < width) {
|
||||||
|
const uint8_t digit = (index < bcd.length)
|
||||||
|
? ascii_to_font('0' + ((bcd.digits >> (4 * index)) & 0b1111))
|
||||||
|
: fill;
|
||||||
|
index++;
|
||||||
|
put_char(base_pattern, (pos.x + width) - index, pos.y, digit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_number_left_align(const uint32_t base_pattern,
|
||||||
|
const screen_cell_t& pos,
|
||||||
|
const int32_t n,
|
||||||
|
const int32_t width)
|
||||||
|
{
|
||||||
|
const bcd_t bcd = int_to_bcd(n);
|
||||||
|
|
||||||
|
int32_t index = 0;
|
||||||
|
int32_t length = width < bcd.length ? width : bcd.length;
|
||||||
|
while (index < length) {
|
||||||
|
const uint8_t digit = ascii_to_font('0' + ((bcd.digits >> (4 * index)) & 0b1111));
|
||||||
|
index++;
|
||||||
|
put_char(base_pattern, pos.x + (length - index), pos.y, digit);
|
||||||
|
}
|
||||||
|
}
|
16
number.hpp
Normal file
16
number.hpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "coordinates.hpp"
|
||||||
|
|
||||||
|
void draw_number_right_align(const uint32_t base_pattern,
|
||||||
|
const screen_cell_t& pos,
|
||||||
|
const int32_t n,
|
||||||
|
const int32_t width,
|
||||||
|
const uint8_t fill);
|
||||||
|
|
||||||
|
void draw_number_left_align(const uint32_t base_pattern,
|
||||||
|
const screen_cell_t& pos,
|
||||||
|
const int32_t n,
|
||||||
|
const int32_t width);
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "gen/pokemon/moves.hpp"
|
#include "gen/pokemon/moves.hpp"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "pokemon_instance.hpp"
|
#include "pokemon_instance.hpp"
|
||||||
|
|
||||||
static uint16_t _determine_stat(const uint32_t base_stat_value,
|
static constexpr uint16_t _determine_stat(const uint32_t base_stat_value,
|
||||||
const uint32_t determinant_value,
|
const uint32_t determinant_value,
|
||||||
const uint32_t stat_experience,
|
const uint32_t stat_experience,
|
||||||
const uint32_t level)
|
const uint32_t level)
|
||||||
@ -17,45 +17,41 @@ static_assert(_determine_stat( 90, 0b0101, 0, 70) == (138 - 5));
|
|||||||
static_assert(_determine_stat(130, 0b1000, 0, 70) == (198 - 5));
|
static_assert(_determine_stat(130, 0b1000, 0, 70) == (198 - 5));
|
||||||
static_assert(_determine_stat(154, 0b0110, 0, 70) == (229 - 5));
|
static_assert(_determine_stat(154, 0b0110, 0, 70) == (229 - 5));
|
||||||
|
|
||||||
constexpr inline uint16_t
|
void
|
||||||
pokemon_instance_t::determine_stat(enum stat_t stat)
|
pokemon_instance_t::determine_stats()
|
||||||
{
|
{
|
||||||
switch (stat) {
|
stat_values.hit_points = _determine_stat(pokemon[species].base_stat_values.hit_points,
|
||||||
default:
|
|
||||||
case stat_t::hit_points:
|
|
||||||
return _determine_stat(pokemon[species].base_stat_values.hit_points,
|
|
||||||
determinant_values.hit_points(),
|
determinant_values.hit_points(),
|
||||||
stat_experience.hit_points,
|
stat_experience.hit_points,
|
||||||
level)
|
level
|
||||||
+ 10 + level;
|
) + 10 + level;
|
||||||
case stat_t::attack:
|
|
||||||
return _determine_stat(pokemon[species].base_stat_values.attack,
|
stat_values.attack = _determine_stat(pokemon[species].base_stat_values.attack,
|
||||||
determinant_values.attack(),
|
determinant_values.attack(),
|
||||||
stat_experience.attack,
|
stat_experience.attack,
|
||||||
level)
|
level
|
||||||
+ 5;
|
) + 5;
|
||||||
case stat_t::defense:
|
|
||||||
return _determine_stat(pokemon[species].base_stat_values.defense,
|
stat_values.defense = _determine_stat(pokemon[species].base_stat_values.defense,
|
||||||
determinant_values.defense(),
|
determinant_values.defense(),
|
||||||
stat_experience.defense,
|
stat_experience.defense,
|
||||||
level)
|
level
|
||||||
+ 5;
|
) + 5;
|
||||||
case stat_t::speed:
|
|
||||||
return _determine_stat(pokemon[species].base_stat_values.speed,
|
stat_values.speed = _determine_stat(pokemon[species].base_stat_values.speed,
|
||||||
determinant_values.speed(),
|
determinant_values.speed(),
|
||||||
stat_experience.speed,
|
stat_experience.speed,
|
||||||
level)
|
level
|
||||||
+ 5;
|
) + 5;
|
||||||
case stat_t::special:
|
|
||||||
return _determine_stat(pokemon[species].base_stat_values.special,
|
stat_values.special = _determine_stat(pokemon[species].base_stat_values.special,
|
||||||
determinant_values.special(),
|
determinant_values.special(),
|
||||||
stat_experience.special,
|
stat_experience.special,
|
||||||
level)
|
level
|
||||||
+ 5;
|
) + 5;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr inline uint16_t
|
void
|
||||||
pokemon_instance_t::learn_move(enum move_t::move move, int32_t index)
|
pokemon_instance_t::learn_move(enum move_t::move move, int32_t index)
|
||||||
{
|
{
|
||||||
switch (index) {
|
switch (index) {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "sqrt.hpp"
|
#include "sqrt.hpp"
|
||||||
@ -77,7 +79,7 @@ struct pokemon_instance_t {
|
|||||||
// - id number
|
// - id number
|
||||||
// - original trainer
|
// - original trainer
|
||||||
|
|
||||||
constexpr inline uint16_t determine_stat(enum stat_t stat);
|
void determine_stats();
|
||||||
|
|
||||||
constexpr inline void learn_move(enum move_t::move move, uint32_t index);
|
void learn_move(enum move_t::move move, int32_t index);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user