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