aboutsummaryrefslogtreecommitdiff
path: root/src/game/game.cpp
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2022-06-08 18:31:58 +0200
committerHampusM <hampus@hampusmat.com>2022-06-13 17:57:01 +0200
commit6d66d5675d0fb78827bc47c49f9d4a1852c7255d (patch)
treee8cbf56d895c6d4acc496fffb076938e822dba40 /src/game/game.cpp
parent7e84d664079d9c407bdf94861825bb05ccf1b0f7 (diff)
feat: implement command mode
Diffstat (limited to 'src/game/game.cpp')
-rw-r--r--src/game/game.cpp248
1 files changed, 235 insertions, 13 deletions
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 3127d40..0b3e85e 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -1,8 +1,12 @@
#include "game.hpp"
#include "engine/data/bounds.hpp"
+#include "engine/escape.hpp"
+#include "game/keycodes.hpp"
#include "util/algorithm.hpp"
+#include "util/string.hpp"
+#include <fmt/color.h>
#include <fmt/core.h>
#include <algorithm>
@@ -24,7 +28,8 @@ Game::Game(
_generation_tracker(std::move(generation_tracker)),
_status_manager(std::move(status_manager)),
_user_input_observer(std::move(user_input_observer)),
- _cell_helper(std::move(cell_helper))
+ _cell_helper(std::move(cell_helper)),
+ _current_mode(Mode::NORMAL)
{
}
@@ -35,7 +40,7 @@ void Game::on_start() noexcept
std::shared_ptr<IStatusLine> statusline =
_statusline_factory(Bounds({.width = scene_size.get_width(), .height = 1}));
- _scene->register_component(statusline, Vector2({0, 0}));
+ _scene->register_component(statusline, Vector2({.x = 0, .y = 1}));
_status_manager->bind(statusline);
@@ -81,11 +86,49 @@ void Game::on_start() noexcept
scene_size.get_width(),
scene_size.get_height()));
- _last_update_time = std::chrono::system_clock::now();
+ _commands["ping"] = CommandInfo(
+ {.option_cnt = 0,
+ .function = [this](CommandInfo::Options /*options*/)
+ {
+ _erase_entire_line();
+
+ const auto position = _cursor_controller->where();
+
+ _cursor_controller->move_to(Vector2({.x = 0, .y = position.get_y()}));
+
+ std::cout << "pong!";
+ std::cout.flush();
+ }});
}
void Game::on_update() noexcept
{
+ if (_current_mode == Mode::COMMAND)
+ {
+ _on_command_mode_update();
+ return;
+ }
+
+ _on_normal_mode_update();
+}
+
+void Game::on_exit() const noexcept
+{
+ for (auto row : *_scene->get_matrix())
+ {
+ for (auto &col : row)
+ {
+ fmt::print("{}", col);
+ }
+
+ fmt::print("\n");
+ }
+
+ std::cout.flush();
+}
+
+void Game::_on_normal_mode_update() noexcept
+{
const auto pressed_key = _user_input_observer->get_currently_pressed_key();
auto cursor_has_moved = false;
@@ -124,6 +167,26 @@ void Game::on_update() noexcept
case 'q':
std::exit(EXIT_SUCCESS);
+ case ':':
+ _last_pos_before_command_mode = _cursor_controller->where();
+
+ _cursor_controller->move_to(Vector2({.x = 0, .y = 0}));
+
+ _erase_entire_line();
+
+ std::cout << ":";
+ std::cout.flush();
+
+ _cursor_controller->update_position(
+ _cursor_controller->where() + Vector2::right());
+
+ _cursor_controller->set_cursor_style(CursorStyle::BlinkingBar);
+
+ _command_mode_input = "";
+
+ _current_mode = Mode::COMMAND;
+ return;
+
case 'i':
{
const auto position = _cursor_controller->where();
@@ -208,7 +271,6 @@ void Game::on_update() noexcept
if (_generation_tracker->get_is_paused() && !is_generation_stepping)
{
- _last_update_time = time_now;
return;
}
@@ -218,7 +280,6 @@ void Game::on_update() noexcept
if (time_since_last_gen.count() <= _min_time_since_last_gen_millis)
{
- _last_update_time = time_now;
return;
}
@@ -261,23 +322,171 @@ void Game::on_update() noexcept
_set_space(matrix, birth_cell_pos, 'x');
_living_cell_positions.push_back(birth_cell_pos);
}
+}
+
+void Game::_on_command_mode_update() noexcept
+{
+ const auto pressed_key = _user_input_observer->get_currently_pressed_key();
+
+ switch (pressed_key)
+ {
+ case keycodes::ESCAPE:
+ _erase_entire_line();
+ _return_to_normal_mode();
+ break;
+
+ case keycodes::ENTER:
+ if (!_command_mode_input.empty())
+ {
+ _run_command(_command_mode_input);
+ }
+
+ if (_current_mode != Mode::NORMAL)
+ {
+ _return_to_normal_mode();
+ }
+
+ break;
+
+ case keycodes::BACKSPACE:
+ {
+ const auto position = _cursor_controller->where();
+
+ const auto input_pos_x = static_cast<uint32_t>(position.get_x()) - 1U;
+
+ if (input_pos_x == 0U)
+ {
+ break;
+ }
+
+ _command_mode_input.erase(input_pos_x - 1U, 1U);
+
+ _cursor_controller->move(Vector2::left(), 1U);
+
+ std::cout.put(' ');
+ std::cout.flush();
+
+ _cursor_controller->update_position(
+ _cursor_controller->where() + Vector2::right());
+
+ _cursor_controller->move(Vector2::left(), 1U);
+
+ _erase_line_from_cursor();
+
+ std::cout << _command_mode_input.substr(input_pos_x - 1U);
+ std::cout.flush();
+
+ _cursor_controller->move_to(position);
+ _cursor_controller->move(Vector2::left(), 1U);
- _last_update_time = time_now;
+ break;
+ }
+
+ case keycodes::LEFT_ARROW:
+ if (_cursor_controller->where().get_x() >= 2)
+ {
+ _cursor_controller->move(Vector2::left(), 1U);
+ }
+ break;
+
+ case keycodes::RIGHT_ARROW:
+ if (static_cast<uint32_t>(_cursor_controller->where().get_x()) - 1U <
+ _command_mode_input.size())
+ {
+ _cursor_controller->move(Vector2::right(), 1U);
+ }
+ break;
+
+ case keycodes::UP_ARROW:
+ case keycodes::DOWN_ARROW:
+ case 0:
+ break;
+
+ default:
+ const auto position = _cursor_controller->where();
+
+ const auto input_pos_x = static_cast<uint32_t>(position.get_x()) - 1U;
+
+ if (input_pos_x < _command_mode_input.length())
+ {
+ _command_mode_input.insert(input_pos_x, 1, pressed_key);
+
+ _erase_line_from_cursor();
+
+ std::cout << pressed_key << _command_mode_input.substr(input_pos_x + 1U);
+ std::cout.flush();
+
+ _cursor_controller->move_to(position + Vector2::right());
+ break;
+ }
+
+ _command_mode_input += pressed_key;
+
+ std::cout << pressed_key;
+ std::cout.flush();
+
+ _cursor_controller->update_position(
+ _cursor_controller->where() + Vector2::right());
+
+ break;
+ }
}
-void Game::on_exit() const noexcept
+void Game::_return_to_normal_mode() noexcept
{
- for (auto row : *_scene->get_matrix())
+ _cursor_controller->move_to(_last_pos_before_command_mode.value_or(
+ Vector2({.x = CURSOR_FALLBACK_POS_X, .y = CURSOR_FALLBACK_POS_Y})));
+
+ _cursor_controller->set_cursor_style(CursorStyle::BlinkingBlock);
+
+ _current_mode = Mode::NORMAL;
+}
+
+void Game::_run_command(const std::string &command) noexcept
+{
+ const auto split_command = split_string<std::vector<std::string>>(command, ' ');
+
+ if (split_command.size() == 1)
{
- for (auto &col : row)
+ if (!_commands.contains(command))
{
- fmt::print("{}", col);
+ _show_command_error(fmt::format("Error: Not a command: {}", command));
+ return;
}
- fmt::print("\n");
+ if (_commands.at(command).option_cnt != 0U)
+ {
+ _show_command_error(
+ fmt::format("Error: Insufficient arguments for command: {}", command));
+ return;
+ }
}
- std::cout.flush();
+ const auto &command_name = split_command[0];
+
+ if (!_commands.contains(command_name))
+ {
+ _show_command_error(fmt::format("Error: Not a command: {}", command));
+ return;
+ }
+
+ const auto &command_info = _commands.at(command_name);
+
+ const auto args =
+ decltype(split_command)(split_command.begin() + 1, split_command.end());
+
+ command_info.function(args);
+}
+
+void Game::_show_command_error(const std::string_view &error_message) noexcept
+{
+ _erase_entire_line();
+
+ const auto position = _cursor_controller->where();
+
+ _cursor_controller->move_to(Vector2({.x = 0, .y = position.get_y()}));
+
+ fmt::print(fmt::fg(fmt::color::red), "{}", error_message);
}
auto Game::_move_cursor(const Vector2 &direction) noexcept -> bool
@@ -293,7 +502,7 @@ auto Game::_move_cursor(const Vector2 &direction) noexcept -> bool
return false;
}
- if (dest_position.get_y() < 1)
+ if (dest_position.get_y() <= 1)
{
return false;
}
@@ -319,3 +528,16 @@ void Game::_set_space(
_cursor_controller->move_to(prev_position);
}
+
+void Game::_erase_entire_line() noexcept
+{
+ fmt::print(ERASE_ENTIRE_LINE, fmt::arg("esc", ESC));
+ std::cout.flush();
+}
+
+void Game::_erase_line_from_cursor() noexcept
+{
+ fmt::print(ERASE_LINE_FROM_CURSOR, fmt::arg("esc", ESC));
+ std::cout.flush();
+}
+