diff --git a/src/main.cpp b/src/main.cpp index eec3360..472082f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,5 @@ #include "samples/ghhb_game.h" +#include "samples/instrument_select.h" #include "samples/title_screen.h" #include "entities/song.h" @@ -7,6 +8,8 @@ #include #endif +int INSTRUMENT_GAMEPAD_INDEX[MAX_INSTRUMENT_TYPES] = {-1, -1, -1, -1}; + Game game; void update() @@ -30,6 +33,7 @@ int main(int argc, char** argv) font_manager->set_texture_filter("Roboto", TEXTURE_FILTER_BILINEAR); game.add_scene("title"); + game.add_scene("instrument_select"); game.add_scene("ghhb"); Song& song = song_manager->load_song("mary_had_a_lil_lamb", "assets/songs/json/mary.json"); diff --git a/src/samples/ghhb_game.h b/src/samples/ghhb_game.h index 5b8340e..8de349e 100644 --- a/src/samples/ghhb_game.h +++ b/src/samples/ghhb_game.h @@ -158,6 +158,8 @@ std::vector load_chart(const char* path) const char* const GHHB_CHART_PATH = "assets/songs/json/mary.json"; } // namespace +extern int INSTRUMENT_GAMEPAD_INDEX[MAX_INSTRUMENT_TYPES]; + class GHHBScene : public Scene { public: diff --git a/src/samples/instrument_select.h b/src/samples/instrument_select.h new file mode 100644 index 0000000..94520f7 --- /dev/null +++ b/src/samples/instrument_select.h @@ -0,0 +1,193 @@ +#pragma once + +#include "engine/prefabs/includes.h" +#include "ghhb_game.h" +#include +#include + +class InstrumentSelectScreen : public Scene +{ +public: + Font font = {0}; + + static constexpr int SLOT_TOP = 0; + static constexpr int SLOT_BOTTOM = 1; + static constexpr int SLOT_LEFT = 2; + static constexpr int SLOT_RIGHT = 3; + + static constexpr int BUTTON_SLOT[4] = { + SLOT_TOP, // GAMEPAD_BUTTON_LEFT_FACE_UP + SLOT_BOTTOM, // GAMEPAD_BUTTON_LEFT_FACE_DOWN + SLOT_LEFT, // GAMEPAD_BUTTON_LEFT_FACE_LEFT + SLOT_RIGHT, // GAMEPAD_BUTTON_LEFT_FACE_RIGHT + }; + static constexpr int FACE_BUTTONS[4] = { + GAMEPAD_BUTTON_LEFT_FACE_UP, + GAMEPAD_BUTTON_LEFT_FACE_DOWN, + GAMEPAD_BUTTON_LEFT_FACE_LEFT, + GAMEPAD_BUTTON_LEFT_FACE_RIGHT, + }; + + static const Color BORDER_COLOR; + static const Color SECTION_BG_COLOR; + + static constexpr const char* INSTRUMENT_IMAGE_PATHS[MAX_INSTRUMENT_TYPES] = { + "assets/instrument_0.png", + "assets/instrument_1.png", + "assets/instrument_2.png", + "assets/instrument_3.png", + }; + + std::array instrument_owner = {-1, -1, -1, -1}; + std::vector participant_order; + + void init_services() override { add_service(); } + + void init() override + { + auto font_manager = game->get_manager(); + font = font_manager->get_font("Roboto"); + instrument_owner.fill(-1); + participant_order.clear(); + } + + int get_or_assign_gamepad_index(int physical_gamepad_id) + { + for (size_t i = 0; i < participant_order.size(); i++) + { + if (participant_order[i] == physical_gamepad_id) + return static_cast(i); + } + if (participant_order.size() < static_cast(MAX_INSTRUMENT_TYPES)) + { + participant_order.push_back(physical_gamepad_id); + return static_cast(participant_order.size() - 1); + } + return -1; + } + + void update(float delta_time) override + { + for (int gp = 0; gp < MAX_GAMEPADS; gp++) + { + if (!IsGamepadAvailable(gp)) + continue; + for (int b = 0; b < 4; b++) + { + if (IsGamepadButtonPressed(gp, FACE_BUTTONS[b])) + { + int slot = BUTTON_SLOT[b]; + int gi = get_or_assign_gamepad_index(gp); + if (gi >= 0) + instrument_owner[slot] = gi; + } + } + } + + bool all_selected = true; + for (int s = 0; s < MAX_INSTRUMENT_TYPES; s++) + { + if (instrument_owner[s] < 0) + { + all_selected = false; + break; + } + } + if (all_selected) + { + if (IsKeyPressed(KEY_ENTER)) + { + for (int i = 0; i < MAX_INSTRUMENT_TYPES; i++) + INSTRUMENT_GAMEPAD_INDEX[i] = instrument_owner[i]; + game->go_to_scene("ghhb"); + return; + } + for (int gp = 0; gp < MAX_GAMEPADS; gp++) + { + if (IsGamepadAvailable(gp) && IsGamepadButtonPressed(gp, GAMEPAD_BUTTON_MIDDLE_RIGHT)) + { + for (int i = 0; i < MAX_INSTRUMENT_TYPES; i++) + INSTRUMENT_GAMEPAD_INDEX[i] = instrument_owner[i]; + game->go_to_scene("ghhb"); + return; + } + } + } + } + + void draw() override + { + float w = static_cast(GetScreenWidth()); + float h = static_cast(GetScreenHeight()); + float cx = w * 0.5f; + float cy = h * 0.5f; + + /* Vertices in counter-clockwise order so Raylib fills the triangle. */ + Vector2 top_tri[3] = {{cx, cy}, {w, 0}, {0, 0}}; + Vector2 bottom_tri[3] = {{0, h}, {w, h}, {cx, cy}}; + Vector2 left_tri[3] = {{0, 0}, {0, h}, {cx, cy}}; + Vector2 right_tri[3] = {{cx, cy}, {w, h}, {w, 0}}; + + auto draw_wedge = [this](const Vector2* v, int slot) { + Color fill = (instrument_owner[slot] >= 0) ? INSTRUMENT_COLORS[instrument_owner[slot]] + : SECTION_BG_COLOR; + DrawTriangle(v[0], v[1], v[2], fill); + }; + + draw_wedge(top_tri, SLOT_TOP); + draw_wedge(bottom_tri, SLOT_BOTTOM); + draw_wedge(left_tri, SLOT_LEFT); + draw_wedge(right_tri, SLOT_RIGHT); + + DrawLineEx(Vector2{0, 0}, Vector2{w, h}, 3.0f, BORDER_COLOR); + DrawLineEx(Vector2{w, 0}, Vector2{0, h}, 3.0f, BORDER_COLOR); + + auto* tex_svc = get_service(); + auto draw_center_icon = [this, tex_svc](float ix, float iy, int slot) { + const char* path = INSTRUMENT_IMAGE_PATHS[slot]; + if (FileExists(path)) + { + Texture2D& tex = tex_svc->get_texture(path); + float tw = static_cast(tex.width); + float th = static_cast(tex.height); + float scale = 1.0f; + float max_dim = 120.0f; + if (tw > max_dim || th > max_dim) + scale = max_dim / (tw > th ? tw : th); + float dw = tw * scale; + float dh = th * scale; + DrawTextureEx(tex, Vector2{ix - dw * 0.5f, iy - dh * 0.5f}, 0.0f, scale, WHITE); + } + else + { + DrawRectangle(static_cast(ix - 40), static_cast(iy - 40), 80, 80, + Color{80, 80, 100, 255}); + } + }; + + draw_center_icon(cx, h / 6.0f, SLOT_TOP); + draw_center_icon(cx, h * 5.0f / 6.0f, SLOT_BOTTOM); + draw_center_icon(w / 6.0f, cy, SLOT_LEFT); + draw_center_icon(w * 5.0f / 6.0f, cy, SLOT_RIGHT); + + bool all_selected = true; + for (int s = 0; s < MAX_INSTRUMENT_TYPES; s++) + { + if (instrument_owner[s] < 0) + { + all_selected = false; + break; + } + } + if (all_selected) + { + const char* prompt = "Press Start to begin"; + float font_size = 24.0f; + float pw = MeasureTextEx(font, prompt, font_size, 1.0f).x; + DrawTextEx(font, prompt, Vector2{cx - pw * 0.5f, h - 40.0f}, font_size, 1.0f, WHITE); + } + } +}; + +inline const Color InstrumentSelectScreen::BORDER_COLOR = {45, 55, 72, 255}; +inline const Color InstrumentSelectScreen::SECTION_BG_COLOR = {95, 115, 140, 255}; diff --git a/src/samples/title_screen.h b/src/samples/title_screen.h index 0a22066..89eebfe 100644 --- a/src/samples/title_screen.h +++ b/src/samples/title_screen.h @@ -18,7 +18,7 @@ public: // Trigger scene change on Enter key or gamepad start button. if (IsKeyPressed(KEY_ENTER) || IsGamepadButtonPressed(0, GAMEPAD_BUTTON_MIDDLE_RIGHT)) { - game->go_to_scene("ghhb"); + game->go_to_scene("instrument_select"); } }