1
0
mirror of https://github.com/lefticus/6502-cpp.git synced 2025-05-17 06:38:19 +00:00

"die" from exhaustion and full map movement

This commit is contained in:
Jason Turner 2021-07-18 18:57:17 -06:00
parent 17962801a0
commit bda3c6a9fc
4 changed files with 320 additions and 97 deletions

View File

@ -1,6 +1,6 @@
# Note: this is compiling for the host OS, so not really useful # Note: this is compiling for the host OS, so not really useful
# but it is handy as # but it is handy as a sanity check
add_executable(game game.cpp geometry.hpp) add_executable(game game.cpp geometry.hpp)
target_link_libraries( target_link_libraries(

View File

@ -13,6 +13,8 @@
struct GameState; struct GameState;
using Square_Passable = bool (*)(const std::uint8_t type) noexcept;
struct Map_Action struct Map_Action
{ {
geometry::rect box; geometry::rect box;
@ -20,55 +22,107 @@ struct Map_Action
using Action_Func = void (*)(GameState &); using Action_Func = void (*)(GameState &);
Action_Func action = nullptr; Action_Func action = nullptr;
constexpr void execute_if_collision(geometry::rect object, constexpr void execute_if_collision(geometry::rect object, GameState &game) const
GameState &game) const
{ {
if (action == nullptr) { return; } if (action == nullptr) { return; }
if (box.intersects(object)) { action(game); } if (box.intersects(object)) { action(game); }
} }
}; };
template<geometry::size Size> struct Map template<geometry::size Size, std::size_t Scale> struct Map
{ {
std::string_view name; std::string_view name;
petscii::Graphic<Size> layout; petscii::Graphic<Size> layout;
Square_Passable passable = nullptr;
std::uint8_t step_scale;
std::span<const Map_Action> actions; std::span<const Map_Action> actions;
[[nodiscard]] constexpr std::uint8_t location_value(geometry::point loc) const noexcept
{
const auto descaled_location =
geometry::point{ static_cast<std::uint8_t>(loc.x / Scale), static_cast<std::uint8_t>(loc.y / Scale) };
return layout[descaled_location];
}
[[nodiscard]] constexpr bool location_passable(geometry::point loc, geometry::size obj_size) const noexcept
{
if (passable != nullptr) {
for (const auto &p : obj_size) {
if (!passable(location_value(p + loc))) { return false; }
}
return true;
}
return false;
}
}; };
struct GameState struct GameState
{ {
enum struct State { Walking, SystemMenu, AboutBox, Exit, AlmostDead };
State state = State::Walking;
std::uint8_t endurance{ 10 }; std::uint8_t endurance{ 8 };
std::uint8_t stamina{ max_stamina() }; std::uint8_t stamina{ max_stamina() };
std::uint16_t cash{ 100 }; std::uint16_t cash{ 100 };
std::uint8_t step_counter{ 0 };
std::uint8_t stamina_counter{ 0 };
geometry::point location{ 20, 12 }; geometry::point location{ 20, 12 };
bool redraw = true; bool redraw = true;
bool redraw_stats = true;
c64::Clock game_clock{}; c64::Clock game_clock{};
Map<geometry::size{ 10, 5 }> const *current_map = nullptr; Map<geometry::size{ 10, 5 }, 4> const *current_map = nullptr;
Map<geometry::size{ 10, 5 }, 4> const *last_map = nullptr;
constexpr void set_current_map(const Map<geometry::size{ 10, 5 }> &new_map) constexpr void goto_last_map(geometry::point new_location)
{ {
current_map = &new_map; std::swap(current_map, last_map);
location = new_location;
redraw = true;
}
constexpr void set_current_map(const Map<geometry::size{ 10, 5 }, 4> &new_map)
{
last_map = std::exchange(current_map, &new_map);
redraw = true; redraw = true;
} }
constexpr void execute_actions(geometry::point new_location, const auto &character) noexcept constexpr void execute_actions(geometry::point new_location, const auto &character) noexcept
{ {
if (new_location.x + character.size().width > 40) { new_location.x = location.x; } if (new_location.x + character.size().width > 40) { new_location.x = location.x; }
if (new_location.y + character.size().height > 20) { new_location.y = location.y; } if (new_location.y + character.size().height > 20) { new_location.y = location.y; }
location = new_location;
if (current_map) { if (current_map && current_map->location_passable(new_location, character.size())) {
for (auto &action : current_map->actions) { action.execute_if_collision({location, character.size()}, *this); } step_counter += current_map->step_scale;
while (step_counter >= endurance) {
redraw_stats = true;
step_counter -= endurance;
if (stamina == 1) {
state = State::AlmostDead;
} else {
stamina -= 1;
}
stamina_counter += 1;
if (stamina_counter == endurance * 3) {
endurance += 1;
stamina_counter = 0;
}
}
location = new_location;
for (const auto &action : current_map->actions) {
action.execute_if_collision({ location, character.size() }, *this);
}
} }
} }
@ -232,6 +286,66 @@ template<class... Ts> struct overloaded : Ts...
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>; template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
struct TextBox
{
consteval TextBox(std::span<const std::string_view> t_lines)
: lines{ t_lines }, box{ geometry::rect{ { 0, 0 },
{ static_cast<std::uint8_t>(
std::max_element(begin(lines),
end(lines),
[](std::string_view lhs, std::string_view rhs) { return lhs.size() < rhs.size(); })
->size()
+ 1),
static_cast<std::uint8_t>(lines.size() + 1) } }
.centered() }
{}
void hide(GameState &game)
{
displayed = false;
game.redraw = true;
}
bool show([[maybe_unused]] GameState &game)
{
if (!displayed) {
displayed = true;
clear(box, vicii::Colors::grey);
draw_box(box, vicii::Colors::white);
for (auto pos = box.top_left() + geometry::point{ 1, 1 }; const auto &str : lines) {
puts(pos, str, vicii::Colors::grey);
pos = pos + geometry::point{ 0, 1 };
}
}
if (selected) {
selected = false;
return true;
} else {
return false;
}
}
bool process_event(const GameState::Event &e)
{
if (not displayed) { return false; }
if (const auto *ptr = std::get_if<GameState::JoyStick2StateChanged>(&e); ptr) {
if (ptr->state.fire()) { selected = true; }
return true;
}
return false;
}
std::span<const std::string_view> lines;
geometry::rect box;
bool selected{ false };
bool displayed{ false };
};
struct Menu struct Menu
{ {
consteval Menu(std::span<const std::string_view> t_options) consteval Menu(std::span<const std::string_view> t_options)
@ -241,8 +355,8 @@ struct Menu
end(options), end(options),
[](std::string_view lhs, std::string_view rhs) { return lhs.size() < rhs.size(); }) [](std::string_view lhs, std::string_view rhs) { return lhs.size() < rhs.size(); })
->size() ->size()
+ 2), + 1),
static_cast<std::uint8_t>(options.size() + 2) } } static_cast<std::uint8_t>(options.size() + 1) } }
.centered() } .centered() }
{} {}
@ -250,7 +364,7 @@ struct Menu
void highlight(std::uint8_t selection) void highlight(std::uint8_t selection)
{ {
const auto cur_y = static_cast<std::uint8_t>(selection + 1 + box.top_left().y); const auto cur_y = static_cast<std::uint8_t>(selection + 1 + box.top_left().y);
for (std::uint8_t cur_x = 1; cur_x < box.width() - 1; ++cur_x) { for (std::uint8_t cur_x = 1; cur_x < box.width(); ++cur_x) {
vicii::invertc(geometry::point{ static_cast<std::uint8_t>(box.top_left().x + cur_x), cur_y }); vicii::invertc(geometry::point{ static_cast<std::uint8_t>(box.top_left().x + cur_x), cur_y });
} }
} }
@ -268,6 +382,7 @@ struct Menu
if (!displayed) { if (!displayed) {
displayed = true; displayed = true;
clear(box, vicii::Colors::grey);
draw_box(box, vicii::Colors::white); draw_box(box, vicii::Colors::white);
for (auto pos = box.top_left() + geometry::point{ 1, 1 }; const auto &str : options) { for (auto pos = box.top_left() + geometry::point{ 1, 1 }; const auto &str : options) {
@ -298,11 +413,22 @@ struct Menu
if (not displayed) { return false; } if (not displayed) { return false; }
if (const auto *ptr = std::get_if<GameState::JoyStick2StateChanged>(&e); ptr) { if (const auto *ptr = std::get_if<GameState::JoyStick2StateChanged>(&e); ptr) {
if (ptr->state.up()) { next_selection = current_selection - 1; } // wrap around up and down during selection
if (ptr->state.up()) {
if (current_selection == 0) {
next_selection = static_cast<std::uint8_t>(options.size() - 1);
} else {
next_selection = current_selection - 1;
}
}
if (ptr->state.down()) { next_selection = current_selection + 1; } if (ptr->state.down()) {
if (current_selection == options.size() - 1) {
if (next_selection > options.size() - 1) { next_selection = static_cast<std::uint8_t>(options.size() - 1); } next_selection = 0;
} else {
next_selection = current_selection + 1;
}
}
if (ptr->state.fire()) { selected = true; } if (ptr->state.fire()) { selected = true; }
@ -321,8 +447,6 @@ struct Menu
}; };
enum struct State { Walking, SystemMenu, AboutBox };
int main() int main()
{ {
// static constexpr auto charset = load_charset(uppercase); // static constexpr auto charset = load_charset(uppercase);
@ -352,14 +476,6 @@ int main()
160,160,160,160,76,160, 160,160,160,160,76,160,
}; };
/*
static constexpr auto town = Graphic<4, 4>{
85, 67, 67, 73,
93, 233, 223, 93,
93, 160, 160, 93,
74, 67, 67, 75 };
*/
static constexpr auto town = petscii::ColoredGraphic<geometry::size{4, 4}>{ static constexpr auto town = petscii::ColoredGraphic<geometry::size{4, 4}>{
{ {
32, 32,32, 32, 32, 32,32, 32,
@ -375,15 +491,7 @@ int main()
} }
}; };
/* static constexpr auto mountain = petscii::ColoredGraphic<geometry::size{4, 4}>{
static constexpr auto mountain = petscii::Graphic<geometry::size{4, 4}>{
32, 78, 77, 32,
32, 32, 78, 77,
78, 77, 32, 32,
32, 78, 77, 32 };
*/
static constexpr auto colored_mountain = petscii::ColoredGraphic<geometry::size{4, 4}>{
{ {
32, 78, 77, 32, 32, 78, 77, 32,
32, 32, 233, 223, 32, 32, 233, 223,
@ -404,7 +512,92 @@ int main()
78, 79, 78, 79,
78, 77 }; 78, 77 };
static constexpr auto city_map = Map<geometry::size{10,5}>{ static constexpr auto ore_town_actions = std::array {
Map_Action { {{0,19},{40,1}}, [](GameState &g) { g.goto_last_map({0, 4}); } },
};
static constexpr auto ore_town = Map<geometry::size{10,5}, 4>{
"ore town",
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 4, 0, 0, 0, 0, 6, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 5, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
},
[](const std::uint8_t type) noexcept { return type != 1; },
1,
ore_town_actions
};
static constexpr auto wool_town_actions = std::array {
Map_Action { {{0,0},{1,20}}, [](GameState &g) { g.goto_last_map({6, 12}); } },
Map_Action { {{39,0},{1,20}}, [](GameState &g) { g.goto_last_map({12, 12}); } },
};
static constexpr auto wool_town = Map<geometry::size{10,5}, 4>{
"wool town",
{
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 4, 0, 0, 0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
},
[](const std::uint8_t type) noexcept { return type != 1; },
1,
wool_town_actions
};
static constexpr auto wheat_town_actions = std::array {
Map_Action { {{0,0},{1,20}}, [](GameState &g) { g.goto_last_map({22, 16}); } },
Map_Action { {{39,0},{1,20}}, [](GameState &g) { g.goto_last_map({28, 16}); } },
Map_Action { {{0,0},{40,1}}, [](GameState &g) { g.goto_last_map({22, 13}); } },
};
static constexpr auto wheat_town = Map<geometry::size{10,5}, 4>{
"wheat town",
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 4, 0, 0, 0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
},
[](const std::uint8_t type) noexcept { return type != 1; },
1,
wheat_town_actions
};
static constexpr auto brick_town_actions = std::array {
Map_Action { {{0,0},{1,20}}, [](GameState &g) { g.goto_last_map({30, 4}); } },
Map_Action { {{39,0},{1,20}}, [](GameState &g) { g.goto_last_map({36, 4}); } },
Map_Action { {{0,0},{40,1}}, [](GameState &g) { g.goto_last_map({32, 0}); } },
Map_Action { {{0,19},{40,1}}, [](GameState &g) { g.goto_last_map({32, 8}); } },
};
static constexpr auto brick_town = Map<geometry::size{10,5}, 4>{
"brick town",
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 4, 0, 0, 0, 0, 6, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
},
[](const std::uint8_t type) noexcept { return type != 1; },
1,
brick_town_actions
};
static constexpr auto wood_town_actions = std::array {
Map_Action { {{0,0},{1,20}}, [](GameState &g) { g.goto_last_map({14, 0}); } },
Map_Action { {{39,0},{1,20}}, [](GameState &g) { g.goto_last_map({20, 0}); } },
Map_Action { {{0,19},{40,1}}, [](GameState &g) { g.goto_last_map({16, 4}); } },
};
static constexpr auto wood_town = Map<geometry::size{10,5}, 4>{
"wood town", "wood town",
{ {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -413,15 +606,21 @@ int main()
0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}, },
std::span<const Map_Action>{} [](const std::uint8_t type) noexcept { return type != 1; },
1,
wood_town_actions
}; };
static constexpr auto overview_actions = std::array { static constexpr auto overview_actions = std::array {
Map_Action { {{16,0},{4,4}}, [](GameState &g) { g.set_current_map(city_map); } } Map_Action { {{0,0},{4,4}}, [](GameState &g) { g.set_current_map(ore_town); } },
}; Map_Action { {{8,12},{4,4}}, [](GameState &g) { g.set_current_map(wool_town); } },
Map_Action { {{24,16},{4,4}}, [](GameState &g) { g.set_current_map(wheat_town); } },
Map_Action { {{32,4},{4,4}}, [](GameState &g) { g.set_current_map(brick_town); } },
Map_Action { {{16,0},{4,4}}, [](GameState &g) { g.set_current_map(wood_town); } }
};
static constexpr auto overview_map = Map<geometry::size{10, 5}>{ static constexpr auto overview_map = Map<geometry::size{10, 5}, 4>{
"the world", "the world",
{ {
3, 1, 1, 0, 3, 0, 0, 0, 0, 0, 3, 1, 1, 0, 3, 0, 0, 0, 0, 0,
@ -430,14 +629,16 @@ int main()
0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 0, 0, 3, 0, 0, 0, 0, 1, 1, 1, 0, 0, 3, 0, 0, 0,
}, },
std::span<const Map_Action>(begin(overview_actions), end(overview_actions)) [](const std::uint8_t type) noexcept { return type != 1; },
10,
overview_actions
}; };
// clang-format on // clang-format on
static constexpr std::array<void (*)(geometry::point), 7> tile_types{ static constexpr std::array<void (*)(geometry::point), 7> tile_types{
[](geometry::point) {}, [](geometry::point) {},
[](geometry::point p) { vicii::put_graphic(p, colored_mountain); }, [](geometry::point p) { vicii::put_graphic(p, mountain); },
[](geometry::point) {}, [](geometry::point) {},
[](geometry::point p) { vicii::put_graphic(p, town); }, [](geometry::point p) { vicii::put_graphic(p, town); },
[](geometry::point p) { vicii::put_graphic(p, inn); }, [](geometry::point p) { vicii::put_graphic(p, inn); },
@ -449,13 +650,15 @@ int main()
const auto draw_map = [](const auto &map) { const auto draw_map = [](const auto &map) {
for (std::size_t tile = 0; tile < tile_types.size(); ++tile) { for (std::size_t tile = 0; tile < tile_types.size(); ++tile) {
for (const auto &pos : map.size()) { for (const auto &pos : map.size()) {
if (map[pos] == tile) { tile_types[tile]({static_cast<std::uint8_t>(pos.x * 4), static_cast<std::uint8_t>(pos.y * 4)}); } if (map[pos] == tile) {
tile_types[tile]({ static_cast<std::uint8_t>(pos.x * 4), static_cast<std::uint8_t>(pos.y * 4) });
}
} }
} }
}; };
GameState game; GameState game;
State state = State::Walking; game.state = GameState::State::Walking;
game.current_map = &overview_map; game.current_map = &overview_map;
constexpr auto show_stats = [](const auto &cur_game) { constexpr auto show_stats = [](const auto &cur_game) {
@ -471,19 +674,35 @@ int main()
vicii::Screen screen; vicii::Screen screen;
static constexpr auto menu_options = std::array{ std::string_view{ "info" }, static constexpr auto menu_options = std::array{ std::string_view{ "about game" }, std::string_view{ "exit menu" } };
std::string_view{ "test2" },
std::string_view{ "test3" },
std::string_view{ "an even longer thing" } };
Menu m(menu_options); Menu m(menu_options);
static constexpr auto url = petscii::PETSCII("HTTPS://GITHUB.COM/LEFTICUS/6502-CPP");
static constexpr auto about_text = std::array{ std::string_view{ "created in c++20 by jason turner" },
std::string_view{ "using an automated conversion of" },
std::string_view{ "gcc generated avr code to 6502" },
std::string_view{ "assembly." },
std::string_view{ url.data(), url.size() } };
TextBox about_box(about_text);
static constexpr auto almost_dead_text = std::array{ std::string_view{ "you became so exhausted that you" },
std::string_view{ "passed out and passers by stole" },
std::string_view{ "some of your cash and items." },
std::string_view{ "" },
std::string_view{ "a kind soul has dropped you off at a" },
std::string_view{ "nearby inn." } };
TextBox almost_dead(almost_dead_text);
auto eventHandler = overloaded{ [&](const GameState::JoyStick2StateChanged &e) { auto eventHandler = overloaded{ [&](const GameState::JoyStick2StateChanged &e) {
auto new_loc = game.location; auto new_loc = game.location;
put_hex({ 36, 1 }, e.state.state, vicii::Colors::dark_grey);
if (e.state.fire()) { if (e.state.fire()) {
state = State::SystemMenu; game.state = GameState::State::SystemMenu;
return; return;
} }
@ -492,19 +711,20 @@ int main()
if (e.state.left()) { --new_loc.x; } if (e.state.left()) { --new_loc.x; }
if (e.state.right()) { ++new_loc.x; } if (e.state.right()) { ++new_loc.x; }
if (new_loc != game.location) {
game.execute_actions(new_loc, character.graphic); game.execute_actions(new_loc, character.graphic);
screen.show(game.location, character);
screen.show(game.location, character); }
put_hex({36, 1}, e.state.state, vicii::Colors::dark_grey);
}, },
[](const GameState::TimeElapsed &e) { vicii::put_hex({36, 0}, e.us.count(), vicii::Colors::dark_grey); } }; [](const GameState::TimeElapsed &e) {
vicii::put_hex({ 36, 0 }, e.us.count(), vicii::Colors::dark_grey);
} };
while (true) { while (game.state != GameState::State::Exit) {
const auto next_event = game.next_event(); const auto next_event = game.next_event();
if (not m.process_event(next_event)) { if (not m.process_event(next_event) && not about_box.process_event(next_event)
&& not almost_dead.process_event(next_event)) {
// if no gui elements needed the event, then we handle it // if no gui elements needed the event, then we handle it
std::visit(eventHandler, next_event); std::visit(eventHandler, next_event);
} }
@ -517,37 +737,37 @@ int main()
mos6502::poke(53281, 0); mos6502::poke(53281, 0);
game.redraw = false; game.redraw = false;
game.redraw_stats = true;
draw_map(game.current_map->layout); draw_map(game.current_map->layout);
draw_box(geometry::rect{ { 0, 20 }, { 40, 5 } }, vicii::Colors::dark_grey); draw_box(geometry::rect{ { 0, 20 }, { 39, 4 } }, vicii::Colors::dark_grey);
puts(geometry::point{ 10, 20 }, game.current_map->name, vicii::Colors::white); puts(geometry::point{ 10, 20 }, game.current_map->name, vicii::Colors::white);
show_stats(game);
screen.show(game.location, character); screen.show(game.location, character);
} }
if (std::uint8_t result = 0; state == State::SystemMenu && m.show(game, result)) { if (game.redraw_stats) {
show_stats(game);
game.redraw_stats = false;
}
if (std::uint8_t result = 0; game.state == GameState::State::SystemMenu && m.show(game, result)) {
// we had a menu item selected // we had a menu item selected
m.hide(game); m.hide(game);
if (result == 0) {} if (result == 0) {
game.state = GameState::State::AboutBox;
vicii::increment_border_color();
}
/*
const auto background_color = [](Colors col) {
memory_loc(0xd021) = static_cast<uint8_t>(col);
};
background_color(Colors::WHITE);
while(true) {
if (joystick_down()) {
increment_border_color();
} else { } else {
decrement_border_color(); game.state = GameState::State::Walking;
} }
} else if (game.state == GameState::State::AboutBox && about_box.show(game)) {
about_box.hide(game);
game.state = GameState::State::Walking;
} else if (game.state == GameState::State::AlmostDead && almost_dead.show(game)) {
almost_dead.hide(game);
game.set_current_map(wheat_town);
game.cash /= 2;
game.stamina = game.max_stamina();
game.state = GameState::State::Walking;
} }
*/
} }
} }

View File

@ -10,8 +10,6 @@ struct point
std::uint8_t y; std::uint8_t y;
[[nodiscard]] constexpr auto operator<=>(const point &other) const = default; [[nodiscard]] constexpr auto operator<=>(const point &other) const = default;
// [[nodiscard]] constexpr bool operator==(const point &other) const = default;
// [[nodiscard]] constexpr bool operator!=(const point &other) const = default;
}; };
constexpr point operator+(const point &lhs, const point &rhs) noexcept constexpr point operator+(const point &lhs, const point &rhs) noexcept
@ -33,12 +31,9 @@ struct rect
[[nodiscard]] constexpr std::uint8_t top() const noexcept { return tl.y; } [[nodiscard]] constexpr std::uint8_t top() const noexcept { return tl.y; }
[[nodiscard]] constexpr std::uint8_t bottom() const noexcept [[nodiscard]] constexpr std::uint8_t bottom() const noexcept
{ {
return static_cast<std::uint8_t>(tl.y + size_.height - 1); return static_cast<std::uint8_t>(tl.y + size_.height);
}
[[nodiscard]] constexpr std::uint8_t right() const noexcept
{
return static_cast<std::uint8_t>(tl.x + size_.width - 1);
} }
[[nodiscard]] constexpr std::uint8_t right() const noexcept { return static_cast<std::uint8_t>(tl.x + size_.width); }
[[nodiscard]] constexpr const point &top_left() const noexcept { return tl; } [[nodiscard]] constexpr const point &top_left() const noexcept { return tl; }
[[nodiscard]] constexpr point bottom_right() const noexcept { return point{ right(), bottom() }; } [[nodiscard]] constexpr point bottom_right() const noexcept { return point{ right(), bottom() }; }
@ -49,6 +44,7 @@ struct rect
[[nodiscard]] constexpr const auto &size() const noexcept { return size_; } [[nodiscard]] constexpr const auto &size() const noexcept { return size_; }
// returns a rectangle of this size, but centered in the screen (40x20)
[[nodiscard]] constexpr auto centered() const noexcept [[nodiscard]] constexpr auto centered() const noexcept
{ {
return rect{ return rect{
@ -64,7 +60,7 @@ struct rect
const auto other_tl = other.top_left(); const auto other_tl = other.top_left();
const auto other_br = other.bottom_right(); const auto other_br = other.bottom_right();
return my_tl <= other_tl && my_br >= other_br; return my_tl.x < other_br.x && my_br.x > other_tl.x && my_tl.y < other_br.y && my_br.y > other_tl.y;
}; };
point tl; point tl;

View File

@ -27,7 +27,7 @@ enum struct Colors : std::uint8_t {
[[maybe_unused]] static void decrement_border_color() { mos6502::poke(0xd020, mos6502::peek(0xd020) - 1); } [[maybe_unused]] static void decrement_border_color() { mos6502::poke(0xd020, mos6502::peek(0xd020) - 1); }
static void increment_border_color() { mos6502::poke(0xd020, mos6502::peek(0xd020) + 1); } // static void increment_border_color() { mos6502::poke(0xd020, mos6502::peek(0xd020) + 1); }
static void putc(geometry::point location, std::uint8_t c, Colors color) static void putc(geometry::point location, std::uint8_t c, Colors color)
{ {
@ -119,7 +119,7 @@ struct Screen
} }
}; };
void draw_vline(geometry::point begin, const geometry::point end, Colors c) static void draw_vline(geometry::point begin, const geometry::point end, Colors c)
{ {
while (begin < end) { while (begin < end) {
putc(begin, 93, c); putc(begin, 93, c);
@ -127,7 +127,7 @@ void draw_vline(geometry::point begin, const geometry::point end, Colors c)
} }
} }
void draw_hline(geometry::point begin, const geometry::point end, Colors c) static void draw_hline(geometry::point begin, const geometry::point end, Colors c)
{ {
while (begin < end) { while (begin < end) {
putc(begin, 67, c); putc(begin, 67, c);
@ -135,7 +135,7 @@ void draw_hline(geometry::point begin, const geometry::point end, Colors c)
} }
} }
void draw_box(geometry::rect geo, Colors color) static void draw_box(geometry::rect geo, Colors color)
{ {
putc(geo.top_left(), 85, color); putc(geo.top_left(), 85, color);
putc(geo.top_right(), 73, color); putc(geo.top_right(), 73, color);
@ -148,6 +148,13 @@ void draw_box(geometry::rect geo, Colors color)
draw_vline(geo.top_left() + geometry::point{ 0, 1 }, geo.bottom_left(), color); draw_vline(geo.top_left() + geometry::point{ 0, 1 }, geo.bottom_left(), color);
draw_vline(geo.top_right() + geometry::point{ 0, 1 }, geo.bottom_right(), color); draw_vline(geo.top_right() + geometry::point{ 0, 1 }, geo.bottom_right(), color);
} }
void clear(geometry::rect box, Colors color) {
for (const auto &p : box.size()) {
putc(p + box.top_left(), ' ', color);
}
}
}// namespace vicii }// namespace vicii
#endif// INC_6502_C_VICII_HPP #endif// INC_6502_C_VICII_HPP