255 lines
9.3 KiB
C++
255 lines
9.3 KiB
C++
#pragma once
|
|
|
|
#include "engine/prefabs/includes.h"
|
|
#include "ghhb_game.h"
|
|
#include <array>
|
|
#include <vector>
|
|
|
|
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 constexpr const char* INSTRUMENT_IMAGE_PATHS[MAX_INSTRUMENT_TYPES] = {
|
|
"assets/instruments/guitar.png",
|
|
"assets/instruments/sax.png",
|
|
"assets/instruments/violin.png",
|
|
"assets/instruments/piano.png",
|
|
};
|
|
|
|
std::array<int, MAX_INSTRUMENT_TYPES> instrument_owner = {-1, -1, -1, -1};
|
|
std::vector<int> participant_order;
|
|
|
|
void init_services() override { add_service<TextureService>(); }
|
|
|
|
void init() override
|
|
{
|
|
auto font_manager = game->get_manager<FontManager>();
|
|
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<int>(i);
|
|
}
|
|
if (participant_order.size() < static_cast<size_t>(MAX_INSTRUMENT_TYPES))
|
|
{
|
|
participant_order.push_back(physical_gamepad_id);
|
|
return static_cast<int>(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)
|
|
{
|
|
if (instrument_owner[slot] == gi)
|
|
instrument_owner[slot] = -1;
|
|
else
|
|
instrument_owner[slot] = gi;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool at_least_one_selected = false;
|
|
for (int s = 0; s < MAX_INSTRUMENT_TYPES; s++)
|
|
{
|
|
if (instrument_owner[s] >= 0)
|
|
{
|
|
at_least_one_selected = true;
|
|
break;
|
|
}
|
|
}
|
|
if (at_least_one_selected)
|
|
{
|
|
if (IsKeyPressed(KEY_ENTER))
|
|
{
|
|
for (int i = 0; i < MAX_INSTRUMENT_TYPES; i++)
|
|
{
|
|
if (instrument_owner[i] >= 0)
|
|
{
|
|
INSTRUMENT_GAMEPAD_INDEX[i] = instrument_owner[i];
|
|
INSTRUMENT_PHYSICAL_GAMEPAD[i] = participant_order[instrument_owner[i]];
|
|
}
|
|
else
|
|
{
|
|
INSTRUMENT_GAMEPAD_INDEX[i] = -1;
|
|
INSTRUMENT_PHYSICAL_GAMEPAD[i] = -1;
|
|
}
|
|
}
|
|
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++)
|
|
{
|
|
if (instrument_owner[i] >= 0)
|
|
{
|
|
INSTRUMENT_GAMEPAD_INDEX[i] = instrument_owner[i];
|
|
INSTRUMENT_PHYSICAL_GAMEPAD[i] = participant_order[instrument_owner[i]];
|
|
}
|
|
else
|
|
{
|
|
INSTRUMENT_GAMEPAD_INDEX[i] = -1;
|
|
INSTRUMENT_PHYSICAL_GAMEPAD[i] = -1;
|
|
}
|
|
}
|
|
game->go_to_scene("ghhb");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void draw() override
|
|
{
|
|
float w = static_cast<float>(GetScreenWidth());
|
|
float h = static_cast<float>(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) {
|
|
DrawTriangle(v[0], v[1], v[2], INSTRUMENT_COLORS[slot]);
|
|
};
|
|
|
|
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<TextureService>();
|
|
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<float>(tex.width);
|
|
float th = static_cast<float>(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<int>(ix - 40), static_cast<int>(iy - 40), 80, 80,
|
|
Color{80, 80, 100, 255});
|
|
}
|
|
};
|
|
|
|
const float icon_y_top = h / 6.0f;
|
|
const float icon_y_bottom = h * 5.0f / 6.0f;
|
|
const float icon_x_left = w / 6.0f;
|
|
const float icon_x_right = w * 5.0f / 6.0f;
|
|
draw_center_icon(cx, icon_y_top, SLOT_TOP);
|
|
draw_center_icon(cx, icon_y_bottom, SLOT_BOTTOM);
|
|
draw_center_icon(icon_x_left, cy, SLOT_LEFT);
|
|
draw_center_icon(icon_x_right, cy, SLOT_RIGHT);
|
|
|
|
const char* player_labels[4] = {"P1", "P2", "P3", "P4"};
|
|
const float label_font_size = 60.0f;
|
|
const float label_offset = 50.0f;
|
|
const float label_offset_bottom = 110.0f;
|
|
auto draw_slot_label = [this, &player_labels, label_font_size, label_offset,
|
|
label_offset_bottom](float ix, float iy, int slot) {
|
|
if (instrument_owner[slot] >= 0)
|
|
{
|
|
const char* label = player_labels[instrument_owner[slot]];
|
|
float lw = MeasureTextEx(font, label, label_font_size, 1.0f).x;
|
|
float ly = (slot == SLOT_BOTTOM) ? iy - label_offset_bottom : iy + label_offset;
|
|
DrawTextEx(font, label, Vector2{ix - lw * 0.5f, ly}, label_font_size, 1.0f, WHITE);
|
|
}
|
|
else
|
|
{
|
|
const char* label = "CPU";
|
|
float lw = MeasureTextEx(font, label, label_font_size, 1.0f).x;
|
|
float ly = (slot == SLOT_BOTTOM) ? iy - label_offset_bottom : iy + label_offset;
|
|
DrawTextEx(font, label, Vector2{ix - lw * 0.5f, ly}, label_font_size, 1.0f,
|
|
Color{180, 180, 180, 255});
|
|
}
|
|
};
|
|
draw_slot_label(cx, icon_y_top, SLOT_TOP);
|
|
draw_slot_label(cx, icon_y_bottom, SLOT_BOTTOM);
|
|
draw_slot_label(icon_x_left, cy, SLOT_LEFT);
|
|
draw_slot_label(icon_x_right, cy, SLOT_RIGHT);
|
|
|
|
bool at_least_one_selected = false;
|
|
for (int s = 0; s < MAX_INSTRUMENT_TYPES; s++)
|
|
{
|
|
if (instrument_owner[s] >= 0)
|
|
{
|
|
at_least_one_selected = true;
|
|
break;
|
|
}
|
|
}
|
|
if (at_least_one_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);
|
|
}
|
|
else
|
|
{
|
|
const char* prompt = "Select at least one instrument";
|
|
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};
|