fixed cooperate play
This commit is contained in:
@ -110,6 +110,54 @@ static void renderBackdropBlur(SDL_Renderer* renderer, const SDL_Rect& logicalVP
|
||||
|
||||
MenuState::MenuState(StateContext& ctx) : State(ctx) {}
|
||||
|
||||
void MenuState::showCoopSetupPanel(bool show) {
|
||||
if (show) {
|
||||
if (!coopSetupVisible && !coopSetupAnimating) {
|
||||
// Avoid overlapping panels
|
||||
if (aboutPanelVisible && !aboutPanelAnimating) {
|
||||
aboutPanelAnimating = true;
|
||||
aboutDirection = -1;
|
||||
}
|
||||
if (helpPanelVisible && !helpPanelAnimating) {
|
||||
helpPanelAnimating = true;
|
||||
helpDirection = -1;
|
||||
}
|
||||
if (optionsVisible && !optionsAnimating) {
|
||||
optionsAnimating = true;
|
||||
optionsDirection = -1;
|
||||
}
|
||||
if (levelPanelVisible && !levelPanelAnimating) {
|
||||
levelPanelAnimating = true;
|
||||
levelDirection = -1;
|
||||
}
|
||||
if (exitPanelVisible && !exitPanelAnimating) {
|
||||
exitPanelAnimating = true;
|
||||
exitDirection = -1;
|
||||
if (ctx.showExitConfirmPopup) *ctx.showExitConfirmPopup = false;
|
||||
}
|
||||
|
||||
coopSetupAnimating = true;
|
||||
coopSetupDirection = 1;
|
||||
coopSetupSelected = (ctx.coopVsAI && *ctx.coopVsAI) ? 1 : 0;
|
||||
coopSetupRectsValid = false;
|
||||
selectedButton = static_cast<int>(ui::BottomMenuItem::Cooperate);
|
||||
// Ensure the transition value is non-zero so render code can show
|
||||
// the inline choice buttons immediately on the same frame.
|
||||
if (coopSetupTransition <= 0.0) coopSetupTransition = 0.001;
|
||||
}
|
||||
} else {
|
||||
if (coopSetupVisible && !coopSetupAnimating) {
|
||||
coopSetupAnimating = true;
|
||||
coopSetupDirection = -1;
|
||||
coopSetupRectsValid = false;
|
||||
// Ensure menu music resumes when closing the coop setup panel
|
||||
if (ctx.musicEnabled && *ctx.musicEnabled) {
|
||||
Audio::instance().playMenuMusic();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MenuState::showHelpPanel(bool show) {
|
||||
if (show) {
|
||||
if (!helpPanelVisible && !helpPanelAnimating) {
|
||||
@ -204,7 +252,8 @@ void MenuState::renderMainButtonTop(SDL_Renderer* renderer, float logicalScale,
|
||||
};
|
||||
|
||||
int startLevel = ctx.startLevelSelection ? *ctx.startLevelSelection : 0;
|
||||
ui::BottomMenu menu = ui::buildBottomMenu(params, startLevel);
|
||||
const bool coopVsAI = ctx.coopVsAI ? *ctx.coopVsAI : false;
|
||||
ui::BottomMenu menu = ui::buildBottomMenu(params, startLevel, coopVsAI);
|
||||
|
||||
const int hovered = (ctx.hoveredButton ? *ctx.hoveredButton : -1);
|
||||
const double baseAlpha = 1.0;
|
||||
@ -228,6 +277,48 @@ void MenuState::onExit() {
|
||||
}
|
||||
|
||||
void MenuState::handleEvent(const SDL_Event& e) {
|
||||
// Mouse input for COOP setup panel or inline coop buttons
|
||||
if (e.type == SDL_EVENT_MOUSE_BUTTON_DOWN && e.button.button == SDL_BUTTON_LEFT) {
|
||||
if (coopSetupRectsValid) {
|
||||
const float mx = static_cast<float>(e.button.x);
|
||||
const float my = static_cast<float>(e.button.y);
|
||||
if (mx >= lastLogicalVP.x && my >= lastLogicalVP.y && mx <= (lastLogicalVP.x + lastLogicalVP.w) && my <= (lastLogicalVP.y + lastLogicalVP.h)) {
|
||||
const float lx = (mx - lastLogicalVP.x) / std::max(0.0001f, lastLogicalScale);
|
||||
const float ly = (my - lastLogicalVP.y) / std::max(0.0001f, lastLogicalScale);
|
||||
|
||||
auto hit = [&](const SDL_FRect& r) {
|
||||
return lx >= r.x && lx <= (r.x + r.w) && ly >= r.y && ly <= (r.y + r.h);
|
||||
};
|
||||
|
||||
int chosen = -1;
|
||||
if (hit(coopSetupBtnRects[0])) chosen = 0;
|
||||
else if (hit(coopSetupBtnRects[1])) chosen = 1;
|
||||
|
||||
if (chosen != -1) {
|
||||
coopSetupSelected = chosen;
|
||||
const bool useAI = (coopSetupSelected == 1);
|
||||
if (ctx.coopVsAI) {
|
||||
*ctx.coopVsAI = useAI;
|
||||
}
|
||||
if (ctx.game) {
|
||||
ctx.game->setMode(GameMode::Cooperate);
|
||||
ctx.game->reset(ctx.startLevelSelection ? *ctx.startLevelSelection : 0);
|
||||
}
|
||||
if (ctx.coopGame) {
|
||||
ctx.coopGame->reset(ctx.startLevelSelection ? *ctx.startLevelSelection : 0);
|
||||
}
|
||||
showCoopSetupPanel(false);
|
||||
if (ctx.startPlayTransition) {
|
||||
ctx.startPlayTransition();
|
||||
} else if (ctx.stateManager) {
|
||||
ctx.stateManager->setState(AppState::Playing);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keyboard navigation for menu buttons
|
||||
if (e.type == SDL_EVENT_KEY_DOWN && !e.key.repeat) {
|
||||
// When the player uses the keyboard, don't let an old mouse hover keep focus on a button.
|
||||
@ -457,6 +548,47 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Coop setup panel navigation (modal within the menu)
|
||||
if ((coopSetupVisible || coopSetupAnimating) && coopSetupTransition > 0.0) {
|
||||
switch (e.key.scancode) {
|
||||
case SDL_SCANCODE_LEFT:
|
||||
case SDL_SCANCODE_A:
|
||||
coopSetupSelected = 0;
|
||||
buttonFlash = 1.0;
|
||||
return;
|
||||
case SDL_SCANCODE_RIGHT:
|
||||
case SDL_SCANCODE_D:
|
||||
coopSetupSelected = 1;
|
||||
buttonFlash = 1.0;
|
||||
return;
|
||||
case SDL_SCANCODE_ESCAPE:
|
||||
showCoopSetupPanel(false);
|
||||
return;
|
||||
case SDL_SCANCODE_RETURN:
|
||||
case SDL_SCANCODE_KP_ENTER:
|
||||
case SDL_SCANCODE_SPACE:
|
||||
{
|
||||
const bool useAI = (coopSetupSelected == 1);
|
||||
if (ctx.coopVsAI) {
|
||||
*ctx.coopVsAI = useAI;
|
||||
}
|
||||
// Start cooperative play
|
||||
if (ctx.game) {
|
||||
ctx.game->setMode(GameMode::Cooperate);
|
||||
ctx.game->reset(ctx.startLevelSelection ? *ctx.startLevelSelection : 0);
|
||||
}
|
||||
if (ctx.coopGame) {
|
||||
ctx.coopGame->reset(ctx.startLevelSelection ? *ctx.startLevelSelection : 0);
|
||||
}
|
||||
showCoopSetupPanel(false);
|
||||
triggerPlay();
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (e.key.scancode) {
|
||||
case SDL_SCANCODE_LEFT:
|
||||
case SDL_SCANCODE_UP:
|
||||
@ -489,15 +621,8 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
||||
triggerPlay();
|
||||
break;
|
||||
case 1:
|
||||
// Cooperative play
|
||||
if (ctx.game) {
|
||||
ctx.game->setMode(GameMode::Cooperate);
|
||||
ctx.game->reset(ctx.startLevelSelection ? *ctx.startLevelSelection : 0);
|
||||
}
|
||||
if (ctx.coopGame) {
|
||||
ctx.coopGame->reset(ctx.startLevelSelection ? *ctx.startLevelSelection : 0);
|
||||
}
|
||||
triggerPlay();
|
||||
// Cooperative play: open setup panel (2P vs AI)
|
||||
showCoopSetupPanel(true);
|
||||
break;
|
||||
case 2:
|
||||
// Start challenge run at level 1
|
||||
@ -566,6 +691,10 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
||||
}
|
||||
break;
|
||||
case SDL_SCANCODE_ESCAPE:
|
||||
if (coopSetupVisible && !coopSetupAnimating) {
|
||||
showCoopSetupPanel(false);
|
||||
return;
|
||||
}
|
||||
// If options panel is visible, hide it first.
|
||||
if (optionsVisible && !optionsAnimating) {
|
||||
optionsAnimating = true;
|
||||
@ -665,6 +794,21 @@ void MenuState::update(double frameMs) {
|
||||
}
|
||||
}
|
||||
|
||||
// Advance coop setup panel animation if active
|
||||
if (coopSetupAnimating) {
|
||||
double delta = (frameMs / coopSetupTransitionDurationMs) * static_cast<double>(coopSetupDirection);
|
||||
coopSetupTransition += delta;
|
||||
if (coopSetupTransition >= 1.0) {
|
||||
coopSetupTransition = 1.0;
|
||||
coopSetupVisible = true;
|
||||
coopSetupAnimating = false;
|
||||
} else if (coopSetupTransition <= 0.0) {
|
||||
coopSetupTransition = 0.0;
|
||||
coopSetupVisible = false;
|
||||
coopSetupAnimating = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Animate level selection highlight position toward the selected cell center
|
||||
if (levelTransition > 0.0 && (lastLogicalScale > 0.0f)) {
|
||||
// Recompute same grid geometry used in render to find target center
|
||||
@ -790,6 +934,8 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
|
||||
const float moveAmount = 420.0f; // increased so lower score rows slide further up
|
||||
|
||||
// Compute eased transition and delta to shift highscores when either options, level, or exit HUD is shown.
|
||||
// Exclude coopSetupTransition from the highscores slide so opening the
|
||||
// COOPERATE setup does not shift the highscores panel upward.
|
||||
float combinedTransition = static_cast<float>(std::max(
|
||||
std::max(std::max(optionsTransition, levelTransition), exitTransition),
|
||||
std::max(helpTransition, aboutTransition)
|
||||
@ -848,7 +994,9 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
|
||||
size_t maxDisplay = std::min(filtered.size(), size_t(10)); // display only top 10
|
||||
|
||||
// Draw highscores as an inline HUD-like panel (no opaque box), matching Options/Level/Exit style
|
||||
if (useFont) {
|
||||
// Keep highscores visible while the coop setup is animating; hide them only
|
||||
// once the coop setup is fully visible so the buttons can appear afterward.
|
||||
if (useFont && !coopSetupVisible) {
|
||||
const float panelW = (wantedType == "cooperate") ? std::min(920.0f, LOGICAL_W * 0.92f) : std::min(780.0f, LOGICAL_W * 0.85f);
|
||||
const float panelH = 36.0f + maxDisplay * 36.0f; // header + rows
|
||||
// Shift the entire highscores panel slightly left (~1.5% of logical width)
|
||||
@ -1112,6 +1260,46 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
|
||||
}
|
||||
}
|
||||
|
||||
// Inline COOP choice buttons: when COOPERATE is selected show two large
|
||||
// choice buttons in the highscores panel area (top of the screen).
|
||||
// coopSetupRectsValid is cleared each frame and set to true when buttons are drawn
|
||||
coopSetupRectsValid = false;
|
||||
// Draw the inline COOP choice buttons as soon as the coop setup starts
|
||||
// animating or is visible. Highscores are no longer slid upward when
|
||||
// the setup opens, so the buttons can show immediately.
|
||||
if (coopSetupAnimating || coopSetupVisible) {
|
||||
// Recompute panel geometry matching highscores layout above so buttons
|
||||
// appear centered inside the same visual area.
|
||||
const float panelW = std::min(920.0f, LOGICAL_W * 0.92f);
|
||||
const float panelShift = LOGICAL_W * 0.015f;
|
||||
const float panelBaseX = (LOGICAL_W - panelW) * 0.5f + contentOffsetX - panelShift;
|
||||
const float panelH = 36.0f + maxDisplay * 36.0f; // same as highscores panel
|
||||
// Highscores are animated upward by `panelDelta` while opening the coop setup.
|
||||
// We want the choice buttons to appear *after* that scroll, in the original
|
||||
// highscores area (not sliding offscreen with the scores).
|
||||
const float panelBaseY = scoresStartY - 20.0f;
|
||||
|
||||
// Make the choice buttons larger and center them vertically in the highscores area
|
||||
const float btnW2 = std::min(420.0f, panelW * 0.44f);
|
||||
const float btnH2 = 84.0f;
|
||||
const float gap = 28.0f;
|
||||
const float bx = panelBaseX + (panelW - (btnW2 * 2.0f + gap)) * 0.5f;
|
||||
const float by = panelBaseY + (panelH - btnH2) * 0.5f;
|
||||
|
||||
coopSetupBtnRects[0] = SDL_FRect{ bx, by, btnW2, btnH2 };
|
||||
coopSetupBtnRects[1] = SDL_FRect{ bx + btnW2 + gap, by, btnW2, btnH2 };
|
||||
coopSetupRectsValid = true;
|
||||
|
||||
SDL_Color bg{ 24, 36, 52, 220 };
|
||||
SDL_Color border{ 110, 200, 255, 220 };
|
||||
UIRenderer::drawButton(renderer, ctx.pixelFont, coopSetupBtnRects[0].x + btnW2 * 0.5f, coopSetupBtnRects[0].y + btnH2 * 0.5f,
|
||||
btnW2, btnH2, "2 PLAYERS", false, coopSetupSelected == 0, bg, border, false, nullptr);
|
||||
UIRenderer::drawButton(renderer, ctx.pixelFont, coopSetupBtnRects[1].x + btnW2 * 0.5f, coopSetupBtnRects[1].y + btnH2 * 0.5f,
|
||||
btnW2, btnH2, "COMPUTER (AI)", false, coopSetupSelected == 1, bg, border, false, nullptr);
|
||||
}
|
||||
// NOTE: slide-up COOP panel intentionally removed. Only the inline
|
||||
// highscores-area choice buttons are shown when coop setup is active.
|
||||
|
||||
// Inline exit HUD (no opaque background) - slides into the highscores area
|
||||
if (exitTransition > 0.0) {
|
||||
float easedE = static_cast<float>(exitTransition);
|
||||
@ -1466,3 +1654,5 @@ void MenuState::render(SDL_Renderer* renderer, float logicalScale, SDL_Rect logi
|
||||
FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "MenuState::render exit\n"); fclose(f); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user