diff --git a/CMakeLists.txt b/CMakeLists.txt index 0532166..3082c53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,10 +11,14 @@ set(CMAKE_CXX_FLAGS, "${CXX_FLAGS}") # Options option(RELEASE_LINUX "Use the final paths" OFF) +option(DEBUG "Print dev information" OFF) if (RELEASE_LINUX) add_definitions(-DRELEASE_LINUX) endif() +if (DEBUG) + add_definitions(-DDEBUG) +endif() # Add SDL2 CMake modules list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/sdl2) @@ -26,23 +30,31 @@ add_executable(${PROJECT_NAME} src/main.cpp src/utils.cpp src/renderer.cpp src/about/window_about.cpp + src/file/file.cpp + src/file/option_file.cpp + src/file/record_file.cpp src/game/target.cpp src/game/dispatcher.cpp - src/game/window_game.cpp + src/game/score.cpp + src/game/window/window_game.cpp + src/game/window/window_gameover.cpp + src/game/window/widget_score.cpp + src/game/window/survival_mode/widget_gauge.cpp + src/game/window/survival_mode/window_survival_mode.cpp + src/game/window/survival_mode/window_welcome_survival.cpp + src/game/window/timer_mode/window_timer_mode.cpp + src/game/window/timer_mode/window_welcome_timer.cpp src/option/window_option.cpp - src/option/option_file.cpp - src/pause/window_pause.cpp - src/score/score.cpp - src/score/widget_score.cpp + src/record/window_record.cpp src/welcome/widget_logo.cpp src/welcome/window_welcome.cpp src/widget/button/widget_selection.cpp src/widget/button/widget_boolean.cpp src/widget/button/widget_list.cpp src/widget/widget_base.cpp - src/widget/widget_menu.cpp src/widget/widget_textbox.cpp - src/widget/widget_window.cpp) + src/widget/window/widget_bottom_menu.cpp + src/widget/window/widget_window.cpp) find_package(Threads) target_link_libraries(${PROJECT_NAME} Threads::Threads) diff --git a/image/example_fast_1.gif b/image/example_fast_1.gif deleted file mode 100644 index 3654dd3..0000000 Binary files a/image/example_fast_1.gif and /dev/null differ diff --git a/image/example_fast_2.gif b/image/example_fast_2.gif deleted file mode 100644 index 293430e..0000000 Binary files a/image/example_fast_2.gif and /dev/null differ diff --git a/image/example_slow_1.gif b/image/example_slow_1.gif deleted file mode 100644 index 26a12d1..0000000 Binary files a/image/example_slow_1.gif and /dev/null differ diff --git a/image/example_slow_2.gif b/image/example_slow_2.gif deleted file mode 100644 index 8d9965e..0000000 Binary files a/image/example_slow_2.gif and /dev/null differ diff --git a/image/example_survival_1.gif b/image/example_survival_1.gif new file mode 100644 index 0000000..99ffdf4 Binary files /dev/null and b/image/example_survival_1.gif differ diff --git a/image/example_survival_2.gif b/image/example_survival_2.gif new file mode 100644 index 0000000..dbe298f Binary files /dev/null and b/image/example_survival_2.gif differ diff --git a/image/example_timer_1.gif b/image/example_timer_1.gif new file mode 100644 index 0000000..179f18d Binary files /dev/null and b/image/example_timer_1.gif differ diff --git a/image/example_timer_2.gif b/image/example_timer_2.gif new file mode 100644 index 0000000..5460348 Binary files /dev/null and b/image/example_timer_2.gif differ diff --git a/readme.md b/readme.md index 179ccb3..3f760d4 100644 --- a/readme.md +++ b/readme.md @@ -6,12 +6,12 @@ Go to options to select the amount of letters, the speed and try to press all of It currently works with QWERTY, AZERTY and BÉPO layouts.
- - + +
- - + +
--- diff --git a/src/about/window_about.cpp b/src/about/window_about.cpp index 4a3e411..6d871db 100644 --- a/src/about/window_about.cpp +++ b/src/about/window_about.cpp @@ -4,10 +4,10 @@ WindowAbout::WindowAbout(kebb::boxsize screen_size, std::shared_ptr renderer) : WidgetWindow(next_window, renderer) { - _widget_menu = std::make_unique(screen_size, renderer, " Quit"); + _widget_menu = std::make_unique(screen_size, renderer, " Quit"); // Geometry - kebb::boxsize char_size = _renderer->font_char_size(FontName::F_Menu); // NOTE: Use font menu ? + kebb::boxsize char_size = _renderer->font_char_size(FontName::F_Menu); kebb::boxsize bs; kebb::point pt; @@ -15,7 +15,7 @@ WindowAbout::WindowAbout(kebb::boxsize screen_size, std::shared_ptr(pt, bs); - _widget_version->set_text("Kebb 1.0"); + _widget_version->set_text("Kebb 1.1"); _widget_version->set_color_text(kebb::color(kebb::ColorName::C_Yellow)); char_size.set_scale(0.5); @@ -44,10 +44,10 @@ WindowAbout::WindowAbout(kebb::boxsize screen_size, std::shared_ptr(pt, bs); - _widget_date->set_text("2023-07-27"); + _widget_date->set_text("2023-08-11"); _widget_date->set_color_text(kebb::color(kebb::ColorName::C_Yellow)); - char_size = _renderer->font_char_size(FontName::F_Menu); // NOTE: Use font menu ? + char_size = _renderer->font_char_size(FontName::F_Menu); bs.w = char_size.w * 36; bs.h = char_size.h; @@ -68,7 +68,7 @@ WindowAbout::WindowAbout(kebb::boxsize screen_size, std::shared_ptrclear_screen(); _widget_title->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); diff --git a/src/about/window_about.h b/src/about/window_about.h index 87d317d..f5089c3 100644 --- a/src/about/window_about.h +++ b/src/about/window_about.h @@ -2,8 +2,8 @@ #define WINDOW_ABOUT #include "utils.h" -#include "widget/widget_menu.h" -#include "widget/widget_window.h" +#include "widget/window/widget_bottom_menu.h" +#include "widget/window/widget_window.h" #include class WindowAbout : public WidgetWindow { @@ -13,7 +13,7 @@ class WindowAbout : public WidgetWindow { virtual ~WindowAbout() override; virtual void control_escape() override; - virtual void render() override; + virtual void render() const override; private: std::unique_ptr _widget_title; @@ -22,7 +22,7 @@ class WindowAbout : public WidgetWindow { std::unique_ptr _widget_github; std::unique_ptr _widget_email; - std::unique_ptr _widget_menu; + std::unique_ptr _widget_menu; }; #endif // !WINDOW_ABOUT diff --git a/src/controller.cpp b/src/controller.cpp index dfce044..81876e2 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -1,6 +1,5 @@ #include "controller.h" -#include "option/option_file.h" -#include +#include Controller::Controller(std::shared_ptr options) : _options(options), _circumflex(false), _grave(false), _diaeresis(false), _mask_mod(0x3FF) {} @@ -9,8 +8,9 @@ void Controller::handle_input(bool &running, std::shared_ptr windo SDL_Event e; while (SDL_PollEvent(&e)) { - if (e.type == SDL_QUIT) { + if (e.type == SDL_QUIT || e.window.event == SDL_WINDOWEVENT_CLOSE) { // TODO: Confirm mouse click on X running = false; + } else if (e.type == SDL_KEYDOWN) { switch (e.key.keysym.sym) { @@ -20,7 +20,7 @@ void Controller::handle_input(bool &running, std::shared_ptr windo break; case SDLK_RETURN: - case SDLK_RETURN2: // NOTE: What is that ? + case SDLK_RETURN2: case SDLK_KP_ENTER: window->control_enter(); break; @@ -52,9 +52,9 @@ void Controller::handle_input(bool &running, std::shared_ptr windo break; default: - if (_options->get(OptionName::Layout) == "US") + if (_options->get().layout == "US") window->control_others(convert_us(e)); - else if (_options->get(OptionName::Layout) == "FR") + else if (_options->get().layout == "FR") window->control_others(convert_fr(e)); else window->control_others(convert_bepo(e)); diff --git a/src/controller.h b/src/controller.h index 1a9a6c4..34978c0 100644 --- a/src/controller.h +++ b/src/controller.h @@ -6,9 +6,10 @@ #include "SDL_keyboard.h" #include "SDL_keycode.h" #include "SDL_stdinc.h" +#include "file/option_file.h" #include "game/target.h" -#include "option/option_file.h" -#include "widget/widget_window.h" +#include "widget/window/widget_window.h" +#include #include #include #include diff --git a/src/file/file.cpp b/src/file/file.cpp new file mode 100644 index 0000000..adc7a64 --- /dev/null +++ b/src/file/file.cpp @@ -0,0 +1,16 @@ +#include "file.h" + +// TODO: Windows path ? +File::File(std::string &&file_name) { + +#ifdef RELEASE_LINUX + std::string path = std::string(getenv("HOME")) + "/.kebb"; +#else + std::string path = "./files_kebb"; +#endif + + if (!std::filesystem::exists(path)) + std::filesystem::create_directory(path); + + _filename = path + "/" + std::move(file_name); +} diff --git a/src/file/file.h b/src/file/file.h new file mode 100644 index 0000000..f24f302 --- /dev/null +++ b/src/file/file.h @@ -0,0 +1,20 @@ +#ifndef FILE_H +#define FILE_H + +#include +#include +#include +#include + +/* + * + */ +class File { +public: + File(std::string &&file_name); + +protected: + std::string _filename; +}; + +#endif // !FILE_H diff --git a/src/file/option_file.cpp b/src/file/option_file.cpp new file mode 100644 index 0000000..6899635 --- /dev/null +++ b/src/file/option_file.cpp @@ -0,0 +1,42 @@ +#include "option_file.h" + +OptionFile::OptionFile() : File("options.kebb") { + + std::ifstream f(_filename); // First launch + if (!f.good()) + save(); + + read(); +} +OptionFile::~OptionFile() { save(); } + +void OptionFile::save() const { + std::ofstream ostrm(_filename); + ostrm << _options.resolution << '\n' + << _options.layout << '\n' + << _options.timer_countdown << '\n' + << _options.timer_nb_targets << '\n' + << _options.timer_speed << '\n' + << _options.survival_nb_targets << '\n' + << _options.survival_speed << '\n' + << _options.last_mode << '\n' + << _options.letters << '\n' + << _options.capitals << '\n' + << _options.numbers << '\n' + << _options.symbols << '\n' + << _options.french_extras << '\n' + << _options.french_extra_caps; +} + +void OptionFile::read() { + std::ifstream istrm(_filename); + istrm >> _options.resolution >> _options.layout >> _options.timer_countdown >> _options.timer_nb_targets >> + _options.timer_speed >> _options.survival_nb_targets >> _options.survival_speed >> _options.last_mode >> + _options.letters >> _options.capitals >> _options.numbers >> _options.symbols >> + _options.french_extras >> _options.french_extra_caps; +} + +// ---------------------------------------------------------------------------------------------------- +// ACCESSORS ------------------------------------------------------------------------------------------ +const Options &OptionFile::get() const { return _options; } +Options &OptionFile::set() { return _options; } diff --git a/src/file/option_file.h b/src/file/option_file.h new file mode 100644 index 0000000..3c4b876 --- /dev/null +++ b/src/file/option_file.h @@ -0,0 +1,44 @@ +#ifndef OPTION_FILE_H +#define OPTION_FILE_H + +#include "file.h" +#include + +struct Options { + std::string resolution = "480-20"; + std::string layout = "US"; + uint16_t timer_countdown = 30; + uint16_t timer_speed = 6; + uint16_t timer_nb_targets = 5; + uint16_t survival_nb_targets = 5; + uint16_t survival_speed = 10; + uint16_t last_mode = 10; + bool letters = true; + bool capitals = false; + bool numbers = true; + bool symbols = false; + bool french_extras = false; + bool french_extra_caps = false; +}; + +/* + * Allows to manage options which are stored in a struct. + * The constructor reads the file to fill the struct. + * Then the destructor write the file with the updated data. + */ +class OptionFile : public File { +public: + OptionFile(); + ~OptionFile(); + + const Options &get() const; + Options &set(); + +private: + Options _options; + + void save() const; + void read(); +}; + +#endif // !OPTION_FILE_H diff --git a/src/file/record_file.cpp b/src/file/record_file.cpp new file mode 100644 index 0000000..a4dde0a --- /dev/null +++ b/src/file/record_file.cpp @@ -0,0 +1,45 @@ +#include "record_file.h" + +RecordFile::RecordFile() : File("records.kebb"), _nb_max_records(15) { read(); } +RecordFile::~RecordFile() { save(); } + +/* + * Add a new record in the array beginnig. + * And delete old record to limit the amount of data. + */ +void RecordFile::add(Record r) { + _records.insert(_records.begin(), r); + while (_records.size() > _nb_max_records) + _records.pop_back(); +} + +/* + * Write the array in the file. + */ +void RecordFile::save() const { + + auto file = std::ofstream(_filename, std::ios::out | std::ios::trunc | std::ios::binary); + for (const auto &r : _records) + file.write((char *)&r, sizeof(Record)); +} + +/* + * Fill the array with the file's data. + */ +void RecordFile::read() { + + auto file = std::ifstream(_filename, std::ios::in | std::ios::binary); + + while (true) { + Record new_record; + file.read((char *)&new_record, sizeof(Record)); + if (new_record.mode != 999) + _records.emplace_back(new_record); + else + break; + } +} + +// ---------------------------------------------------------------------------------------------------- +// ACCESSORS ------------------------------------------------------------------------------------------ +const std::vector &RecordFile::records() { return _records; } diff --git a/src/file/record_file.h b/src/file/record_file.h new file mode 100644 index 0000000..c676507 --- /dev/null +++ b/src/file/record_file.h @@ -0,0 +1,49 @@ +#ifndef RECORD_FILE_H +#define RECORD_FILE_H + +#include "file.h" +#include +#include +#include +#include +#include + +struct Record { + uint16_t mode = 999; + uint16_t status; + uint16_t success; + uint16_t fail; + uint16_t miss; + time_t time_start; + int time_game; + uint16_t survival_nb_targets; + uint16_t survival_speed; + uint16_t survival_level; + uint16_t timer_speed; + uint16_t timer_nb_target; +}; + +/* + * Allow to read/write a file with the previous games data. + * add() to insert the last game in the array beginning. + * The constructor reads the file to fill the array. + * Then the destructor write the file with the (updated) array. + */ +class RecordFile : public File { + +public: + RecordFile(); + ~RecordFile(); + + void add(Record r); + const std::vector &records(); + +private: + uint16_t _nb_max_records; + std::vector _records; + + void save() const; + void read(); +}; + +#endif // !RECORD_FILE_H diff --git a/src/game/dispatcher.cpp b/src/game/dispatcher.cpp index 0ede21b..ae9a47c 100644 --- a/src/game/dispatcher.cpp +++ b/src/game/dispatcher.cpp @@ -2,34 +2,34 @@ Dispatcher::Dispatcher(std::shared_ptr options) : _engine(_seed()), _number_of_chars(0) { - if (std::stoi(options->get(OptionName::Letters))) { + if (options->get().letters) { for (uint16_t i = 10; i < 36; ++i) { _keycodes.emplace_back(i); ++_number_of_chars; } } - if (std::stoi(options->get(OptionName::Capitals))) { + if (options->get().capitals) { for (uint16_t i = 100; i < 126; ++i) { _keycodes.emplace_back(i); ++_number_of_chars; } } - if (std::stoi(options->get(OptionName::Numbers))) { + if (options->get().numbers) { for (uint16_t i = 500; i < 510; ++i) { _keycodes.emplace_back(i); ++_number_of_chars; } } - if (std::stoi(options->get(OptionName::Symbols))) { + if (options->get().symbols) { for (uint16_t i = 1000; i < 1032; ++i) { _keycodes.emplace_back(i); ++_number_of_chars; } } - if (std::stoi(options->get(OptionName::FrenchExtras))) { + if (options->get().french_extras) { uint16_t max = 2016; - if (options->get(OptionName::Layout) == "FR") // Avoid æ œ // NOTE: With other layouts ? + if (options->get().layout == "FR") // Avoid æ œ // NOTE: With other layouts ? max = 2114; for (uint16_t i = 2000; i < 2014; ++i) { @@ -38,10 +38,10 @@ Dispatcher::Dispatcher(std::shared_ptr options) : _engine(_seed()), } } - if (std::stoi(options->get(OptionName::FrenchExtraCaps))) { + if (options->get().french_extra_caps) { uint16_t max = 2115; - if (options->get(OptionName::Layout) == "FR") // Avoid æ œ + if (options->get().layout == "FR") // Avoid æ œ max = 2113; for (uint16_t i = 2100; i < max; ++i) { @@ -50,7 +50,7 @@ Dispatcher::Dispatcher(std::shared_ptr options) : _engine(_seed()), } } - for (uint16_t i = 0; i < 360; i += 5) + for (uint16_t i = 0; i < 360; i += 10) // NOTE: 36 threads maxi ! _angles.emplace_back(i); } diff --git a/src/game/dispatcher.h b/src/game/dispatcher.h index bd5fb9b..13eeeb1 100644 --- a/src/game/dispatcher.h +++ b/src/game/dispatcher.h @@ -1,7 +1,7 @@ #ifndef DISPATCHER_H #define DISPATCHER_H -#include "option/option_file.h" +#include "file/option_file.h" #include #include #include diff --git a/src/score/score.cpp b/src/game/score.cpp similarity index 76% rename from src/score/score.cpp rename to src/game/score.cpp index 26fe142..f2834e8 100644 --- a/src/score/score.cpp +++ b/src/game/score.cpp @@ -1,8 +1,9 @@ #include "score.h" +#include Score::Score() : _sucess(0), _fail(0), _miss(0) {} -void Score::start_timer() { _timer_start = std::chrono::steady_clock().now(); } +void Score::start_timer() { _timer_start = std::chrono::system_clock().now(); } void Score::up_sucess() { std::unique_lock ul(_mutex); @@ -27,10 +28,12 @@ uint16_t Score::success() const { return _sucess; } uint16_t Score::fail() const { return _fail; } uint16_t Score::miss() const { return _miss; } int Score::seconds_spent() { - _timer_last_check = std::chrono::steady_clock().now(); + _timer_last_check = std::chrono::system_clock().now(); return std::chrono::duration_cast(_timer_last_check - _timer_start).count(); } int Score::seconds_until_stop() const { return std::chrono::duration_cast(_timer_last_check - _timer_start).count(); } + +time_t Score::seconds_timer_started() const { return std::chrono::system_clock::to_time_t(_timer_start); } diff --git a/src/score/score.h b/src/game/score.h similarity index 70% rename from src/score/score.h rename to src/game/score.h index 827caa0..e344aef 100644 --- a/src/score/score.h +++ b/src/game/score.h @@ -25,6 +25,7 @@ class Score { void start_timer(); int seconds_spent(); int seconds_until_stop() const; + time_t seconds_timer_started() const; // Time when the timer started (useful for records) private: uint16_t _sucess; @@ -32,8 +33,8 @@ class Score { uint16_t _miss; std::mutex _mutex; - std::chrono::time_point _timer_start; - std::chrono::time_point _timer_last_check; + std::chrono::time_point _timer_start; + std::chrono::time_point _timer_last_check; }; #endif // DEBUG diff --git a/src/game/target.cpp b/src/game/target.cpp index 71e11e6..12deb78 100644 --- a/src/game/target.cpp +++ b/src/game/target.cpp @@ -1,18 +1,23 @@ #include "target.h" // clang-format off -Target::Target(kebb::point center_area, uint16_t radius_area, kebb::boxsize char_size, uint16_t waiting_time, +Target::Target(kebb::point center_area, uint16_t radius_area, kebb::boxsize char_size, uint16_t speed, std::shared_ptr dispatcher, std::shared_ptr score) : WidgetTextBox(center_area, char_size), _active(true), _ok(false), - _center_area(center_area), _radius_area(radius_area), - _waiting_time(waiting_time), - _dispatcher(dispatcher), _score(score), + _center_area(center_area), + _radius_area(radius_area), + _waiting_time(10), + _new_waiting_time(0), + _dispatcher(dispatcher), + _score(score), _move_x(1), _move_y(1), _keycode(0), _angle(-1) // clang-format on { + set_speed(speed); + _green = kebb::color(kebb::ColorName::C_Green); _red = kebb::color(kebb::ColorName::C_Red); _white = kebb::color(kebb::ColorName::C_Text); @@ -20,18 +25,17 @@ Target::Target(kebb::point center_area, uint16_t radius_area, kebb::boxsize char bool Target::check_keycode(uint16_t k) { - if (_keycode == k) { // FIX: Need mutex ?? + if (_keycode == k) { // NOTE: Need mutex ? _ok = true; return true; } return false; } +// ---------------------------------------------------------------------------------------------------- +// THREAD --------------------------------------------------------------------------------------------- void Target::stop() { _active = false; } -/* - * Method for threads - */ void Target::update() { init(); @@ -86,12 +90,19 @@ void Target::update() { } } +void set_waiting_time(uint16_t t) {} + /* * Init the target, set the default position, ask information to the dispatcher * and calculate the new move increments. */ void Target::init() { + if (_new_waiting_time != 0) { + _waiting_time = _new_waiting_time; + _new_waiting_time = 0; + } + _color_text = {_white.r, _white.g, _white.b, 1}; _position.x = _center_area.x - _size.w / 2; _position.y = _center_area.y - _size.h / 2; @@ -106,3 +117,70 @@ void Target::init() { _move_x = std::cos(angle_rad) * 10; _move_y = std::sin(angle_rad) * 10; } + +// ---------------------------------------------------------------------------------------------------- +// TIME ----------------------------------------------------------------------------------------------- +void Target::set_speed(uint16_t t) { + + switch (t) { + case 1: + _new_waiting_time = 38; + break; + case 2: + _new_waiting_time = 34; + break; + case 3: + _new_waiting_time = 30; + break; + case 4: + _new_waiting_time = 28; + break; + case 5: + _new_waiting_time = 26; + break; + case 6: + _new_waiting_time = 24; + break; + case 7: + _new_waiting_time = 22; + break; + case 8: + _new_waiting_time = 20; + break; + case 9: + _new_waiting_time = 18; + break; + case 10: + _new_waiting_time = 16; + break; + case 11: + _new_waiting_time = 14; + break; + case 12: + _new_waiting_time = 12; + break; + case 13: + _new_waiting_time = 10; + break; + case 14: + _new_waiting_time = 8; + break; + case 15: + _new_waiting_time = 6; + break; + case 16: + _new_waiting_time = 5; + break; + case 17: + _new_waiting_time = 4; + break; + case 18: + _new_waiting_time = 3; + break; + case 19: + _new_waiting_time = 2; + break; + default: + _new_waiting_time = 1; + } +} diff --git a/src/game/target.h b/src/game/target.h index 3c43ff2..4ac8e55 100644 --- a/src/game/target.h +++ b/src/game/target.h @@ -3,7 +3,7 @@ #include "SDL.h" #include "dispatcher.h" -#include "score/score.h" +#include "game/score.h" #include "utils.h" #include "widget/widget_base.h" #include "widget/widget_textbox.h" @@ -21,12 +21,14 @@ */ class Target : public WidgetTextBox { public: - Target(kebb::point center_area, uint16_t radius_area, kebb::boxsize char_size, uint16_t waiting_time, + Target(kebb::point center_area, uint16_t radius_area, kebb::boxsize char_size, uint16_t speed, std::shared_ptr dispatcher, std::shared_ptr score); void update(); void stop(); + void set_speed(uint16_t t); // Only updated by init() + bool check_keycode(uint16_t k); private: @@ -40,7 +42,9 @@ class Target : public WidgetTextBox { uint16_t _keycode; uint16_t _angle; - const uint16_t _waiting_time; + uint16_t _waiting_time; + uint16_t _new_waiting_time; + const kebb::point _center_area; const uint16_t _radius_area; diff --git a/src/game/window/survival_mode/widget_gauge.cpp b/src/game/window/survival_mode/widget_gauge.cpp new file mode 100644 index 0000000..e5e90a0 --- /dev/null +++ b/src/game/window/survival_mode/widget_gauge.cpp @@ -0,0 +1,83 @@ +#include "widget_gauge.h" + +WidgetGauge::WidgetGauge(kebb::boxsize screen_size, std::shared_ptr renderer) + : _renderer(renderer), _gauge_alpha(100) { + + _gauge_color = kebb::color(kebb::ColorName::C_Teal); + auto text_color = kebb::color(kebb::ColorName::C_Blue); + + // Geometry + _char_size = _renderer->font_char_size(FontName::F_Game); + _char_size.set_scale(1.5); + kebb::boxsize text_size = {static_cast(_char_size.w * 2), _char_size.h}; // 2 chars for the txt + kebb::boxsize size = {static_cast(_char_size.w * 8), _char_size.h}; // 5.5 chars for the gauge + + uint16_t margin = text_size.h * 0.1; + _pt_insertion = {static_cast(screen_size.w - size.w - margin), + static_cast(screen_size.h - size.h - margin)}; + kebb::point pt_text = {static_cast(_pt_insertion.x + (5.5 * _char_size.w)), _pt_insertion.y}; + + _textbox_level = std::make_unique(pt_text, text_size); + _textbox_level->set_color_text(std::move(text_color)); + _textbox_level->set_text("00"); + + // Gauge -- + _padding_y_txt = _char_size.h * 0.2; + _x_hundred = _char_size.w * 5; + float y_zero = _pt_insertion.y + _char_size.h - _padding_y_txt; + + uint8_t alpha = 70; + + _triangle = { + { + SDL_FPoint{static_cast(_pt_insertion.x), y_zero}, + {_gauge_color.r, _gauge_color.g, _gauge_color.b, _gauge_alpha}, + SDL_FPoint{0}, + }, + { + SDL_FPoint{0, y_zero}, + {_gauge_color.r, _gauge_color.g, _gauge_color.b, _gauge_alpha}, + SDL_FPoint{0}, + }, + { + SDL_FPoint{0, 0}, + {_gauge_color.r, _gauge_color.g, _gauge_color.b, _gauge_alpha}, + SDL_FPoint{0}, + }, + }; +} + +// ---------------------------------------------------------------------------------------------------- +// ACCESSORS ------------------------------------------------------------------------------------------ +void WidgetGauge::set_percentage(uint16_t val) { + + int x, y; + + if (val >= 0 && val <= 100) { + x = val * _x_hundred / 100; + y = (_char_size.h - _padding_y_txt * 2) * (100 - val) / 100; // Reverse the calculation + + _triangle[1].position.x = _pt_insertion.x + x; + _triangle[2].position.x = _pt_insertion.x + x; + _triangle[2].position.y = _pt_insertion.y + _padding_y_txt + y; + } +} +void WidgetGauge::set_level(uint16_t val) { + + auto level = std::to_string(val); + if (level.size() == 1) + level = "0" + level; + + _textbox_level->set_text(std::move(level)); +} + +uint16_t WidgetGauge::get_level() const { return std::stoi(_textbox_level->get_text()); } + +// ---------------------------------------------------------------------------------------------------- +// RENDER --------------------------------------------------------------------------------------------- +void WidgetGauge::render() const { + _textbox_level->render(_renderer->renderer(), _renderer->font(FontName::F_Game)); + + SDL_SetRenderDrawColor(_renderer->renderer(), 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderGeometry(_renderer->renderer(), nullptr, _triangle.data(), _triangle.size(), nullptr, 0); +} diff --git a/src/game/window/survival_mode/widget_gauge.h b/src/game/window/survival_mode/widget_gauge.h new file mode 100644 index 0000000..baed413 --- /dev/null +++ b/src/game/window/survival_mode/widget_gauge.h @@ -0,0 +1,47 @@ +#ifndef WIDGET_GAUGE_H +#define WIDGET_GAUGE_H + +#include "renderer.h" +#include "utils.h" +#include "widget/widget_base.h" +#include "widget/widget_textbox.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Render on the bottom right, a number with two char and a gauge. + * This gauge is a triangle which increases according to the value given in the set_percentage() method. + * The text allows to display the current level. + */ +class WidgetGauge { +public: + WidgetGauge(kebb::boxsize screen_size, std::shared_ptr renderer); + + void render() const; + + void set_percentage(uint16_t val); + void set_level(uint16_t val); + uint16_t get_level() const; + +private: + std::unique_ptr _textbox_level; + std::shared_ptr _renderer; + + std::vector _triangle; + + SDL_Color _gauge_color; + const uint8_t _gauge_alpha; + + kebb::boxsize _char_size; + kebb::point _pt_insertion; // Top left + float _padding_y_txt; // Used to reduce the gauge height + float _x_hundred; // x value for 100% +}; + +#endif // !WIDGET_GAUGE_H diff --git a/src/game/window/survival_mode/window_survival_mode.cpp b/src/game/window/survival_mode/window_survival_mode.cpp new file mode 100644 index 0000000..5d2cc93 --- /dev/null +++ b/src/game/window/survival_mode/window_survival_mode.cpp @@ -0,0 +1,206 @@ +#include "window_survival_mode.h" +#include + +// clang-format off +WindowSurvivalMode::WindowSurvivalMode(kebb::boxsize screen_size, + std::shared_ptr next_window, + std::shared_ptr renderer, + std::shared_ptr records, + std::shared_ptr options) + : WindowGame(screen_size, next_window, renderer, records, options), + _previous_fail(0), + _previous_miss(0), + _previous_success(0), + _points(0) +// clang-format on +{ + // Gauge + _widget_gauge = std::make_unique(screen_size, renderer); + _widget_gauge->set_percentage(100); + + // Levels -- + uint16_t nb_targets = _options->get().survival_nb_targets; + uint16_t speed = _options->get().survival_speed; + uint16_t next_level = 0; + + _price_fail = ((speed + nb_targets) / 3 < 1) ? 1 : (speed + nb_targets) / 3; + _price_miss = ((speed + nb_targets) / 8 < 1) ? 1 : (speed + nb_targets) / 8; + + _max_fail = 10; + _max_miss = 20; + +#ifdef DEBUG + std::cout << "--------------------------------------------------\n"; + std::cout << "- Start with " << nb_targets << " targets - " + << " Speed: " << speed << std::endl; + + std::cout << "- price fail: " << _price_fail << std::endl; + std::cout << "- price miss: " << _price_miss << std::endl; + std::cout << "- max fail: " << _max_fail << std::endl; + std::cout << "- max miss: " << _max_miss << std::endl; +#endif + + for (uint16_t i = 0; i < 12; ++i) { + next_level += (nb_targets + speed) * 4; // NOTE: Add another option ? + + if (i != 0) { + if (i % 2 == 0) + ++speed; + else + ++nb_targets; + } + + _levels.push_back({.speed = speed, .nb_target = nb_targets, .points_next_level = next_level}); + +#ifdef DEBUG + std::cout << " --- Level " << i << ": speed " << speed << " nb: " << nb_targets + << " next: " << next_level << std::endl; +#endif + } + + // First level -- + // Threads + for (uint8_t i = 0; i < _levels[0].nb_target; ++i) + _targets.emplace_back(std::make_shared(_target_center_aera, _target_radius_aera, + _renderer->font_char_size(FontName::F_Target), + _levels[0].speed, _dispatcher, _score)); + + // Start ! + for (auto &t : _targets) + _threads.emplace_back(std::thread(&Target::update, t)); + + _score->reset(); + _score->start_timer(); +} + +WindowSurvivalMode::~WindowSurvivalMode() {} + +// ---------------------------------------------------------------------------------------------------- +// TARGETS -------------------------------------------------------------------------------------------- +void WindowSurvivalMode::add_target(uint16_t waiting_time) { + + if (_targets.size() < _nb_max_target) { + + auto new_target = std::make_shared(Target(_target_center_aera, _target_radius_aera, + _renderer->font_char_size(FontName::F_Target), + waiting_time, _dispatcher, _score)); + + _threads.emplace_back(std::thread(&Target::update, new_target)); + _targets.emplace_back(new_target); + } +} + +void WindowSurvivalMode::remove_target() { + + if (_targets.size() > 0) { + _targets.back()->stop(); + _threads.back().join(); + + _targets.pop_back(); + _threads.pop_back(); + } +} + +// ---------------------------------------------------------------------------------------------------- +// POINTS --------------------------------------------------------------------------------------------- +void WindowSurvivalMode::up_points() { + + auto miss = _score->miss(); + auto fail = _score->fail(); + auto success = _score->success(); + + auto new_miss = (miss > _previous_miss) ? miss - _previous_miss : 0; + auto new_fail = (fail > _previous_fail) ? fail - _previous_fail : 0; + auto new_success = (success > _previous_success) ? success - _previous_success : 0; + + _points -= new_miss * _price_miss; + _points -= new_fail * _price_fail; + _points += new_success; + + if (_points < 0) + _points = 0; + + _previous_miss = miss; + _previous_fail = fail; + _previous_success = success; +} + +// ---------------------------------------------------------------------------------------------------- +// LOGIC ---------------------------------------------------------------------------------------------- +void WindowSurvivalMode::logic() { + + if (_score->miss() >= _max_miss) { + _game_status = kebb::GameStatus::S_Loose; + control_escape(); + } + if (_score->fail() >= _max_fail) { + _game_status = kebb::GameStatus::S_Loose; + control_escape(); + } + + // Find the current level according to the points + up_points(); + + // -- + uint16_t previous_level = 0; + std::vector::iterator it = _levels.begin(); + for (; it != _levels.end(); ++it) { + if ((*it).points_next_level > _points || it == _levels.end() - 1) + break; + + previous_level = (*it).points_next_level; + } + + auto percentage = (_points - previous_level) * 100 / (((*it).points_next_level) - previous_level); + _widget_gauge->set_percentage(percentage); + _widget_gauge->set_level(it - _levels.begin() + 1); + + // Last level ! + if (it == _levels.end() - 1) { + + if (percentage >= 100) { + _game_status = kebb::GameStatus::S_Win; + control_escape(); + } + } else { + + // Number of targets + while (_targets.size() < (*it).nb_target && _targets.size() < _nb_max_target) + add_target((*it).speed); + while (_targets.size() > (*it).nb_target && _targets.size() > 0) + remove_target(); + + // Speed + for (auto &t : _targets) + t->set_speed((*it).speed); + } + + // Up time + _widget_score->logic(_score->seconds_spent()); +} + +// ---------------------------------------------------------------------------------------------------- +// RENDER --------------------------------------------------------------------------------------------- +void WindowSurvivalMode::render() const { + + _renderer->clear_screen(); + _widget_score->render(); + _widget_gauge->render(); + WindowGame::render(); +} + +// ---------------------------------------------------------------------------------------------------- +// RECORDS -------------------------------------------------------------------------------------------- +void WindowSurvivalMode::save_record() const { + + _records->add({.mode = uint16_t(kebb::GameMode::M_Survival), + .status = uint16_t(_game_status), + .success = _score->success(), + .fail = _score->fail(), + .miss = _score->miss(), + .time_start = _score->seconds_timer_started(), + .time_game = _score->seconds_spent(), + .survival_nb_targets = _options->get().survival_nb_targets, + .survival_speed = _options->get().survival_speed, + .survival_level = _widget_gauge->get_level()}); +} diff --git a/src/game/window/survival_mode/window_survival_mode.h b/src/game/window/survival_mode/window_survival_mode.h new file mode 100644 index 0000000..789aad3 --- /dev/null +++ b/src/game/window/survival_mode/window_survival_mode.h @@ -0,0 +1,50 @@ +#ifndef WINDOW_SURVIVAL_MODE_H +#define WINDOW_SURVIVAL_MODE_H + +#include "game/window/survival_mode/widget_gauge.h" +#include "game/window/window_game.h" + +struct Level { + uint16_t speed; + uint16_t nb_target; + uint16_t points_next_level; +}; + +/* + * Window game survival + */ +class WindowSurvivalMode : public WindowGame { +public: + WindowSurvivalMode(kebb::boxsize screen_size, std::shared_ptr next_window, + std::shared_ptr renderer, std::shared_ptr records, + std::shared_ptr options); + + virtual ~WindowSurvivalMode() override; + + virtual void logic() override; + virtual void render() const override; + +private: + void add_target(uint16_t waiting_time); + void remove_target(); + + std::vector _levels; + + void up_points(); + int16_t _points; + uint16_t _price_fail; // Change according to the difficulty + uint16_t _price_miss; + + uint16_t _previous_miss; // Used for level calculations + uint16_t _previous_fail; + uint16_t _previous_success; + + uint16_t _max_fail; + uint16_t _max_miss; + + std::unique_ptr _widget_gauge; + + virtual void save_record() const override; +}; + +#endif // !WINDOW_SURVIVAL_MODE_H diff --git a/src/game/window/survival_mode/window_welcome_survival.cpp b/src/game/window/survival_mode/window_welcome_survival.cpp new file mode 100644 index 0000000..36dc219 --- /dev/null +++ b/src/game/window/survival_mode/window_welcome_survival.cpp @@ -0,0 +1,104 @@ +#include "window_welcome_survival.h" +#include "renderer.h" +#include + +WindowWelcomeSurvival::WindowWelcomeSurvival(kebb::boxsize screen_size, + std::shared_ptr next_window, + std::shared_ptr renderer, + std::shared_ptr options) + : WidgetWindowSelection(next_window, renderer), _options(options) { + + _widget_menu = std::make_unique(screen_size, renderer, " Cancel Go !"); + + // Geometry + kebb::boxsize char_size = _renderer->font_char_size(FontName::F_Menu); + kebb::boxsize bs_title; + kebb::point pt; + + // ------------------------------------------------------------------------ + // Title ------------------------------------------------------------------ + char_size.set_scale(3.1); + bs_title.w = char_size.w * 13; + bs_title.h = char_size.h; + + pt.x = screen_size.w / 2 - bs_title.w / 2; + pt.y = bs_title.h * 0.5; + + _textbox_title = std::make_unique(pt, bs_title); + _textbox_title->set_text("Survival mode"); + _textbox_title->set_color_text(kebb::color(kebb::ColorName::C_Peach)); + + // ------------------------------------------------------------------------ + // Selection fields ------------------------------------------------------- + kebb::boxsize bs = renderer->font_char_size(FontName::F_Menu).scale(1.5); + const uint16_t y_space = bs.h * 1.2; + + pt.x = screen_size.w / 2; + pt.y += bs_title.h * 2; + + _widget_select_fields.emplace_back(std::make_unique(pt, bs, "Nb targets:", 2, 15, 1, true)); + _widget_select_fields.back()->set_choice_by_value(_options->get().survival_nb_targets); + + pt.y += y_space; + _widget_select_fields.emplace_back(std::make_unique(pt, bs, "Speed", 1, 15, 1)); + _widget_select_fields.back()->set_choice_by_value(_options->get().survival_speed); + + pt.y += y_space * 1.8; + + // ------------------------------------------------------------------------ + // Explanations ----------------------------------------------------------- + std::string explanation_l1 = "Select starting options and try"; + std::string explanation_l2 = "to reach the twelfth level !"; + + char_size = _renderer->font_char_size(FontName::F_Menu); + bs.h = char_size.h; + + bs.w = char_size.w * explanation_l1.length(); + pt.x = screen_size.w / 2 - bs.w / 2; + + _textbox_explanation_l1 = std::make_unique(pt, bs); + _textbox_explanation_l1->set_text(std::move(explanation_l1)); + _textbox_explanation_l1->set_color_text(kebb::color(kebb::ColorName::C_Overlay1)); + + pt.y += bs.h; + bs.w = char_size.w * explanation_l2.length(); + pt.x = screen_size.w / 2 - bs.w / 2; + + _textbox_explanation_l2 = std::make_unique(pt, bs); + _textbox_explanation_l2->set_text(std::move(explanation_l2)); + _textbox_explanation_l2->set_color_text(kebb::color(kebb::ColorName::C_Overlay1)); +} + +WindowWelcomeSurvival::~WindowWelcomeSurvival() {} + +// ---------------------------------------------------------------------------------------------------- +// RENDER --------------------------------------------------------------------------------------------- +void WindowWelcomeSurvival::render() const { + + _renderer->clear_screen(); + _textbox_title->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + + for (auto &w : _widget_select_fields) + w->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + + _textbox_explanation_l1->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + _textbox_explanation_l2->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + + _widget_menu->render(); + + // Update Screen + SDL_RenderPresent(_renderer->renderer()); +} + +// ---------------------------------------------------------------------------------------------------- +// CONTROLS ------------------------------------------------------------------------------------------- +void WindowWelcomeSurvival::control_escape() { *_next_window = kebb::WindowName::W_Welcome; } +void WindowWelcomeSurvival::control_enter() { + + // Up options, save and launch the game ! + _options->set().survival_nb_targets = _widget_select_fields[0]->get_choice().value_uint; + _options->set().survival_speed = _widget_select_fields[1]->get_choice().value_uint; + _options->set().last_mode = uint16_t(kebb::GameMode::M_Survival); + + *_next_window = kebb::WindowName::W_GameSurvival; +} diff --git a/src/game/window/survival_mode/window_welcome_survival.h b/src/game/window/survival_mode/window_welcome_survival.h new file mode 100644 index 0000000..232a68d --- /dev/null +++ b/src/game/window/survival_mode/window_welcome_survival.h @@ -0,0 +1,36 @@ +#ifndef WINDOW_WELCOME_SURVIVAL_H +#define WINDOW_WELCOME_SURVIVAL_H + +#include "file/option_file.h" +#include "utils.h" +#include "widget/button/widget_boolean.h" +#include "widget/button/widget_list.h" +#include "widget/button/widget_selection.h" +#include "widget/window/widget_bottom_menu.h" +#include "widget/window/widget_window.h" +#include +#include +#include +#include +#include + +class WindowWelcomeSurvival : public WidgetWindowSelection { +public: + WindowWelcomeSurvival(kebb::boxsize screen_size, std::shared_ptr next_window, + std::shared_ptr renderer, std::shared_ptr options); + virtual ~WindowWelcomeSurvival() override; + + virtual void control_escape() override; + virtual void control_enter() override; + + virtual void render() const override; + +private: + std::unique_ptr _textbox_title; + std::unique_ptr _textbox_explanation_l1; + std::unique_ptr _textbox_explanation_l2; + + std::unique_ptr _widget_menu; + std::shared_ptr _options; +}; +#endif // !WINDOW_WELCOME_SURVIVAL_H diff --git a/src/game/window/timer_mode/window_timer_mode.cpp b/src/game/window/timer_mode/window_timer_mode.cpp new file mode 100644 index 0000000..975df5e --- /dev/null +++ b/src/game/window/timer_mode/window_timer_mode.cpp @@ -0,0 +1,68 @@ +#include "window_timer_mode.h" + +// clang-format off +WindowTimerMode::WindowTimerMode(kebb::boxsize screen_size, + std::shared_ptr next_window, + std::shared_ptr renderer, + std::shared_ptr records, + std::shared_ptr options) + : WindowGame(screen_size, next_window, renderer, records, options) +// clang-format on +{ + + // Limit the amount of threads if needed + uint16_t nb_targets = options->get().timer_nb_targets; + if (nb_targets >= _dispatcher->number_of_chars()) + nb_targets = _dispatcher->number_of_chars() * 0.6; // Remove to create a difficulty + + for (uint8_t i = 0; i < nb_targets; ++i) + _targets.emplace_back(std::make_shared(_target_center_aera, _target_radius_aera, + _renderer->font_char_size(FontName::F_Target), + options->get().timer_speed, _dispatcher, _score)); + + // Start ! + for (auto &t : _targets) + _threads.emplace_back(std::thread(&Target::update, t)); + + _countdown_value = options->get().timer_countdown; + _score->reset(); + _score->start_timer(); +} + +WindowTimerMode::~WindowTimerMode() {} + +// ---------------------------------------------------------------------------------------------------- +// LOGIC ---------------------------------------------------------------------------------------------- +void WindowTimerMode::logic() { + + int16_t time_seconds = _countdown_value - _score->seconds_spent(); + if (time_seconds <= 0) { + _game_status = kebb::GameStatus::S_TimeUp; + control_escape(); + } + + _widget_score->logic(time_seconds); +} +// ---------------------------------------------------------------------------------------------------- +// RENDER --------------------------------------------------------------------------------------------- +void WindowTimerMode::render() const { + + _renderer->clear_screen(); + _widget_score->render(); + WindowGame::render(); +} + +// ---------------------------------------------------------------------------------------------------- +// RECORDS -------------------------------------------------------------------------------------------- +void WindowTimerMode::save_record() const { + + _records->add({.mode = uint16_t(kebb::GameMode::M_Timer), + .status = uint16_t(_game_status), + .success = _score->success(), + .fail = _score->fail(), + .miss = _score->miss(), + .time_start = _score->seconds_timer_started(), + .time_game = _score->seconds_spent(), + .timer_speed = _options->get().timer_speed, + .timer_nb_target = _options->get().timer_nb_targets}); +} diff --git a/src/game/window/timer_mode/window_timer_mode.h b/src/game/window/timer_mode/window_timer_mode.h new file mode 100644 index 0000000..8e661be --- /dev/null +++ b/src/game/window/timer_mode/window_timer_mode.h @@ -0,0 +1,23 @@ +#ifndef WINDOW_TIMER_MODE_H +#define WINDOW_TIMER_MODE_H + +#include "game/window/window_game.h" + +class WindowTimerMode : public WindowGame { +public: + WindowTimerMode(kebb::boxsize screen_size, std::shared_ptr next_window, + std::shared_ptr renderer, std::shared_ptr records, + std::shared_ptr options); + + virtual ~WindowTimerMode() override; + + virtual void logic() override; + virtual void render() const override; + +private: + uint16_t _countdown_value; // Seconds + + virtual void save_record() const override; +}; + +#endif // !WINDOW_TIMER_MODE_H diff --git a/src/game/window/timer_mode/window_welcome_timer.cpp b/src/game/window/timer_mode/window_welcome_timer.cpp new file mode 100644 index 0000000..1e66eaa --- /dev/null +++ b/src/game/window/timer_mode/window_welcome_timer.cpp @@ -0,0 +1,92 @@ +#include "window_welcome_timer.h" + +WindowWelcomeTimer::WindowWelcomeTimer(kebb::boxsize screen_size, + std::shared_ptr next_window, + std::shared_ptr renderer, + std::shared_ptr options) + : WidgetWindowSelection(next_window, renderer), _options(options) { + + _widget_menu = std::make_unique(screen_size, renderer, " Cancel Go !"); + + // Geometry + kebb::boxsize char_size = _renderer->font_char_size(FontName::F_Menu); + kebb::boxsize bs_title; + kebb::point pt; + + // ------------------------------------------------------------------------ + // Title ------------------------------------------------------------------ + char_size.set_scale(3.5); + bs_title.w = char_size.w * 10; + bs_title.h = char_size.h; + + pt.x = screen_size.w / 2 - bs_title.w / 2; + pt.y = bs_title.h * 0.5; + + _textbox_title = std::make_unique(pt, bs_title); + _textbox_title->set_text("Timer mode"); + _textbox_title->set_color_text(kebb::color(kebb::ColorName::C_Peach)); + + // ------------------------------------------------------------------------ + // Selection fields ------------------------------------------------------- + const kebb::boxsize bs_field = renderer->font_char_size(FontName::F_Menu).scale(1.5); + const uint16_t y_space = bs_field.h * 1.2; + + pt.x = screen_size.w / 2; + pt.y += bs_title.h * 2; + + _widget_select_fields.emplace_back( + std::make_unique(pt, bs_field, "Countdown:", + std::vector{{.text = "15s", .value_uint = 15}, + {.text = "30s", .value_uint = 30}, + {.text = "45s", .value_uint = 45}, + {.text = "60s", .value_uint = 60}, + {.text = "1m30", .value_uint = 90}, + {.text = "2m", .value_uint = 120}, + {.text = "2m30", .value_uint = 150}, + {.text = "3m", .value_uint = 180}, + {.text = "3m30", .value_uint = 210}, + {.text = "4m", .value_uint = 240}, + {.text = "4m30", .value_uint = 270}, + {.text = "5m", .value_uint = 300}, + {.text = "10m", .value_uint = 600}}, + true)); // Selected ! + _widget_select_fields.back()->set_choice_by_value(_options->get().timer_countdown); + + pt.y += y_space; + _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "Nb targets:", 1, 20, 1)); + _widget_select_fields.back()->set_choice_by_value(_options->get().timer_nb_targets); + + pt.y += y_space; + _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "Speed", 1, 20, 1)); + _widget_select_fields.back()->set_choice_by_value(_options->get().timer_speed); +} + +WindowWelcomeTimer::~WindowWelcomeTimer() {} + +void WindowWelcomeTimer::render() const { + + _renderer->clear_screen(); + _textbox_title->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + + for (auto &w : _widget_select_fields) + w->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + + _widget_menu->render(); + + // Update Screen + SDL_RenderPresent(_renderer->renderer()); +} + +// ---------------------------------------------------------------------------------------------------- +// CONTROLS ------------------------------------------------------------------------------------------- +void WindowWelcomeTimer::control_escape() { *_next_window = kebb::WindowName::W_Welcome; } +void WindowWelcomeTimer::control_enter() { + + // Up options, save and launch the game ! + _options->set().timer_countdown = _widget_select_fields[0]->get_choice().value_uint; + _options->set().timer_nb_targets = _widget_select_fields[1]->get_choice().value_uint; + _options->set().timer_speed = _widget_select_fields[2]->get_choice().value_uint; + _options->set().last_mode = uint16_t(kebb::GameMode::M_Timer); + + *_next_window = kebb::WindowName::W_GameTimer; +} diff --git a/src/game/window/timer_mode/window_welcome_timer.h b/src/game/window/timer_mode/window_welcome_timer.h new file mode 100644 index 0000000..62c1bcf --- /dev/null +++ b/src/game/window/timer_mode/window_welcome_timer.h @@ -0,0 +1,34 @@ +#ifndef WINDOW_WELCOME_TIMER_H +#define WINDOW_WELCOME_TIMER_H + +#include "file/option_file.h" +#include "utils.h" +#include "widget/button/widget_boolean.h" +#include "widget/button/widget_list.h" +#include "widget/button/widget_selection.h" +#include "widget/window/widget_bottom_menu.h" +#include "widget/window/widget_window.h" +#include +#include +#include +#include +#include +#include + +class WindowWelcomeTimer : public WidgetWindowSelection { +public: + WindowWelcomeTimer(kebb::boxsize screen_size, std::shared_ptr next_window, + std::shared_ptr renderer, std::shared_ptr options); + virtual ~WindowWelcomeTimer() override; + + virtual void control_escape() override; + virtual void control_enter() override; + + virtual void render() const override; + +private: + std::unique_ptr _textbox_title; + std::unique_ptr _widget_menu; + std::shared_ptr _options; +}; +#endif // !WINDOW_WELCOME_TIMER_H diff --git a/src/game/window/widget_score.cpp b/src/game/window/widget_score.cpp new file mode 100644 index 0000000..4207f96 --- /dev/null +++ b/src/game/window/widget_score.cpp @@ -0,0 +1,67 @@ +#include "widget_score.h" + +WidgetScore::WidgetScore(kebb::boxsize screen_size, std::shared_ptr score, + std::shared_ptr renderer) + : _score(score), _renderer(renderer) { + + // Geometry + kebb::boxsize char_size = _renderer->font_char_size(FontName::F_Game); + kebb::boxsize line_size; + kebb::point pt; + + // ------------------------------------------------------------------------ + // Success / Fail / Miss -------------------------------------------------- + line_size.w = char_size.w * 13; + line_size.h = char_size.h; + + pt.x = screen_size.w - line_size.w - char_size.h * 0.1; + pt.y = char_size.h * 0.1; + + _textbox_success = std::make_unique(pt, line_size); + pt.y += char_size.h; + _textbox_fail = std::make_unique(pt, line_size); + pt.y += char_size.h; + _textbox_miss = std::make_unique(pt, line_size); + + // ------------------------------------------------------------------------ + // Timer ------------------------------------------------------------------ + char_size = _renderer->font_char_size(FontName::F_Game); + + // Timer bigger and on the left -- + char_size.set_scale(1.7); + line_size.w = char_size.w * 5; + line_size.h = char_size.h; + + pt.x = char_size.h * 1.5 * 0.2; + pt.y = char_size.h * 1.5 * 0.2; + + _textbox_time = std::make_unique(pt, line_size); + + _textbox_time->set_color_text(kebb::color(kebb::ColorName::C_Peach)); + + _textbox_success->set_color_text(kebb::color(kebb::ColorName::C_Blue)); + _textbox_fail->set_color_text(kebb::color(kebb::ColorName::C_Blue)); + _textbox_miss->set_color_text(kebb::color(kebb::ColorName::C_Blue)); +} + +// ---------------------------------------------------------------------------------------------------- +// LOGIC ---------------------------------------------------------------------------------------------- +void WidgetScore::logic(uint16_t time_seconds) { + + _textbox_success->set_text("Success " + kebb::adapt_string_length(std::to_string(_score->success()), 5)); + _textbox_fail->set_text("Fail " + kebb::adapt_string_length(std::to_string(_score->fail()), 8)); + _textbox_miss->set_text("Miss " + kebb::adapt_string_length(std::to_string(_score->miss()), 8)); + + _textbox_time->set_text(kebb::adapt_string_length(std::to_string(time_seconds / 60), 2, '0') + ":" + + kebb::adapt_string_length(std::to_string(time_seconds % 60), 2, '0')); +} + +// ---------------------------------------------------------------------------------------------------- +// RENDER --------------------------------------------------------------------------------------------- +void WidgetScore::render() const { + + _textbox_time->render(_renderer->renderer(), _renderer->font(FontName::F_Game)); + _textbox_success->render(_renderer->renderer(), _renderer->font(FontName::F_Game)); + _textbox_fail->render(_renderer->renderer(), _renderer->font(FontName::F_Game)); + _textbox_miss->render(_renderer->renderer(), _renderer->font(FontName::F_Game)); +} diff --git a/src/score/widget_score.h b/src/game/window/widget_score.h similarity index 59% rename from src/score/widget_score.h rename to src/game/window/widget_score.h index c5325d3..d8f36d5 100644 --- a/src/score/widget_score.h +++ b/src/game/window/widget_score.h @@ -1,22 +1,18 @@ #ifndef WIDGET_SCORE_H #define WIDGET_SCORE_H +#include "game/score.h" #include "renderer.h" -#include "score.h" #include "utils.h" #include "widget/widget_textbox.h" #include -enum class WidgetScoreType { - FullScreen, - Top, -}; - class WidgetScore { public: - WidgetScore(WidgetScoreType type, kebb::boxsize screen_size, std::shared_ptr score, - std::shared_ptr renderer); - void render(uint16_t time_seconds); + WidgetScore(kebb::boxsize screen_size, std::shared_ptr score, std::shared_ptr renderer); + + void logic(uint16_t time_seconds); + void render() const; private: std::unique_ptr _textbox_time; @@ -26,8 +22,6 @@ class WidgetScore { std::shared_ptr _score; std::shared_ptr _renderer; - - std::string int_to_string(uint16_t i, uint8_t text_length, char c = ' '); }; #endif // !WIDGET_SCORE_H diff --git a/src/game/window/window_game.cpp b/src/game/window/window_game.cpp new file mode 100644 index 0000000..6499187 --- /dev/null +++ b/src/game/window/window_game.cpp @@ -0,0 +1,71 @@ +#include "window_game.h" + +// clang-format off +WindowGame::WindowGame(kebb::boxsize screen_size, + std::shared_ptr next_window, + std::shared_ptr renderer, + std::shared_ptr records, + std::shared_ptr options) + : WidgetWindow(next_window, renderer), + _records(records), + _options(options), + _game_status(kebb::GameStatus::S_Quit) { + // clang-format on + + _dispatcher = std::make_shared(options); + _nb_max_target = _dispatcher->number_of_chars() * 0.6; // Prevent the same targets/thread amount. + + _score = std::make_shared(); + _widget_score = std::make_unique(screen_size, _score, renderer); + + // Geometry + _target_center_aera = {static_cast(screen_size.w / 2), static_cast(screen_size.h / 2)}; + _target_radius_aera = int16_t(screen_size.w * 0.4); +} + +WindowGame::~WindowGame() {} + +void WindowGame::stop_game() { + + for (auto &t : _targets) { + t->stop(); + } + for (auto &t : _threads) { + t.join(); + } + + save_record(); +} + +// ---------------------------------------------------------------------------------------------------- +// RENDER --------------------------------------------------------------------------------------------- +/* + * Call target renders and update screen. + * Children have to clear the screen before calling this method. + */ +void WindowGame::render() const { + + for (auto &target : _targets) + target->render(_renderer->renderer(), _renderer->font(FontName::F_Target)); + + // Update Screen + SDL_RenderPresent(_renderer->renderer()); +} + +// ---------------------------------------------------------------------------------------------------- +// CONTROLS ------------------------------------------------------------------------------------------- +void WindowGame::control_escape() { + stop_game(); + *_next_window = kebb::WindowName::W_GameOver; +} +void WindowGame::control_others(uint16_t keycode) { + + // Loop in all targets, if ok, up the loop + for (auto &target : _targets) { + + if (target->check_keycode(keycode)) + return; + } + + _score->up_fail(); +} diff --git a/src/game/window_game.h b/src/game/window/window_game.h similarity index 52% rename from src/game/window_game.h rename to src/game/window/window_game.h index b682903..567bc13 100644 --- a/src/game/window_game.h +++ b/src/game/window/window_game.h @@ -3,40 +3,53 @@ #include "SDL_render.h" #include "SDL_ttf.h" -#include "option/option_file.h" +#include "file/option_file.h" +#include "file/record_file.h" +#include "game/score.h" +#include "game/target.h" +#include "game/window/widget_score.h" #include "renderer.h" -#include "score/score.h" -#include "score/widget_score.h" -#include "target.h" #include "widget/widget_base.h" -#include "widget/widget_window.h" +#include "widget/window/widget_window.h" #include #include #include +/* + * This class is a mother for window games, it regroups variables, manages controls and give a base for the + * render method. + */ class WindowGame : public WidgetWindow { public: WindowGame(kebb::boxsize screen_size, std::shared_ptr next_window, - std::shared_ptr renderer, std::shared_ptr score, + std::shared_ptr renderer, std::shared_ptr records, std::shared_ptr options); virtual ~WindowGame() override; virtual void control_escape() override; virtual void control_others(uint16_t keycode) override; - virtual void render() override; + virtual void render() const override; - void stop_threads(); + void stop_game(); -private: - std::vector _targets; +protected: + std::vector> _targets; std::vector _threads; std::shared_ptr _dispatcher; + std::shared_ptr _options; + std::shared_ptr _records; std::shared_ptr _score; std::unique_ptr _widget_score; - uint16_t _countdown_value; // Seconds + kebb::point _target_center_aera; + uint16_t _target_radius_aera; + + uint16_t _nb_max_target; + + kebb::GameStatus _game_status; + virtual void save_record() const {}; }; #endif // !WINDOW_GAME_H diff --git a/src/game/window/window_gameover.cpp b/src/game/window/window_gameover.cpp new file mode 100644 index 0000000..8648d34 --- /dev/null +++ b/src/game/window/window_gameover.cpp @@ -0,0 +1,234 @@ +#include "window_gameover.h" + +WindowGameOver::WindowGameOver(kebb::boxsize screen_size, std::shared_ptr next_window, + std::shared_ptr renderer, std::shared_ptr records, + std::shared_ptr options) + : WidgetWindow(next_window, renderer), _records(records), _options(options) { + + // Geometry + kebb::boxsize char_size = _renderer->font_char_size(FontName::F_Menu); + kebb::boxsize bs; + kebb::point pt; + + if (_records->records().empty()) { + + // TODO: Add a message (Cannot be empty) ?? + std::cout << "Record file is empty !" << std::endl; + + } else { + + // ------------------------------------------------------------------------ + // Title ------------------------------------------------------------------ + std::string title; + SDL_Color title_color; + + switch ((kebb::GameStatus)_records->records()[0].status) { + + case kebb::GameStatus::S_Win: + title = "You win"; + title_color = kebb::color(kebb::ColorName::C_Green); + break; + case kebb::GameStatus::S_Loose: + title = "You loose"; + title_color = kebb::color(kebb::ColorName::C_Red); + break; + case kebb::GameStatus::S_TimeUp: + title = "Time's up"; + title_color = kebb::color(kebb::ColorName::C_Green); + break; + default: + title = "Quit"; + title_color = kebb::color(kebb::ColorName::C_Sky); + } + char_size.set_scale(3); + bs.w = char_size.w * title.length(); + bs.h = char_size.h; + + pt.x = screen_size.w / 2 - bs.w / 2; + pt.y = bs.h * 0.2; + + _textbox_title = std::make_unique(pt, bs); + _textbox_title->set_text(std::move(title)); + _textbox_title->set_color_text(std::move(title_color)); + + pt.y += bs.h * 1.3; + + // ------------------------------------------------------------------------ + // Separation ------------------------------------------------------------- + _separation_color = kebb::color(kebb::ColorName::C_Overlay0); + _separation_color.a = 40; + + _separation_0.w = screen_size.w / 2; + _separation_0.h = char_size.h / 10; + _separation_0.x = screen_size.w / 2 - _separation_0.w / 2; + _separation_0.y = pt.y; + + pt.y += _separation_0.h * 2; + + // ------------------------------------------------------------------------ + // Mode ------------------------------------------------------------------- + std::string mode; + if ((kebb::GameMode)_records->records()[0].mode == kebb::GameMode::M_Survival) + mode = "Survival mode"; + else + mode = "Timer mode"; + + char_size = _renderer->font_char_size(FontName::F_Menu); + char_size.set_scale(1.3); + bs.w = char_size.w * mode.length(); + bs.h = char_size.h; + + pt.x = screen_size.w / 2 - bs.w / 2; + pt.y += bs.h * 0.05; + + _textbox_mode = std::make_unique(pt, bs); + _textbox_mode->set_text(std::move(mode)); + _textbox_mode->set_color_text(kebb::color(kebb::ColorName::C_Text)); + + pt.y += bs.h; + + // ------------------------------------------------------------------------ + // Difficulty ------------------------------------------------------------- + std::string difficulty; + if ((kebb::GameMode)_records->records()[0].mode == kebb::GameMode::M_Survival) { + + if (_records->records()[0].survival_speed + _records->records()[0].survival_nb_targets < 5) + difficulty += "Very easy"; + else if (_records->records()[0].survival_speed + _records->records()[0].survival_nb_targets < 10) + difficulty += "Easy"; + else if (_records->records()[0].survival_speed + _records->records()[0].survival_nb_targets < 15) + difficulty += "Normal"; + else if (_records->records()[0].survival_speed + _records->records()[0].survival_nb_targets < 20) + difficulty += "Hard"; + else if (_records->records()[0].survival_speed + _records->records()[0].survival_nb_targets < 25) + difficulty += "Very hard"; + else + difficulty += "Impossible"; + + difficulty += " (" + std::to_string(_records->records()[0].survival_nb_targets) + "/" + + std::to_string(_records->records()[0].survival_speed) + ")"; + } else { + difficulty = std::to_string(_records->records()[0].timer_nb_target) + " targets - speed " + + std::to_string(_records->records()[0].timer_speed); + } + + char_size = _renderer->font_char_size(FontName::F_Menu); + char_size.set_scale(1.2); + bs.w = char_size.w * difficulty.length(); + bs.h = char_size.h; + + pt.x = screen_size.w / 2 - bs.w / 2; + pt.y += bs.h * 0.05; + + _textbox_difficulty = std::make_unique(pt, bs); + _textbox_difficulty->set_text(std::move(difficulty)); + _textbox_difficulty->set_color_text(kebb::color(kebb::ColorName::C_Text)); + + pt.y += bs.h * 1.5; + } + + // ------------------------------------------------------------------------ + // Separation ------------------------------------------------------------- + _separation_1 = _separation_0; + _separation_1.y = pt.y; + + pt.y += _separation_1.h * 2; + + // ------------------------------------------------------------------------ + // Timer ------------------------------------------------------------------ + char_size = _renderer->font_char_size(FontName::F_Menu); + + char_size.set_scale(2.4); + bs.w = char_size.w * 5; + bs.h = char_size.h; + + pt.x = screen_size.w / 2 - bs.w / 2; + pt.y += char_size.h * 0.05; + + _textbox_time = std::make_unique(pt, bs); + _textbox_time->set_color_text(kebb::color(kebb::ColorName::C_Peach)); + + pt.y += bs.h * 1.1; + + // ------------------------------------------------------------------------ + // Success / Fail / Miss -------------------------------------------------- + char_size = _renderer->font_char_size(FontName::F_Menu); + char_size.set_scale(1.5); + + bs.w = char_size.w * 13; // 13 chars in total for all lines to align + bs.h = char_size.h; + + pt.x = screen_size.w / 2 - bs.w / 2; + pt.y += bs.h * 0.05; + + _textbox_success = std::make_unique(pt, bs); + pt.y += char_size.h; + _textbox_fail = std::make_unique(pt, bs); + pt.y += char_size.h; + _textbox_miss = std::make_unique(pt, bs); + + _textbox_success->set_color_text(kebb::color(kebb::ColorName::C_Blue)); + _textbox_fail->set_color_text(kebb::color(kebb::ColorName::C_Blue)); + _textbox_miss->set_color_text(kebb::color(kebb::ColorName::C_Blue)); + + _textbox_success->set_text("Success " + + kebb::adapt_string_length(std::to_string(_records->records()[0].success), 5)); + _textbox_fail->set_text("Fail " + + kebb::adapt_string_length(std::to_string(_records->records()[0].fail), 8)); + _textbox_miss->set_text("Miss " + + kebb::adapt_string_length(std::to_string(_records->records()[0].miss), 8)); + + int t = _records->records()[0].time_game; + _textbox_time->set_text(kebb::adapt_string_length(std::to_string(t / 60), 2, '0') + ":" + + kebb::adapt_string_length(std::to_string(t % 60), 2, '0')); + + // ------------------------------------------------------------------------ + // Menu ------------------------------------------------------------------- + _widget_menu = std::make_unique(screen_size, renderer, " Quit Restart"); +} + +WindowGameOver::~WindowGameOver() {} + +// ---------------------------------------------------------------------------------------------------- +// LOGIC ---------------------------------------------------------------------------------------------- +void WindowGameOver::logic() { + // _widget_score->logic(_score->seconds_until_stop()); +} + +// ---------------------------------------------------------------------------------------------------- +// RENDER --------------------------------------------------------------------------------------------- +void WindowGameOver::render() const { + + _renderer->clear_screen(); + + _textbox_title->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + _textbox_mode->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + _textbox_difficulty->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + + _textbox_time->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + _textbox_success->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + _textbox_fail->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + _textbox_miss->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + + // -- + SDL_SetRenderDrawColor(_renderer->renderer(), _separation_color.r, _separation_color.g, _separation_color.b, + _separation_color.a); + SDL_RenderFillRect(_renderer->renderer(), &_separation_0); + SDL_RenderFillRect(_renderer->renderer(), &_separation_1); + + // -- + _widget_menu->render(); + + // Update Screen + SDL_RenderPresent(_renderer->renderer()); +} + +// ---------------------------------------------------------------------------------------------------- +// CONTROLS ------------------------------------------------------------------------------------------- +void WindowGameOver::control_escape() { *_next_window = kebb::WindowName::W_Welcome; } +void WindowGameOver::control_enter() { + if (_options->get().last_mode == uint16_t(kebb::GameMode::M_Timer)) + *_next_window = kebb::WindowName::W_GameTimer; + else + *_next_window = kebb::WindowName::W_GameSurvival; +} diff --git a/src/game/window/window_gameover.h b/src/game/window/window_gameover.h new file mode 100644 index 0000000..a787e43 --- /dev/null +++ b/src/game/window/window_gameover.h @@ -0,0 +1,48 @@ +#ifndef WINDOW_GAMEOVER_H +#define WINDOW_GAMEOVER_H + +#include "file/option_file.h" +#include "file/record_file.h" +#include "renderer.h" +#include "utils.h" +#include "widget/widget_base.h" +#include "widget/widget_textbox.h" +#include "widget/window/widget_bottom_menu.h" +#include "widget/window/widget_window.h" +#include +#include + +class WindowGameOver : public WidgetWindow { +public: + WindowGameOver(kebb::boxsize screen_size, std::shared_ptr next_window, + std::shared_ptr renderer, std::shared_ptr records, + std::shared_ptr options); + virtual ~WindowGameOver() override; + + virtual void control_escape() override; + virtual void control_enter() override; + + virtual void logic() override; + virtual void render() const override; + +private: + std::shared_ptr _records; + std::shared_ptr _options; + + std::unique_ptr _textbox_title; + std::unique_ptr _textbox_mode; + std::unique_ptr _textbox_difficulty; + + std::unique_ptr _textbox_time; + std::unique_ptr _textbox_success; + std::unique_ptr _textbox_fail; + std::unique_ptr _textbox_miss; + + std::unique_ptr _widget_menu; + + SDL_Rect _separation_0; + SDL_Rect _separation_1; + SDL_Color _separation_color; +}; + +#endif // !WINDOW_GAMEOVER_H diff --git a/src/game/window_game.cpp b/src/game/window_game.cpp deleted file mode 100644 index 2d2df7c..0000000 --- a/src/game/window_game.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "window_game.h" - -// clang-format off -WindowGame::WindowGame(kebb::boxsize screen_size, - std::shared_ptr next_window, - std::shared_ptr renderer, - std::shared_ptr score, - std::shared_ptr options) - : WidgetWindow(next_window, renderer), - _score(score) { - // clang-format on - - _dispatcher = std::make_shared(options); - _widget_score = std::make_unique(WidgetScoreType::Top, screen_size, score, renderer); - - // Geometry - const kebb::point target_center_aera( - {static_cast(screen_size.w / 2), static_cast(screen_size.h / 2)}); - const uint16_t target_radius_aera(int16_t(screen_size.w * 0.4)); - - // Limit the amount of threads if needed - uint16_t nb_targets = std::stoi(options->get(OptionName::Targets)); - if (nb_targets >= _dispatcher->number_of_chars()) - nb_targets = _dispatcher->number_of_chars() - 3; // Remove 3 to create a difficulty - - for (uint8_t i = 0; i < nb_targets; ++i) - _targets.emplace_back(Target(target_center_aera, target_radius_aera, - _renderer->font_char_size(FontName::F_Target), - std::stoi(options->get(OptionName::Speed)), _dispatcher, _score)); - - // Start ! - for (auto &t : _targets) - _threads.emplace_back(std::thread(&Target::update, &t)); - - _countdown_value = std::stoi(options->get(OptionName::Countdown)); - _score->reset(); - _score->start_timer(); -} - -WindowGame::~WindowGame() {} - -void WindowGame::stop_threads() { - - for (auto &t : _targets) { - t.stop(); - } - for (auto &t : _threads) { - t.join(); - } -} - -// ---------------------------------------------------------------------------------------------------- -// RENDER --------------------------------------------------------------------------------------------- -void WindowGame::render() { - - // Up timer - int16_t time_seconds = _countdown_value - _score->seconds_spent(); - if (time_seconds <= 0) { - control_escape(); - } - - _renderer->clear_screen(); - _widget_score->render(time_seconds); - - for (auto &target : _targets) - target.render(_renderer->renderer(), _renderer->font(FontName::F_Target)); - - // Update Screen - SDL_RenderPresent(_renderer->renderer()); -} - -// ---------------------------------------------------------------------------------------------------- -// CONTROLS ------------------------------------------------------------------------------------------- -void WindowGame::control_escape() { - stop_threads(); - *_next_window = kebb::WindowName::W_Pause; -} -void WindowGame::control_others(uint16_t keycode) { - - // Loop in all targets, if ok, up the loop - for (auto &target : _targets) { - - if (target.check_keycode(keycode)) - return; - } - - _score->up_fail(); -} diff --git a/src/loop.cpp b/src/loop.cpp index 88ccccd..df3b892 100644 --- a/src/loop.cpp +++ b/src/loop.cpp @@ -1,14 +1,13 @@ #include "loop.h" // clang-format off -Loop::Loop(kebb::boxsize screen_size, std::shared_ptr score, - std::shared_ptr renderer, std::shared_ptr options) : +Loop::Loop(kebb::boxsize screen_size, std::shared_ptr renderer, + std::shared_ptr options) : _screen_size(screen_size), - _score(score), _renderer(renderer), _options(options) { - _dispatcher = std::make_shared(_options); + _records = std::make_shared(); } // clang-format on @@ -19,12 +18,13 @@ void Loop::run(Controller &controller) { bool running = true; auto next_window_name = std::make_shared(kebb::WindowName::W_None); - _current_window = std::make_shared(_screen_size, next_window_name, _renderer); + _current_window = std::make_shared(_screen_size, next_window_name, _renderer, _options); // Main game loop. while (running) { controller.handle_input(running, _current_window); + _current_window->logic(); _current_window->render(); // Window management @@ -33,21 +33,38 @@ void Loop::run(Controller &controller) { _current_window.reset(); // NOTE: Mandatory/usefull or not ? switch (*next_window_name) { - case kebb::WindowName::W_Game: + case kebb::WindowName::W_About: + _current_window = std::make_shared(_screen_size, next_window_name, _renderer); + break; + case kebb::WindowName::W_GameOver: _current_window = - std::make_shared(_screen_size, next_window_name, _renderer, _score, _options); + std::make_shared(_screen_size, next_window_name, _renderer, _records, _options); break; - case kebb::WindowName::W_Pause: - _current_window = std::make_shared(_screen_size, next_window_name, _renderer, _score); + case kebb::WindowName::W_GameSurvival: + _current_window = std::make_shared(_screen_size, next_window_name, _renderer, + _records, _options); break; - case kebb::WindowName::W_Welcome: - _current_window = std::make_shared(_screen_size, next_window_name, _renderer); + case kebb::WindowName::W_GameTimer: + _current_window = + std::make_shared(_screen_size, next_window_name, _renderer, _records, _options); break; case kebb::WindowName::W_Option: _current_window = std::make_shared(_screen_size, next_window_name, _renderer, _options); break; - case kebb::WindowName::W_About: - _current_window = std::make_shared(_screen_size, next_window_name, _renderer); + case kebb::WindowName::W_Record: + _current_window = std::make_shared(_screen_size, next_window_name, _renderer, _records); + break; + case kebb::WindowName::W_Welcome: + _current_window = + std::make_shared(_screen_size, next_window_name, _renderer, _options); + break; + case kebb::WindowName::W_WelcomeSurvival: + _current_window = + std::make_shared(_screen_size, next_window_name, _renderer, _options); + break; + case kebb::WindowName::W_WelcomeTimer: + _current_window = + std::make_shared(_screen_size, next_window_name, _renderer, _options); break; default: // W_Ouit running = false; @@ -67,6 +84,6 @@ void Loop::run(Controller &controller) { title_timestamp = frame_end; } - SDL_Delay(3); // NOTE: Is 3 ok ? + SDL_Delay(5); // NOTE: Is 5 ok ? } } diff --git a/src/loop.h b/src/loop.h index 42fecf6..ae38d24 100644 --- a/src/loop.h +++ b/src/loop.h @@ -7,35 +7,31 @@ #include "SDL_ttf.h" #include "about/window_about.h" #include "controller.h" -#include "game/dispatcher.h" -#include "game/target.h" -#include "game/window_game.h" -#include "option/option_file.h" +#include "file/option_file.h" +#include "file/record_file.h" +#include "game/window/survival_mode/window_survival_mode.h" +#include "game/window/survival_mode/window_welcome_survival.h" +#include "game/window/timer_mode/window_timer_mode.h" +#include "game/window/timer_mode/window_welcome_timer.h" +#include "game/window/window_gameover.h" #include "option/window_option.h" -#include "pause/window_pause.h" +#include "record/window_record.h" #include "renderer.h" -#include "score/score.h" #include "welcome/window_welcome.h" -#include "widget/widget_base.h" -#include "widget/widget_window.h" -#include class Loop { public: - Loop(kebb::boxsize screen_size, std::shared_ptr score, std::shared_ptr renderer, - std::shared_ptr options); + Loop(kebb::boxsize screen_size, std::shared_ptr renderer, std::shared_ptr options); void run(Controller &controller); private: std::shared_ptr _current_window; // Shared with the controller - std::vector _targets; - std::shared_ptr _dispatcher; - std::shared_ptr _score; - std::shared_ptr _renderer; + std::shared_ptr _records; // Created here to avoid repetitive read/write actions std::shared_ptr _options; + std::shared_ptr _renderer; const kebb::boxsize _screen_size; }; diff --git a/src/main.cpp b/src/main.cpp index 876698c..04a5168 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,6 @@ #include "controller.h" +#include "file/option_file.h" #include "loop.h" -#include "option/option_file.h" #include "renderer.h" #include "utils.h" #include "widget/widget_base.h" @@ -11,10 +11,9 @@ int main() { auto options = std::make_shared(); - options->read(); // Resolution & scale -- - auto resolution_option = options->get(OptionName::Resolution); + auto resolution_option = options->get().resolution; auto hyphen_position = resolution_option.find('-'); uint16_t res = std::stoi(resolution_option.substr(0, hyphen_position)); @@ -28,12 +27,13 @@ int main() { auto renderer = std::make_shared(screen_size, scale_factor, target_font_size, score_font_size, menu_font_size); - auto score = std::make_shared(); - Controller controller(options); - Loop game(screen_size.scale(scale_factor), score, renderer, options); - game.run(controller); + if (renderer->init_ok()) { + + Controller controller(options); + Loop game(screen_size.scale(scale_factor), renderer, options); + game.run(controller); + } - // std::cout << "Kebb has terminated successfully!\n"; return 0; } diff --git a/src/option/option_file.cpp b/src/option/option_file.cpp deleted file mode 100644 index 1474d11..0000000 --- a/src/option/option_file.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "option_file.h" - -OptionFile::OptionFile() - : _resolution("480-20"), _layout("US"), _nb_targets("5"), _countdown("30"), _speed("20"), _letters("1"), - _capitals("0"), _numbers("1"), _symbols("0"), _french_extras("0"), _french_extra_caps("0") { - -#ifdef RELEASE_LINUX - _filename = std::string(getenv("HOME")) + "/.kebb"; -#else - _filename = std::string("./options.kebb"); -#endif - - std::ifstream f(_filename); // First launch - if (!f.good()) - save(); -} - -void OptionFile::save() const { - std::ofstream ostrm(_filename); - ostrm << _resolution << '\n' - << _layout << '\n' - << _nb_targets << '\n' - << _countdown << '\n' - << _speed << '\n' - << _letters << '\n' - << _capitals << '\n' - << _numbers << '\n' - << _symbols << '\n' - << _french_extras << '\n' - << _french_extra_caps; -} - -void OptionFile::read() { - std::ifstream istrm(_filename); - istrm >> _resolution >> _layout >> _nb_targets >> _countdown >> _speed >> _letters >> _capitals >> - _numbers >> _symbols >> _french_extras >> _french_extra_caps; -} - -// ---------------------------------------------------------------------------------------------------- -// ASSESSORS ------------------------------------------------------------------------------------------ -void OptionFile::set(OptionName type, const std::string &val) { - switch (type) { - case OptionName::Resolution: - _resolution = val; - break; - case OptionName::Layout: - _layout = val; - break; - case OptionName::Targets: - _nb_targets = val; - break; - case OptionName::Countdown: - _countdown = val; - break; - case OptionName::Speed: - _speed = val; - break; - case OptionName::Letters: - _letters = val; - break; - case OptionName::Capitals: - _capitals = val; - break; - case OptionName::Numbers: - _numbers = val; - break; - case OptionName::Symbols: - _symbols = val; - break; - case OptionName::FrenchExtras: - _french_extras = val; - break; - default: - _french_extra_caps = val; - } -} - -std::string OptionFile::get(OptionName name) { - switch (name) { - case OptionName::Resolution: - return _resolution; - case OptionName::Layout: - return _layout; - case OptionName::Targets: - return _nb_targets; - case OptionName::Countdown: - return _countdown; - case OptionName::Speed: - return _speed; - case OptionName::Letters: - return _letters; - case OptionName::Capitals: - return _capitals; - case OptionName::Numbers: - return _numbers; - case OptionName::Symbols: - return _symbols; - case OptionName::FrenchExtras: - return _french_extras; - default: - return _french_extra_caps; - } -} diff --git a/src/option/option_file.h b/src/option/option_file.h deleted file mode 100644 index 234db95..0000000 --- a/src/option/option_file.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef OPTION_FILE_H -#define OPTION_FILE_H - -#include -#include -#include - -enum class OptionName { - Resolution, - Layout, - Targets, - Countdown, - Speed, - Letters, - Capitals, - Numbers, - Symbols, - FrenchExtras, - FrenchExtraCaps, -}; - -/* - * Allows to read and save the option file. - * All entries are saved in string format. - */ -class OptionFile { -public: - OptionFile(); - - void save() const; - void read(); - - void set(OptionName type, const std::string &val); - std::string get(OptionName type); - -private: - std::string _filename; - - std::string _resolution; - std::string _layout; - - std::string _nb_targets; - std::string _countdown; - std::string _speed; - - std::string _letters; - std::string _capitals; - std::string _numbers; - std::string _symbols; - std::string _french_extras; - std::string _french_extra_caps; -}; - -#endif // !OPTION_FILE_H diff --git a/src/option/window_option.cpp b/src/option/window_option.cpp index cc704ec..4efcd74 100644 --- a/src/option/window_option.cpp +++ b/src/option/window_option.cpp @@ -5,17 +5,17 @@ WindowOption::WindowOption(kebb::boxsize screen_size, std::shared_ptr(screen_size, renderer, " Cancel Save"); + _widget_menu = std::make_unique(screen_size, renderer, " Cancel Save"); // Geometry - kebb::boxsize char_size = _renderer->font_char_size(FontName::F_Menu); // NOTE: Use font menu ? + kebb::boxsize char_size = _renderer->font_char_size(FontName::F_Menu); kebb::boxsize bs_title; kebb::point pt; // ------------------------------------------------------------------------ // Title ------------------------------------------------------------------ - char_size.set_scale(2.5); - bs_title.w = char_size.w * 4; + char_size.set_scale(3); + bs_title.w = char_size.w * 7; bs_title.h = char_size.h; pt.x = screen_size.w / 2 - bs_title.w / 2; @@ -27,94 +27,58 @@ WindowOption::WindowOption(kebb::boxsize screen_size, std::shared_ptrfont_char_size(FontName::F_Menu).scale(0.85); - const uint16_t y_long_space = bs_field.h * 1.9; + const kebb::boxsize bs_field = renderer->font_char_size(FontName::F_Menu).scale(1.1); + const uint16_t y_long_space = bs_field.h * 2; const uint16_t y_medium_space = bs_field.h * 1.5; const uint16_t y_small_space = bs_field.h * 1.03; pt.x = screen_size.w / 2; pt.y += bs_title.h * 1.5; - _widget_select_fields.emplace_back(std::make_unique( - pt, bs_field, "Resolution:", - std::vector{{"480x480", "480-20"}, - {"640x640", "640-15"}, - {"800x800", "800-10"}, - {"1024x1024", "1024-5"}}, // NOTE: Are scales ok ? - true)); - _widget_select_fields.back()->set_choice_by_value(_options->get(OptionName::Resolution)); + _widget_select_fields.emplace_back( + std::make_unique(pt, bs_field, "Resolution:", + // std::vector{{"480x480", "480-25"}, // Logic: ~12 000 + // {"640x640", "640-19"}, + // {"800x800", "800-15"}, + // {"1024x1024", "1024-12"}}, + + std::vector{{"480x480", "480-20"}, // Logic: ~9 600 + {"640x640", "640-15"}, + {"800x800", "800-12"}, + {"1024x1024", "1024-9"}}, + + true)); + _widget_select_fields.back()->set_choice_by_value(_options->get().resolution); pt.y += y_small_space; _widget_select_fields.emplace_back(std::make_unique( pt, bs_field, "Keyboard layout:", std::vector{{"QWERTY", "US"}, {"AZERTY", "FR"}, {"BEPO (beta)", "BEPO"}})); - _widget_select_fields.back()->set_choice_by_value(_options->get(OptionName::Layout)); - - pt.y += y_long_space; - _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "Nb targets:", 1, 20, 1)); - _widget_select_fields.back()->set_choice_by_value(_options->get(OptionName::Targets)); - - pt.y += y_small_space; - _widget_select_fields.emplace_back( - std::make_unique(pt, bs_field, "Countdown:", - std::vector{{"15s", "15"}, - {"30s", "30"}, - {"45s", "45"}, - {"60s", "60"}, - {"1m30", "90"}, - {"2m", "120"}, - {"2m30", "150"}, - {"3m", "180"}, - {"3m30", "210"}, - {"4m", "240"}, - {"4m30", "270"}, - {"5m", "300"}, - {"10m", "600"}})); - _widget_select_fields.back()->set_choice_by_value(_options->get(OptionName::Countdown)); - - pt.y += y_small_space; - _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "Speed:", - std::vector{{"1", "30"}, - {"2", "28"}, - {"3", "26"}, - {"4", "24"}, - {"5", "22"}, - {"6", "20"}, - {"7", "18"}, - {"8", "16"}, - {"9", "14"}, - {"10", "12"}, - {"11", "10"}, - {"12", "8"}, - {"13", "6"}, - {"14", "4"}, - {"15", "2"}, - {"16", "1"}})); - _widget_select_fields.back()->set_choice_by_value(_options->get(OptionName::Speed)); + _widget_select_fields.back()->set_choice_by_value(_options->get().layout); pt.y += y_long_space; _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "Letters")); - _widget_select_fields.back()->set_bool(std::stoi(_options->get(OptionName::Letters))); + _widget_select_fields.back()->set_bool(_options->get().letters); pt.y += y_small_space; _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "Capitals")); - _widget_select_fields.back()->set_bool(std::stoi(_options->get(OptionName::Capitals))); + _widget_select_fields.back()->set_bool(_options->get().capitals); pt.y += y_small_space; _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "Numbers")); - _widget_select_fields.back()->set_bool(std::stoi(_options->get(OptionName::Numbers))); + _widget_select_fields.back()->set_bool(_options->get().numbers); pt.y += y_small_space; _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "Symbols")); - _widget_select_fields.back()->set_bool(std::stoi(_options->get(OptionName::Symbols))); + _widget_select_fields.back()->set_bool(_options->get().symbols); pt.y += y_medium_space; _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "French extras")); - _widget_select_fields.back()->set_bool(std::stoi(_options->get(OptionName::FrenchExtras))); + _widget_select_fields.back()->set_bool(_options->get().french_extras); pt.y += y_small_space; _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "French extra caps")); - _widget_select_fields.back()->set_bool(std::stoi(_options->get(OptionName::FrenchExtraCaps))); + _widget_select_fields.back()->set_bool(_options->get().french_extra_caps); // ------------------------------------------------------------------------ // Message ---------------------------------------------------------------- @@ -129,7 +93,7 @@ WindowOption::WindowOption(kebb::boxsize screen_size, std::shared_ptrclear_screen(); _widget_title->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); @@ -164,15 +128,15 @@ void WindowOption::display_message(std::string &&message) { void WindowOption::check_new_resolution() { - if (_options->get(OptionName::Resolution) != _widget_select_fields[0]->get_choice().value) { + if (_options->get().resolution != _widget_select_fields[0]->get_choice().value_string) { display_message(" Restart the application to set the new resolution. "); _message_displayed = true; } } void WindowOption::check_french_extra() { - if ((_widget_select_fields[9]->get_bool() || _widget_select_fields[10]->get_bool()) && - _widget_select_fields[1]->get_choice().value == "US") { + if ((_widget_select_fields[6]->get_bool() || _widget_select_fields[7]->get_bool()) && + _widget_select_fields[1]->get_choice().value_string == "US") { display_message(" French extras requier the US Altgr-intl layout "); _message_displayed = true; } @@ -183,24 +147,19 @@ void WindowOption::check_french_extra() { void WindowOption::control_escape() { *_next_window = kebb::WindowName::W_Welcome; } void WindowOption::control_enter() { // Use has to select at least one target type - if (_widget_select_fields[5]->get_bool() == true || _widget_select_fields[6]->get_bool() == true || - _widget_select_fields[7]->get_bool() == true || _widget_select_fields[8]->get_bool() == true || - _widget_select_fields[9]->get_bool() == true || _widget_select_fields[10]->get_bool() == true) { + if (_widget_select_fields[2]->get_bool() == true || _widget_select_fields[3]->get_bool() == true || + _widget_select_fields[4]->get_bool() == true || _widget_select_fields[4]->get_bool() == true || + _widget_select_fields[5]->get_bool() == true || _widget_select_fields[6]->get_bool() == true) { // Up options, save and quit - _options->set(OptionName::Resolution, _widget_select_fields[0]->get_choice().value); - _options->set(OptionName::Layout, _widget_select_fields[1]->get_choice().value); - _options->set(OptionName::Targets, _widget_select_fields[2]->get_choice().value); - _options->set(OptionName::Countdown, _widget_select_fields[3]->get_choice().value); - _options->set(OptionName::Speed, _widget_select_fields[4]->get_choice().value); - _options->set(OptionName::Letters, std::to_string(_widget_select_fields[5]->get_bool())); - _options->set(OptionName::Capitals, std::to_string(_widget_select_fields[6]->get_bool())); - _options->set(OptionName::Numbers, std::to_string(_widget_select_fields[7]->get_bool())); - _options->set(OptionName::Symbols, std::to_string(_widget_select_fields[8]->get_bool())); - _options->set(OptionName::FrenchExtras, std::to_string(_widget_select_fields[9]->get_bool())); - _options->set(OptionName::FrenchExtraCaps, std::to_string(_widget_select_fields[10]->get_bool())); - - _options->save(); + _options->set().resolution = _widget_select_fields[0]->get_choice().value_string; + _options->set().layout = _widget_select_fields[1]->get_choice().value_string; + _options->set().letters = _widget_select_fields[2]->get_bool(); + _options->set().capitals = _widget_select_fields[3]->get_bool(); + _options->set().numbers = _widget_select_fields[4]->get_bool(); + _options->set().symbols = _widget_select_fields[5]->get_bool(); + _options->set().french_extras = _widget_select_fields[6]->get_bool(); + _options->set().french_extra_caps = _widget_select_fields[7]->get_bool(); *_next_window = kebb::WindowName::W_Welcome; } else diff --git a/src/option/window_option.h b/src/option/window_option.h index 28f0587..03078cb 100644 --- a/src/option/window_option.h +++ b/src/option/window_option.h @@ -1,13 +1,13 @@ #ifndef WINDOW_OPTION_H #define WINDOW_OPTION_H -#include "option/option_file.h" +#include "file/option_file.h" #include "utils.h" #include "widget/button/widget_boolean.h" #include "widget/button/widget_list.h" #include "widget/button/widget_selection.h" -#include "widget/widget_menu.h" -#include "widget/widget_window.h" +#include "widget/window/widget_bottom_menu.h" +#include "widget/window/widget_window.h" #include #include #include @@ -24,12 +24,12 @@ class WindowOption : public WidgetWindowSelection { virtual void control_left() override; virtual void control_right() override; - virtual void render() override; + virtual void render() const override; private: std::unique_ptr _widget_title; std::unique_ptr _widget_message; - std::unique_ptr _widget_menu; + std::unique_ptr _widget_menu; std::shared_ptr _options; // Help message diff --git a/src/pause/window_pause.cpp b/src/pause/window_pause.cpp deleted file mode 100644 index 26f4fda..0000000 --- a/src/pause/window_pause.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "window_pause.h" - -WindowPause::WindowPause(kebb::boxsize screen_size, std::shared_ptr next_window, - std::shared_ptr renderer, std::shared_ptr score) - : WidgetWindow(next_window, renderer), _score(score) { - - _widget_score = std::make_unique(WidgetScoreType::FullScreen, screen_size, score, renderer); - _widget_menu = std::make_unique(screen_size, renderer, " Quit Restart"); -} - -WindowPause::~WindowPause() {} - -void WindowPause::render() { - - _renderer->clear_screen(); - - // Reverse the timer - _widget_score->render(_score->seconds_until_stop()); - _widget_menu->render(); - - // Update Screen - SDL_RenderPresent(_renderer->renderer()); -} - -// ---------------------------------------------------------------------------------------------------- -// CONTROLS ------------------------------------------------------------------------------------------- -void WindowPause::control_escape() { *_next_window = kebb::WindowName::W_Welcome; } -void WindowPause::control_enter() { *_next_window = kebb::WindowName::W_Game; } diff --git a/src/pause/window_pause.h b/src/pause/window_pause.h deleted file mode 100644 index 2b1d53e..0000000 --- a/src/pause/window_pause.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef WINDOW_PAUSE_H -#define WINDOW_PAUSE_H - -#include "renderer.h" -#include "score/score.h" -#include "score/widget_score.h" -#include "widget/widget_base.h" -#include "widget/widget_menu.h" -#include "widget/widget_window.h" -#include - -class WindowPause : public WidgetWindow { -public: - WindowPause(kebb::boxsize screen_size, std::shared_ptr next_window, - std::shared_ptr renderer, std::shared_ptr score); - virtual ~WindowPause() override; - - virtual void control_escape() override; - virtual void control_enter() override; - - virtual void render() override; - -private: - std::shared_ptr _score; - std::unique_ptr _widget_score; - std::unique_ptr _widget_menu; -}; - -#endif // !WINDOW_PAUSE_H diff --git a/src/record/window_record.cpp b/src/record/window_record.cpp new file mode 100644 index 0000000..ffabae9 --- /dev/null +++ b/src/record/window_record.cpp @@ -0,0 +1,135 @@ +#include "window_record.h" +#include + +WindowRecord::WindowRecord(kebb::boxsize screen_size, std::shared_ptr next_window, + std::shared_ptr renderer, std::shared_ptr records) + : WidgetWindow(next_window, renderer), _records(records) { + + _widget_menu = std::make_unique(screen_size, renderer, " Quit"); + + // Geometry + kebb::boxsize char_size = _renderer->font_char_size(FontName::F_Menu); + kebb::boxsize bs; + kebb::point pt; + + // ------------------------------------------------------------------------ + // Title ------------------------------------------------------------------ + char_size.set_scale(3); + bs.w = char_size.w * 7; + bs.h = char_size.h; + + pt.x = screen_size.w / 2 - bs.w / 2; + pt.y = bs.h * 0.05; + + _widget_title = std::make_unique(pt, bs); + _widget_title->set_text("Records"); + _widget_title->set_color_text(kebb::color(kebb::ColorName::C_Peach)); + + pt.y += bs.h * 0.9; + + // ------------------------------------------------------------------------ + // Text ------------------------------------------------------------------- + char_size = _renderer->font_char_size(FontName::F_Menu).scale(1.2); + + bs.w = char_size.w * 18; + bs.h = char_size.h; + + pt.x = screen_size.w / 2 - bs.w / 2; + + _widget_title2 = std::make_unique(pt, bs); + _widget_title2->set_text("Last fifteen games"); + _widget_title2->set_color_text(kebb::color(kebb::ColorName::C_Peach)); + + pt.y += bs.h * 1.4; + + // ------------------------------------------------------------------------ + // Entries ---------------------------------------------------------------- + char_size = _renderer->font_char_size(FontName::F_Menu).scale(0.75); + bs.h = char_size.h; + + for (const auto &e : _records->records()) { + + std::string text; + + // Date + struct tm *timeinfo; + char buffer[9]; + + timeinfo = localtime(&e.time_start); + strftime(buffer, 9, "%y/%m/%d", timeinfo); + text += buffer; + text += " "; + + // Text - mode & difficulty + if ((kebb::GameMode)e.mode == kebb::GameMode::M_Survival) { + text += "Survival: " + std::to_string(e.survival_nb_targets) + "/" + std::to_string(e.survival_speed); + text += " lvl " + std::to_string(e.survival_level); + + } else + text += "Timer: " + std::to_string(e.timer_nb_target) + "/" + std::to_string(e.timer_speed); + + // Text - status + switch ((kebb::GameStatus)e.status) { + case kebb::GameStatus::S_Win: + text += " WON "; + break; + case kebb::GameStatus::S_Loose: + text += " LOST "; + break; + case kebb::GameStatus::S_TimeUp: + text += " TIMEUP "; + break; + default: + text += " QUIT "; + break; + } + + // Time + text += kebb::adapt_string_length(std::to_string(e.time_game / 60), 2, '0') + ":" + + kebb::adapt_string_length(std::to_string(e.time_game % 60), 2, '0'); + + // Text - scores + text += " S" + std::to_string(e.success); + text += " F" + std::to_string(e.fail); + text += " M" + std::to_string(e.miss); + + // Up size & position + bs.w = char_size.w * text.length(); + pt.x = screen_size.w / 2 - bs.w / 2; + + _entries.emplace_back(std::make_unique(pt, bs)); + _entries.back()->set_text(std::move(text)); + + if ((kebb::GameMode)e.mode == kebb::GameMode::M_Survival) + _entries.back()->set_color_text(kebb::color(kebb::ColorName::C_Text)); + else + _entries.back()->set_color_text(kebb::color(kebb::ColorName::C_Sky)); + + pt.y += bs.h * 1.15; + } +} + +WindowRecord::~WindowRecord() {} + +// ---------------------------------------------------------------------------------------------------- +// RENDER --------------------------------------------------------------------------------------------- +void WindowRecord::render() const { + + _renderer->clear_screen(); + + _widget_title->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + _widget_title2->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + + for (const auto &e : _entries) { + e->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); + } + + _widget_menu->render(); + + // Update Screen + SDL_RenderPresent(_renderer->renderer()); +} + +// ---------------------------------------------------------------------------------------------------- +// CONTROLS ------------------------------------------------------------------------------------------- +void WindowRecord::control_escape() { *_next_window = kebb::WindowName::W_Welcome; } diff --git a/src/record/window_record.h b/src/record/window_record.h new file mode 100644 index 0000000..b7afc5f --- /dev/null +++ b/src/record/window_record.h @@ -0,0 +1,31 @@ +#ifndef WINDOW_RECORD +#define WINDOW_RECORD + +#include "file/record_file.h" +#include "utils.h" +#include "widget/window/widget_bottom_menu.h" +#include "widget/window/widget_window.h" + +/* + * Display entries saved in record file (calculated for 15 entries). + */ +class WindowRecord : public WidgetWindow { +public: + WindowRecord(kebb::boxsize screen_size, std::shared_ptr next_window, + std::shared_ptr renderer, std::shared_ptr records); + virtual ~WindowRecord() override; + + virtual void control_escape() override; + virtual void render() const override; + +private: + std::shared_ptr _records; + + std::unique_ptr _widget_title; + std::unique_ptr _widget_title2; + std::vector> _entries; + + std::unique_ptr _widget_menu; +}; + +#endif // !WINDOW_RECORD diff --git a/src/renderer.cpp b/src/renderer.cpp index 7c411af..7b76bd0 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -1,97 +1,104 @@ #include "renderer.h" Renderer::Renderer(kebb::boxsize screen_size, uint16_t scale_factor, uint16_t font_size_target, - uint16_t font_size_score, uint16_t font_size_menu) - : _screen_size(screen_size), _scale_factor(scale_factor), _font_target(nullptr), _font_score(nullptr), - _font_menu(nullptr) { + uint16_t font_size_game, uint16_t font_size_menu) + : _screen_size(screen_size), _scale_factor(scale_factor), _font_target(nullptr), _font_game(nullptr), + _font_menu(nullptr), _init_ok(false) { - // _color_background = kebb::color(kebb::ColorName::C_Sky); _color_background = kebb::color(kebb::ColorName::C_Base); // Initialize SDL - if (SDL_Init(SDL_INIT_VIDEO) < 0) { - std::cerr << "SDL could not initialize.\n"; - std::cerr << "SDL_Error: " << SDL_GetError() << "\n"; - } + if (SDL_Init(SDL_INIT_VIDEO) >= 0) { - // Initialize the TTF library - if (TTF_Init() < 0) { - std::cerr << "Could not initialize TTF.\n"; - std::cerr << "SDL_Error: " << SDL_GetError() << "\n"; - } + // Initialize the TTF library + if (TTF_Init() >= 0) { - // Fonts -- + // Fonts -- #ifdef RELEASE_LINUX - _font_target = TTF_OpenFont("/usr/share/kebb/font/dejavu-sans-mono.bold.ttf", font_size_target); - _font_score = TTF_OpenFont("/usr/share/kebb/font/charybdis.regular.ttf", font_size_score); - _font_menu = TTF_OpenFont("/usr/share/kebb/font/charybdis.regular.ttf", font_size_menu); + _font_target = TTF_OpenFont("/usr/share/kebb/font/dejavu-sans-mono.bold.ttf", font_size_target); + _font_game = TTF_OpenFont("/usr/share/kebb/font/charybdis.regular.ttf", font_size_game); + _font_menu = TTF_OpenFont("/usr/share/kebb/font/charybdis.regular.ttf", font_size_menu); #else - _font_target = TTF_OpenFont("./font/dejavu-sans-mono.bold.ttf", font_size_target); - _font_score = TTF_OpenFont("./font/charybdis.regular.ttf", font_size_score); - _font_menu = TTF_OpenFont("./font/charybdis.regular.ttf", font_size_menu); + _font_target = TTF_OpenFont("./font/dejavu-sans-mono.bold.ttf", font_size_target); + _font_game = TTF_OpenFont("./font/charybdis.regular.ttf", font_size_game); + _font_menu = TTF_OpenFont("./font/charybdis.regular.ttf", font_size_menu); #endif - if (_font_target == nullptr || _font_score == nullptr || _font_menu == nullptr) { - std::cerr << "Could not open the lazy.ttf"; - std::cerr << " SDL_Error: " << SDL_GetError() << "\n"; + if (_font_target != nullptr && _font_game != nullptr && _font_menu != nullptr) { + + int w = 0; // Get the size for one char here + int h = 0; + + TTF_SizeUTF8(_font_target, "X", &w, &h); + _char_size_target = {static_cast(w), static_cast(h)}; + + TTF_SizeUTF8(_font_game, "X", &w, &h); + _char_size_game = {static_cast(w), static_cast(h)}; + + TTF_SizeUTF8(_font_menu, "X", &w, &h); + _char_size_menu = {static_cast(w), static_cast(h)}; + + // Create Window + _window = SDL_CreateWindow("Kebb", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, _screen_size.w, + _screen_size.h, SDL_WINDOW_SHOWN); + if (_window != nullptr) { + + // Create renderer + _renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED); + if (_renderer != nullptr) { + + // Add transparency + if (SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND) == 0) { + + // Set a logical scale, mandatory to move in all directions + if (SDL_RenderSetLogicalSize(_renderer, _screen_size.w * _scale_factor, + _screen_size.h * _scale_factor) == 0) { + + _init_ok = true; + + } else { + std::cerr << "Renderer could not be scale.\n"; + std::cerr << "SDL_Error: " << SDL_GetError() << "\n"; + } + } else { + std::cerr << "Transparency management could not be set.\n"; + std::cerr << "SDL_Error: " << SDL_GetError() << "\n"; + } + } else { + std::cerr << "Renderer could not be created.\n"; + std::cerr << "SDL_Error: " << SDL_GetError() << "\n"; + } + } else { + std::cerr << "Window could not be created.\n"; + std::cerr << " SDL_Error: " << SDL_GetError() << "\n"; + } + } else { + std::cerr << "Could not open the font ttf files.\n"; + std::cerr << " SDL_Error: " << SDL_GetError() << "\n"; + } + } else { + std::cerr << "Could not initialize TTF.\n"; + std::cerr << "SDL_Error: " << SDL_GetError() << "\n"; + } } else { - - int w = 0; // Get the size for one char here - int h = 0; - - TTF_SizeUTF8(_font_target, "X", &w, &h); // NOTE: Add a check ? - _char_size_target = {static_cast(w), static_cast(h)}; - - TTF_SizeUTF8(_font_score, "X", &w, &h); - _char_size_score = {static_cast(w), static_cast(h)}; - - TTF_SizeUTF8(_font_menu, "X", &w, &h); - _char_size_menu = {static_cast(w), static_cast(h)}; - } - - // Create Window - _window = SDL_CreateWindow("Kebb", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, _screen_size.w, - _screen_size.h, SDL_WINDOW_SHOWN); - - if (nullptr == _window) { - std::cerr << "Window could not be created.\n"; - std::cerr << " SDL_Error: " << SDL_GetError() << "\n"; - } - - // Create renderer - _renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED); - if (nullptr == _renderer) { - std::cerr << "Renderer could not be created.\n"; - std::cerr << "SDL_Error: " << SDL_GetError() << "\n"; - } - - // Add transparency - if (SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND) != 0) { - std::cerr << "Transparency management could not be set.\n"; - std::cerr << "SDL_Error: " << SDL_GetError() << "\n"; - } - - // Set a logical scale, mandatory to move in all directions - if (SDL_RenderSetLogicalSize(_renderer, _screen_size.w * _scale_factor, _screen_size.h * _scale_factor) != - 0) { - std::cerr << "Renderer could not be scale.\n"; + std::cerr << "SDL could not initialize.\n"; std::cerr << "SDL_Error: " << SDL_GetError() << "\n"; } } Renderer::~Renderer() { TTF_CloseFont(_font_target); - TTF_CloseFont(_font_score); + TTF_CloseFont(_font_menu); + TTF_CloseFont(_font_game); TTF_Quit(); SDL_DestroyWindow(_window); SDL_Quit(); } +bool Renderer::init_ok() const { return _init_ok; } SDL_Renderer *Renderer::renderer() { return _renderer; } void Renderer::update_window_title(uint16_t fps) { - // std::string title{"Kebb - " + std::to_string(_screen_size.w) + "x" + std::to_string(_screen_size.h) + - // " - fps: " + std::to_string(fps)}; std::string title{"Kebb - [" + std::to_string(fps) + (" fps]")}; SDL_SetWindowTitle(_window, title.c_str()); } @@ -100,7 +107,6 @@ void Renderer::update_window_title(uint16_t fps) { * Standardize SDL_RenderClear with the background color */ void Renderer::clear_screen() { - // SDL_SetRenderDrawColor(_renderer, 0x1E, 0x1E, 0x1E, 0xFF); SDL_SetRenderDrawColor(_renderer, _color_background.r, _color_background.g, _color_background.b, 0xFF); SDL_RenderClear(_renderer); } @@ -113,8 +119,8 @@ TTF_Font *Renderer::font(FontName fn) { return _font_target; case FontName::F_Menu: return _font_menu; - default: // F_Score - return _font_score; + default: + return _font_game; } } kebb::boxsize Renderer::font_char_size(FontName fn) const { @@ -123,7 +129,7 @@ kebb::boxsize Renderer::font_char_size(FontName fn) const { return _char_size_target; case FontName::F_Menu: return _char_size_menu; - default: // F_Score - return _char_size_score; + default: + return _char_size_game; } } diff --git a/src/renderer.h b/src/renderer.h index ead5ef7..b263f4a 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -12,21 +12,22 @@ enum class FontName { F_Target, - F_Score, + F_Game, F_Menu, }; class Renderer { public: Renderer(kebb::boxsize screen_size, uint16_t scale_factor, uint16_t font_size_target, - uint16_t font_size_score, uint16_t font_size_menu); + uint16_t font_size_game, uint16_t font_size_menu); ~Renderer(); + bool init_ok() const; + SDL_Renderer *renderer(); + void update_window_title(uint16_t fps); void clear_screen(); - SDL_Renderer *renderer(); - TTF_Font *font(FontName fn); kebb::boxsize font_char_size(FontName fn) const; @@ -35,16 +36,18 @@ class Renderer { SDL_Renderer *_renderer; TTF_Font *_font_target; - TTF_Font *_font_score; + TTF_Font *_font_game; TTF_Font *_font_menu; kebb::boxsize _char_size_target; - kebb::boxsize _char_size_score; + kebb::boxsize _char_size_game; kebb::boxsize _char_size_menu; const kebb::boxsize _screen_size; const uint16_t _scale_factor; SDL_Color _color_background; + + bool _init_ok; }; #endif diff --git a/src/score/widget_score.cpp b/src/score/widget_score.cpp deleted file mode 100644 index d241434..0000000 --- a/src/score/widget_score.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "widget_score.h" - -WidgetScore::WidgetScore(WidgetScoreType type, kebb::boxsize screen_size, std::shared_ptr score, - std::shared_ptr renderer) - : _score(score), _renderer(renderer) { - - // Geometry - kebb::boxsize char_size = _renderer->font_char_size(FontName::F_Score); - kebb::boxsize line_size; - kebb::point pt; - - // ------------------------------------------------------------------------ - // Success / Fail / Miss -------------------------------------------------- - if (type == WidgetScoreType::FullScreen) { - - char_size.set_scale(2); - line_size.w = char_size.w * 13; // 13 chars in total for all lines (see render()) - line_size.h = char_size.h; - - pt.x = screen_size.w / 2 - line_size.w / 2; - pt.y = line_size.h * 5; - - } else { - - line_size.w = char_size.w * 13; - line_size.h = char_size.h; - - pt.x = screen_size.w - line_size.w - char_size.h * 0.1; - pt.y = char_size.h * 0.1; - } - - _textbox_success = std::make_unique(pt, line_size); - pt.y += char_size.h; - _textbox_fail = std::make_unique(pt, line_size); - pt.y += char_size.h; - _textbox_miss = std::make_unique(pt, line_size); - - // ------------------------------------------------------------------------ - // Timer ------------------------------------------------------------------ - char_size = _renderer->font_char_size(FontName::F_Score); - - if (type == WidgetScoreType::FullScreen) { - - char_size.set_scale(5); - line_size.w = char_size.w * 5; - line_size.h = char_size.h; - - pt.x = screen_size.w / 2 - line_size.w / 2; - pt.y = char_size.h * 0.5; - - } else { - - // Timer bigger and on the left -- - char_size.set_scale(1.7); - line_size.w = char_size.w * 5; - line_size.h = char_size.h; - - pt.x = char_size.h * 1.5 * 0.2; - pt.y = char_size.h * 1.5 * 0.2; - } - _textbox_time = std::make_unique(pt, line_size); - - _textbox_time->set_color_text(kebb::color(kebb::ColorName::C_Peach)); - - _textbox_success->set_color_text(kebb::color(kebb::ColorName::C_Blue)); - _textbox_fail->set_color_text(kebb::color(kebb::ColorName::C_Blue)); - _textbox_miss->set_color_text(kebb::color(kebb::ColorName::C_Blue)); -} - -void WidgetScore::render(uint16_t time_seconds) { - - _textbox_success->set_text("Success " + int_to_string(_score->success(), 5)); - _textbox_fail->set_text("Fail " + int_to_string(_score->fail(), 8)); - _textbox_miss->set_text("Miss " + int_to_string(_score->miss(), 8)); - - _textbox_time->set_text(int_to_string(time_seconds / 60, 2, '0') + ":" + - int_to_string(time_seconds % 60, 2, '0')); - - _textbox_time->render(_renderer->renderer(), _renderer->font(FontName::F_Score)); - _textbox_success->render(_renderer->renderer(), _renderer->font(FontName::F_Score)); - _textbox_fail->render(_renderer->renderer(), _renderer->font(FontName::F_Score)); - _textbox_miss->render(_renderer->renderer(), _renderer->font(FontName::F_Score)); -} - -std::string WidgetScore::int_to_string(uint16_t i, uint8_t text_length, char c) { - std::string text = std::to_string(i); - while (text.length() < text_length) - text.insert(0, 1, c); - - return text; -} diff --git a/src/utils.cpp b/src/utils.cpp index e8b4573..303d8f0 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,5 +1,12 @@ #include "utils.h" +std::string kebb::adapt_string_length(std::string &&text, uint8_t text_length, char c) { + while (text.length() < text_length) + text.insert(0, 1, c); + + return std::move(text); // NOTE: Is move here farfetch ? +} + // clang-format off std::string kebb::keycode_to_string(uint16_t val) { diff --git a/src/utils.h b/src/utils.h index df7d31f..4a3e187 100644 --- a/src/utils.h +++ b/src/utils.h @@ -34,6 +34,28 @@ namespace kebb { }; }; + /* + * Allow to make a string longer with the char 'c'. + * Useful to align text. + */ + std::string adapt_string_length(std::string &&text, uint8_t text_length, char c = ' '); + + // ---------------------------------------------------------------------------------------------------- + // GAME MODES ----------------------------------------------------------------------------------------- + enum class GameMode { + M_Survival = 10, + M_Timer = 11, + }; + + // ---------------------------------------------------------------------------------------------------- + // GAME STATUS ---------------------------------------------------------------------------------------- + enum class GameStatus { + S_Win = 10, + S_Loose = 11, + S_TimeUp = 12, + S_Quit = 13, + }; + // ---------------------------------------------------------------------------------------------------- // WINDOWNAME (used to navigate) ---------------------------------------------------------------------- enum class WindowName { @@ -41,10 +63,14 @@ namespace kebb { W_Quit, W_About, - W_Game, + W_GameOver, + W_GameSurvival, + W_GameTimer, W_Option, - W_Pause, + W_Record, W_Welcome, + W_WelcomeTimer, + W_WelcomeSurvival, }; // ---------------------------------------------------------------------------------------------------- diff --git a/src/welcome/window_welcome.cpp b/src/welcome/window_welcome.cpp index 7d4341c..d0d6fae 100644 --- a/src/welcome/window_welcome.cpp +++ b/src/welcome/window_welcome.cpp @@ -1,13 +1,13 @@ #include "window_welcome.h" WindowWelcome::WindowWelcome(kebb::boxsize screen_size, std::shared_ptr next_window, - std::shared_ptr renderer) - : WidgetWindowSelection(next_window, renderer) { + std::shared_ptr renderer, std::shared_ptr options) + : WidgetWindowSelection(next_window, renderer), _options(options) { - _widget_menu = std::make_unique(screen_size, renderer, " Quit Valid"); + _widget_menu = std::make_unique(screen_size, renderer, " Quit Valid"); // Geometry - kebb::boxsize char_size = _renderer->font_char_size(FontName::F_Menu); // NOTE: Use font menu ? + kebb::boxsize char_size = _renderer->font_char_size(FontName::F_Menu); kebb::boxsize bs_title; kebb::point pt; @@ -36,14 +36,25 @@ WindowWelcome::WindowWelcome(kebb::boxsize screen_size, std::shared_ptrfont_char_size(FontName::F_Menu).scale(1.7); + kebb::boxsize bs_field = renderer->font_char_size(FontName::F_Menu).scale(1.5); pt.x = screen_size.w / 2; pt.y += bs_logo.h * 1.7; - _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "Play", true)); + // Set the current selection based on the last game + bool first_sel = _options->get().last_mode == uint16_t(kebb::GameMode::M_Survival); + + _widget_select_fields.emplace_back( + std::make_unique(pt, bs_field, "Survival mode", first_sel)); pt.y += bs_field.h * 1.1; + _widget_select_fields.emplace_back( + std::make_unique(pt, bs_field, "Timer mode", !first_sel)); + pt.y += bs_field.h * 1.3; + + bs_field = renderer->font_char_size(FontName::F_Menu).scale(1.2); _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "Options")); pt.y += bs_field.h * 1.1; + _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "Records")); + pt.y += bs_field.h * 1.1; _widget_select_fields.emplace_back(std::make_unique(pt, bs_field, "About")); } @@ -52,7 +63,7 @@ WindowWelcome::~WindowWelcome() { _thread.join(); } -void WindowWelcome::render() { +void WindowWelcome::render() const { _renderer->clear_screen(); _widget_title->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); @@ -73,9 +84,13 @@ void WindowWelcome::control_escape() { *_next_window = kebb::WindowName::W_Quit; void WindowWelcome::control_enter() { if (_widget_select_fields[0]->is_selected()) - *_next_window = kebb::WindowName::W_Game; + *_next_window = kebb::WindowName::W_WelcomeSurvival; else if (_widget_select_fields[1]->is_selected()) - *_next_window = kebb::WindowName::W_Option; + *_next_window = kebb::WindowName::W_WelcomeTimer; else if (_widget_select_fields[2]->is_selected()) + *_next_window = kebb::WindowName::W_Option; + else if (_widget_select_fields[3]->is_selected()) + *_next_window = kebb::WindowName::W_Record; + else if (_widget_select_fields[4]->is_selected()) *_next_window = kebb::WindowName::W_About; } diff --git a/src/welcome/window_welcome.h b/src/welcome/window_welcome.h index d29b3b6..51af8aa 100644 --- a/src/welcome/window_welcome.h +++ b/src/welcome/window_welcome.h @@ -1,13 +1,14 @@ #ifndef WINDOW_WELCOME_H #define WINDOW_WELCOME_H +#include "file/option_file.h" #include "renderer.h" #include "utils.h" #include "welcome/widget_logo.h" #include "widget/button/widget_selection.h" -#include "widget/widget_menu.h" #include "widget/widget_textbox.h" -#include "widget/widget_window.h" +#include "widget/window/widget_bottom_menu.h" +#include "widget/window/widget_window.h" #include #include #include @@ -15,20 +16,22 @@ class WindowWelcome : public WidgetWindowSelection { public: WindowWelcome(kebb::boxsize screen_size, std::shared_ptr next_window, - std::shared_ptr renderer); + std::shared_ptr renderer, std::shared_ptr options); virtual ~WindowWelcome() override; virtual void control_escape() override; virtual void control_enter() override; - virtual void render() override; + virtual void render() const override; private: std::unique_ptr _widget_title; - std::unique_ptr _widget_menu; + std::unique_ptr _widget_menu; std::shared_ptr _widget_logo; std::thread _thread; + + std::shared_ptr _options; }; #endif // !WINDOW_WELCOME_H diff --git a/src/widget/button/widget_list.cpp b/src/widget/button/widget_list.cpp index 9a4eb67..5d57862 100644 --- a/src/widget/button/widget_list.cpp +++ b/src/widget/button/widget_list.cpp @@ -9,13 +9,13 @@ WidgetList::WidgetList(kebb::point pos_center, kebb::boxsize size_char, std::str } WidgetList::WidgetList(kebb::point pos_center, kebb::boxsize size_char, std::string &&text, - int16_t range_start, int16_t range_stop, int16_t step, bool selected) + uint16_t range_start, uint16_t range_stop, uint16_t step, bool selected) : WidgetSelection(pos_center, size_char, std::move(text), selected), _size_char(size_char), _longest_choice_width(0), _space(0) { for (; range_start <= range_stop; range_start += step) - _choices.emplace_back(SelectionItem{std::to_string(range_start), std::to_string(range_start)}); + _choices.emplace_back(SelectionItem{.text = std::to_string(range_start), .value_uint = range_start}); _it = _choices.begin(); @@ -52,7 +52,22 @@ void WidgetList::set_choice_by_value(const std::string &value) { _it = _choices.begin(); while (_it != _choices.end()) { - if ((*_it).value == value) { + if ((*_it).value_string == value) { + display_current_it(); + return; + } + ++_it; + } + + _it = _choices.begin(); +} + +void WidgetList::set_choice_by_value(uint16_t value) { // TODO: Rewrite both ? + + _it = _choices.begin(); + while (_it != _choices.end()) { + + if ((*_it).value_uint == value) { display_current_it(); return; } diff --git a/src/widget/button/widget_list.h b/src/widget/button/widget_list.h index 5589f48..195d2ab 100644 --- a/src/widget/button/widget_list.h +++ b/src/widget/button/widget_list.h @@ -11,15 +11,15 @@ /* * Based on WidgetSelection, this class add another WidgetTextBox to display an range of strings. - * Values are stored in a vector of SelectionItem struct (see the mother). + * Values (string or uint) are stored in a vector of SelectionItem struct (see the mother). * Use action_left/action_right methods to change the current value. */ class WidgetList : public WidgetSelection { public: WidgetList(kebb::point pos_center, kebb::boxsize size_char, std::string &&text, std::vector &&choices, bool selected = false); - WidgetList(kebb::point pos_center, kebb::boxsize size_char, std::string &&text, int16_t range_start, - int16_t range_stop, int16_t step, bool selected = false); + WidgetList(kebb::point pos_center, kebb::boxsize size_char, std::string &&text, uint16_t range_start, + uint16_t range_stop, uint16_t step, bool selected = false); virtual ~WidgetList() override; virtual void action_left() override; @@ -27,6 +27,7 @@ class WidgetList : public WidgetSelection { virtual SelectionItem get_choice() const override; virtual void set_choice_by_value(const std::string &value) override; + virtual void set_choice_by_value(uint16_t value) override; virtual void render(SDL_Renderer *renderer, TTF_Font *font) const override; diff --git a/src/widget/button/widget_selection.h b/src/widget/button/widget_selection.h index cc509bd..3d26f49 100644 --- a/src/widget/button/widget_selection.h +++ b/src/widget/button/widget_selection.h @@ -8,7 +8,8 @@ // Use by child WidgetList struct SelectionItem { std::string text; - std::string value; + std::string value_string = ""; + uint16_t value_uint = 0; }; /* @@ -30,8 +31,9 @@ class WidgetSelection : public WidgetTextBox { virtual bool get_bool() const { return false; }; virtual void set_bool(bool val){}; - virtual SelectionItem get_choice() const { return {"", ""}; }; + virtual SelectionItem get_choice() const { return {"", "", 0}; }; virtual void set_choice_by_value(const std::string &value){}; + virtual void set_choice_by_value(uint16_t value){}; protected: bool _selected; diff --git a/src/widget/widget_menu.h b/src/widget/widget_menu.h deleted file mode 100644 index d1299e3..0000000 --- a/src/widget/widget_menu.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef WIDGET_MENU_H -#define WIDGET_MENU_H - -#include "renderer.h" -#include "widget/widget_textbox.h" -#include - -class WidgetMenu { -public: - WidgetMenu(kebb::boxsize screen_size, std::shared_ptr renderer, std::string &&text); - void render() const; - -private: - std::unique_ptr _widget_textbox; - std::shared_ptr _renderer; -}; - -#endif // !WIDGET_MENU_H diff --git a/src/widget/widget_textbox.cpp b/src/widget/widget_textbox.cpp index 2227a4a..4ba0d95 100644 --- a/src/widget/widget_textbox.cpp +++ b/src/widget/widget_textbox.cpp @@ -7,9 +7,14 @@ WidgetTextBox::WidgetTextBox(kebb::point position, kebb::boxsize size) } WidgetTextBox::~WidgetTextBox() {} +// ---------------------------------------------------------------------------------------------------- +// ACCESSORS ------------------------------------------------------------------------------------------ void WidgetTextBox::set_text(std::string &&text) { _text = std::move(text); } +std::string WidgetTextBox::get_text() const { return _text; } void WidgetTextBox::set_color_text(SDL_Color &&color) { _color_text = std::move(color); } +// ---------------------------------------------------------------------------------------------------- +// RENDER --------------------------------------------------------------------------------------------- void WidgetTextBox::render(SDL_Renderer *renderer, TTF_Font *font) const { SDL_Surface *new_textSurface = TTF_RenderUTF8_Shaded(font, _text.c_str(), _color_text, _color); diff --git a/src/widget/widget_textbox.h b/src/widget/widget_textbox.h index 692bbf8..9548b65 100644 --- a/src/widget/widget_textbox.h +++ b/src/widget/widget_textbox.h @@ -13,6 +13,7 @@ class WidgetTextBox : public WidgetBase { // NOTE: Keep move ? void set_text(std::string &&txt); void set_color_text(SDL_Color &&color); + std::string get_text() const; protected: std::string _text; diff --git a/src/widget/widget_menu.cpp b/src/widget/window/widget_bottom_menu.cpp similarity index 73% rename from src/widget/widget_menu.cpp rename to src/widget/window/widget_bottom_menu.cpp index a221d3b..667fbbc 100644 --- a/src/widget/widget_menu.cpp +++ b/src/widget/window/widget_bottom_menu.cpp @@ -1,6 +1,7 @@ -#include "widget_menu.h" +#include "widget_bottom_menu.h" -WidgetMenu::WidgetMenu(kebb::boxsize screen_size, std::shared_ptr renderer, std::string &&text) +WidgetBottomMenu::WidgetBottomMenu(kebb::boxsize screen_size, std::shared_ptr renderer, + std::string &&text) : _renderer(renderer) { kebb::boxsize char_size = _renderer->font_char_size(FontName::F_Menu); @@ -13,6 +14,6 @@ WidgetMenu::WidgetMenu(kebb::boxsize screen_size, std::shared_ptr rend _widget_textbox->set_color_text(kebb::color(kebb::ColorName::C_Overlay2)); } -void WidgetMenu::render() const { +void WidgetBottomMenu::render() const { _widget_textbox->render(_renderer->renderer(), _renderer->font(FontName::F_Menu)); } diff --git a/src/widget/window/widget_bottom_menu.h b/src/widget/window/widget_bottom_menu.h new file mode 100644 index 0000000..4b778dc --- /dev/null +++ b/src/widget/window/widget_bottom_menu.h @@ -0,0 +1,21 @@ +#ifndef WIDGET_BOTTOM_MENU_H +#define WIDGET_BOTTOM_MENU_H + +#include "renderer.h" +#include "widget/widget_textbox.h" +#include + +/* + * Display a textbox which allows you to indicate user actions. + */ +class WidgetBottomMenu { +public: + WidgetBottomMenu(kebb::boxsize screen_size, std::shared_ptr renderer, std::string &&text); + void render() const; + +private: + std::unique_ptr _widget_textbox; + std::shared_ptr _renderer; +}; + +#endif // !WIDGET_BOTTOM_MENU_H diff --git a/src/widget/widget_window.cpp b/src/widget/window/widget_window.cpp similarity index 83% rename from src/widget/widget_window.cpp rename to src/widget/window/widget_window.cpp index 25b7eb9..2d9d769 100644 --- a/src/widget/widget_window.cpp +++ b/src/widget/window/widget_window.cpp @@ -54,3 +54,22 @@ void WidgetWindowSelection::control_down() { } } } + +void WidgetWindowSelection::control_left() { + + for (auto &w : _widget_select_fields) { + if (w->is_selected()) { + w->action_left(); + return; + } + } +} +void WidgetWindowSelection::control_right() { + + for (auto &w : _widget_select_fields) { + if (w->is_selected()) { + w->action_right(); + return; + } + } +} diff --git a/src/widget/widget_window.h b/src/widget/window/widget_window.h similarity index 91% rename from src/widget/widget_window.h rename to src/widget/window/widget_window.h index 68f8ecc..f587446 100644 --- a/src/widget/widget_window.h +++ b/src/widget/window/widget_window.h @@ -27,7 +27,8 @@ class WidgetWindow { virtual void control_others(uint16_t keycode){}; virtual void controller(){}; - virtual void render(){}; + virtual void logic(){}; + virtual void render() const {}; protected: std::shared_ptr _next_window; @@ -47,6 +48,8 @@ class WidgetWindowSelection : public WidgetWindow { virtual void control_up() override; virtual void control_down() override; + virtual void control_left() override; + virtual void control_right() override; protected: std::vector> _widget_select_fields;