diff --git a/romfs/graphics/game/line_markers.png b/romfs/graphics/game/line_markers.png new file mode 100644 index 0000000..8cfefc2 Binary files /dev/null and b/romfs/graphics/game/line_markers.png differ diff --git a/romfs/graphics/menu/windowskin.png b/romfs/graphics/menu/windowskin.png index 7f2a4a0..f2220dd 100644 Binary files a/romfs/graphics/menu/windowskin.png and b/romfs/graphics/menu/windowskin.png differ diff --git a/source/replay_helpers.cpp b/source/replay_helpers.cpp new file mode 100644 index 0000000..250bce2 --- /dev/null +++ b/source/replay_helpers.cpp @@ -0,0 +1,82 @@ +#include "replay_helpers.hpp" +#include +#include "recorder.hpp" + +u32 ReplayInputDataSource::trigger() const +{ + if (index >= data.size()) return 0; + return data[index].trigger; +} + +u32 ReplayInputDataSource::held() const +{ + if (index >= data.size()) return 0; + return data[index].held; +} + +void ReplayInputDataSource::update() +{ + if (index >= data.size()) return; + frame++; + if (frame - start > data[index].frames) + { + start += data[index].frames; + index++; + } +} + +bool load_replay(const std::string& filename, ReplayInfo& info) +{ + std::ifstream file(filename.c_str(), std::ios::binary); + return load_replay(file, info); +} + +bool load_replay(std::istream& file, ReplayInfo& info) +{ + char magic[4]; + + file.read(magic, 4); + if (memcmp(magic, "BBB", 4) != 0) + return false; + + char major = file.get(); + char minor = file.get(); + + if (major != RECORDER_MAJOR_VERSION) + return false; + if (minor != RECORDER_MINOR_VERSION) + return false; + + info.rows = file.get(); + info.columns = file.get(); + info.type = file.get(); + info.difficulty = file.get(); + info.level = file.get(); + + std::vector initial; + unsigned int size = info.rows * info.columns; + initial.reserve(size); + for (unsigned int i = 0; i < size; i++) + initial.push_back(static_cast(file.get())); + + file.read(reinterpret_cast(&size), sizeof(size)); + std::vector next; + next.reserve(size); + for (unsigned int i = 0; i < size; i++) + next.push_back(static_cast(file.get())); + + info.source.reset(new ReplayPanelSource(info.rows, info.columns, initial, next)); + + file.read(reinterpret_cast(&size), sizeof(size)); + std::vector items; + for (unsigned int i = 0; i < size; i++) + { + ReplayInputItem move; + file.read(reinterpret_cast(&move), sizeof(move)); + items.emplace_back(move); + } + + info.input.reset(new ReplayInputDataSource(items)); + + return true; +} diff --git a/source/replay_helpers.hpp b/source/replay_helpers.hpp new file mode 100644 index 0000000..0214ff0 --- /dev/null +++ b/source/replay_helpers.hpp @@ -0,0 +1,68 @@ +#ifndef REPLAY_HELPERS_HPP +#define REPLAY_HELPERS_HPP + +#include +#include +#include +#include + +#include + +#include "panel_source.hpp" + +struct ReplayInputItem +{ + u32 trigger; + u32 held; + u32 frames; +}; + +class ReplayPanelSource : public PanelSource +{ +public: + ReplayPanelSource(int rows, int columns, const std::vector& start, const std::vector next_set) : + PanelSource(rows, columns), initial(start), next(next_set), index(0) {} + ~ReplayPanelSource() {} + std::vector board() override {return initial;} + Panel::Type panel() override + { + if (index >= next.size()) return Panel::EMPTY; + return next[index++]; + } +private: + std::vector initial; + std::vector next; + unsigned int index; +}; + +class ReplayInputDataSource : public InputDataSourceInterface +{ +public: + ReplayInputDataSource(const std::vector& items) : data(items), index(0) {} + ~ReplayInputDataSource() {} + u32 trigger() const override; + u32 held() const override; + void update() override; +private: + std::vector data; + unsigned int index; + unsigned int start; + unsigned int frame; + +}; + +struct ReplayInfo +{ + std::unique_ptr source; + std::unique_ptr input; + char rows; + char columns; + char type; + char difficulty; + char level; +}; + +bool load_replay(const std::string& filename, ReplayInfo& info); +bool load_replay(std::istream& file, ReplayInfo& info); + +#endif diff --git a/source/scenes/game_scene.cpp b/source/scenes/game_scene.cpp index 2029877..e2ee905 100644 --- a/source/scenes/game_scene.cpp +++ b/source/scenes/game_scene.cpp @@ -88,6 +88,7 @@ void GameScene::init_sprites() selector.create("romfs:/graphics/game/selector.png"); border.create("romfs:/graphics/borders/red.png"); debug.create("romfs:/graphics/game/debug_text.png"); + line_markers.create("romfs:/graphics/game/line_markers.png"); frames.reset(); } @@ -340,6 +341,29 @@ void GameScene::draw_board() border.draw(startx, starty); } +void GameScene::draw_line_marker(int line, LineMarker type) +{ + int bottom_line = table->get_lines(); + int top_line = bottom_line - table->height(); + + if (!(line >= top_line && line <= bottom_line)) + return; + + int offset_line = top_line - line; + int column_underlined = frame % table->width(); + + int startx = (BOTTOM_SCREEN_WIDTH - border.width()) / 2 + 9 + 4; + int starty = BOTTOM_SCREEN_HEIGHT - border.height() + 9; + const int panel_size = PANEL_SIZE; + int offset = table->get_rise(); + + int x = startx + table->width() * panel_size + 6; + int y = starty + (offset_line + 1) * panel_size - offset; + + line_markers.draw(x, y, 0, type * 16, 40, 16); +} + + void GameScene::draw_debug_top() { extern Font* default_font; diff --git a/source/scenes/game_scene.hpp b/source/scenes/game_scene.hpp index 660de7a..167419c 100644 --- a/source/scenes/game_scene.hpp +++ b/source/scenes/game_scene.hpp @@ -31,6 +31,13 @@ enum TimingMode MOVES = 2, }; +/// Represents the type of line marker to draw +enum LineMarker +{ + CLEAR = 0, + GOAL = 1, +}; + /// TODO clean up this class and provide a much clearer interface to write other modes. class GameScene : public Scene2D { @@ -105,6 +112,7 @@ class GameScene : public Scene2D void draw_selector(); void draw_board(); void draw_panels(); + void draw_line_marker(int line, LineMarker type); virtual void draw_debug_bottom(); // Configuration @@ -118,6 +126,7 @@ class GameScene : public Scene2D Texture selector; Texture border; Texture debug; + Texture line_markers; // Core game stuff std::unique_ptr table; diff --git a/source/scenes/score_scene.cpp b/source/scenes/score_scene.cpp index 137f66c..2389935 100644 --- a/source/scenes/score_scene.cpp +++ b/source/scenes/score_scene.cpp @@ -121,6 +121,13 @@ void ScoreScene::draw_game_top() info.draw(); } +void ScoreScene::draw_game_bottom() +{ + GameScene::draw_game_bottom(); + if (config.time_mode == LINES) + draw_line_marker(config.value, GOAL); +} + void ScoreScene::draw_gameover_top() { game_over.draw(); diff --git a/source/scenes/score_scene.hpp b/source/scenes/score_scene.hpp index ce6ca9b..5c13013 100644 --- a/source/scenes/score_scene.hpp +++ b/source/scenes/score_scene.hpp @@ -23,6 +23,7 @@ class ScoreScene : public GameScene void update_on_gameover() override; void draw_game_top() override; + void draw_game_bottom() override; void draw_gameover_top() override; private: ScoreInfoWindow info;