basic gameplay for cooperative
This commit is contained in:
@ -144,4 +144,7 @@ void draw(SDL_Renderer* renderer, SDL_Texture*) {
|
||||
|
||||
double getLogoAnimCounter() { return logoAnimCounter; }
|
||||
int getHoveredButton() { return hoveredButton; }
|
||||
void spawn(float x, float y) {
|
||||
fireworks.emplace_back(x, y);
|
||||
}
|
||||
} // namespace AppFireworks
|
||||
|
||||
@ -6,4 +6,5 @@ namespace AppFireworks {
|
||||
void update(double frameMs);
|
||||
double getLogoAnimCounter();
|
||||
int getHoveredButton();
|
||||
void spawn(float x, float y);
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
#include "core/state/StateManager.h"
|
||||
|
||||
#include "gameplay/core/Game.h"
|
||||
#include "gameplay/coop/CoopGame.h"
|
||||
#include "gameplay/effects/LineEffect.h"
|
||||
|
||||
#include "graphics/effects/SpaceWarp.h"
|
||||
@ -228,6 +229,7 @@ struct TetrisApp::Impl {
|
||||
std::atomic<size_t> loadingStep{0};
|
||||
|
||||
std::unique_ptr<Game> game;
|
||||
std::unique_ptr<CoopGame> coopGame;
|
||||
std::vector<std::string> singleSounds;
|
||||
std::vector<std::string> doubleSounds;
|
||||
std::vector<std::string> tripleSounds;
|
||||
@ -242,7 +244,13 @@ struct TetrisApp::Impl {
|
||||
bool isFullscreen = false;
|
||||
bool leftHeld = false;
|
||||
bool rightHeld = false;
|
||||
bool p1LeftHeld = false;
|
||||
bool p1RightHeld = false;
|
||||
bool p2LeftHeld = false;
|
||||
bool p2RightHeld = false;
|
||||
double moveTimerMs = 0.0;
|
||||
double p1MoveTimerMs = 0.0;
|
||||
double p2MoveTimerMs = 0.0;
|
||||
double DAS = 170.0;
|
||||
double ARR = 40.0;
|
||||
SDL_Rect logicalVP{0, 0, LOGICAL_W, LOGICAL_H};
|
||||
@ -421,6 +429,8 @@ int TetrisApp::Impl::init()
|
||||
game->setGravityGlobalMultiplier(Config::Gameplay::GRAVITY_SPEED_MULTIPLIER);
|
||||
game->reset(startLevelSelection);
|
||||
|
||||
coopGame = std::make_unique<CoopGame>(startLevelSelection);
|
||||
|
||||
// Define voice line banks for gameplay callbacks
|
||||
singleSounds = {"well_played", "smooth_clear", "great_move"};
|
||||
doubleSounds = {"nice_combo", "you_fire", "keep_that_ryhtm"};
|
||||
@ -479,7 +489,10 @@ int TetrisApp::Impl::init()
|
||||
isFullscreen = Settings::instance().isFullscreen();
|
||||
leftHeld = false;
|
||||
rightHeld = false;
|
||||
p1LeftHeld = p1RightHeld = p2LeftHeld = p2RightHeld = false;
|
||||
moveTimerMs = 0;
|
||||
p1MoveTimerMs = 0.0;
|
||||
p2MoveTimerMs = 0.0;
|
||||
DAS = 170.0;
|
||||
ARR = 40.0;
|
||||
logicalVP = SDL_Rect{0, 0, LOGICAL_W, LOGICAL_H};
|
||||
@ -506,6 +519,7 @@ int TetrisApp::Impl::init()
|
||||
ctx = StateContext{};
|
||||
ctx.stateManager = stateMgr.get();
|
||||
ctx.game = game.get();
|
||||
ctx.coopGame = coopGame.get();
|
||||
ctx.scores = nullptr;
|
||||
ctx.starfield = &starfield;
|
||||
ctx.starfield3D = &starfield3D;
|
||||
@ -858,6 +872,9 @@ void TetrisApp::Impl::runLoop()
|
||||
if (e.key.scancode == SDL_SCANCODE_RETURN || e.key.scancode == SDL_SCANCODE_KP_ENTER || e.key.scancode == SDL_SCANCODE_SPACE) {
|
||||
if (game->getMode() == GameMode::Challenge) {
|
||||
game->startChallengeRun(1);
|
||||
} else if (game->getMode() == GameMode::Cooperate) {
|
||||
game->setMode(GameMode::Cooperate);
|
||||
game->reset(startLevelSelection);
|
||||
} else {
|
||||
game->setMode(GameMode::Endless);
|
||||
game->reset(startLevelSelection);
|
||||
@ -893,6 +910,13 @@ void TetrisApp::Impl::runLoop()
|
||||
if (game) game->setMode(GameMode::Endless);
|
||||
startMenuPlayTransition();
|
||||
break;
|
||||
case ui::BottomMenuItem::Cooperate:
|
||||
if (game) {
|
||||
game->setMode(GameMode::Cooperate);
|
||||
game->reset(startLevelSelection);
|
||||
}
|
||||
startMenuPlayTransition();
|
||||
break;
|
||||
case ui::BottomMenuItem::Challenge:
|
||||
if (game) {
|
||||
game->setMode(GameMode::Challenge);
|
||||
@ -1153,29 +1177,88 @@ void TetrisApp::Impl::runLoop()
|
||||
|
||||
if (state == AppState::Playing)
|
||||
{
|
||||
if (!game->isPaused()) {
|
||||
game->tickGravity(frameMs);
|
||||
game->updateElapsedTime();
|
||||
const bool coopActive = game && game->getMode() == GameMode::Cooperate && coopGame;
|
||||
|
||||
if (lineEffect.isActive()) {
|
||||
if (lineEffect.update(frameMs / 1000.0f)) {
|
||||
game->clearCompletedLines();
|
||||
if (coopActive) {
|
||||
// Coop DAS/ARR handling (per-side)
|
||||
const bool* ks = SDL_GetKeyboardState(nullptr);
|
||||
|
||||
auto handleSide = [&](CoopGame::PlayerSide side,
|
||||
bool leftHeldPrev,
|
||||
bool rightHeldPrev,
|
||||
double& timer,
|
||||
SDL_Scancode leftKey,
|
||||
SDL_Scancode rightKey,
|
||||
SDL_Scancode downKey) {
|
||||
bool left = ks[leftKey];
|
||||
bool right = ks[rightKey];
|
||||
bool down = ks[downKey];
|
||||
|
||||
coopGame->setSoftDropping(side, down);
|
||||
|
||||
int moveDir = 0;
|
||||
if (left && !right) moveDir = -1;
|
||||
else if (right && !left) moveDir = +1;
|
||||
|
||||
if (moveDir != 0) {
|
||||
if ((moveDir == -1 && !leftHeldPrev) || (moveDir == +1 && !rightHeldPrev)) {
|
||||
coopGame->move(side, moveDir);
|
||||
timer = DAS;
|
||||
} else {
|
||||
timer -= frameMs;
|
||||
if (timer <= 0) {
|
||||
coopGame->move(side, moveDir);
|
||||
timer += ARR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
timer = 0.0;
|
||||
}
|
||||
};
|
||||
|
||||
handleSide(CoopGame::PlayerSide::Left, p1LeftHeld, p1RightHeld, p1MoveTimerMs, SDL_SCANCODE_A, SDL_SCANCODE_D, SDL_SCANCODE_S);
|
||||
handleSide(CoopGame::PlayerSide::Right, p2LeftHeld, p2RightHeld, p2MoveTimerMs, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_DOWN);
|
||||
|
||||
p1LeftHeld = ks[SDL_SCANCODE_A];
|
||||
p1RightHeld = ks[SDL_SCANCODE_D];
|
||||
p2LeftHeld = ks[SDL_SCANCODE_LEFT];
|
||||
p2RightHeld = ks[SDL_SCANCODE_RIGHT];
|
||||
|
||||
if (!game->isPaused()) {
|
||||
coopGame->tickGravity(frameMs);
|
||||
coopGame->updateVisualEffects(frameMs);
|
||||
}
|
||||
|
||||
if (coopGame->isGameOver()) {
|
||||
state = AppState::GameOver;
|
||||
stateMgr->setState(state);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!game->isPaused()) {
|
||||
game->tickGravity(frameMs);
|
||||
game->updateElapsedTime();
|
||||
|
||||
if (lineEffect.isActive()) {
|
||||
if (lineEffect.update(frameMs / 1000.0f)) {
|
||||
game->clearCompletedLines();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (game->isGameOver())
|
||||
{
|
||||
if (game->score() > 0) {
|
||||
isNewHighScore = true;
|
||||
playerName.clear();
|
||||
SDL_StartTextInput(window);
|
||||
} else {
|
||||
isNewHighScore = false;
|
||||
ensureScoresLoaded();
|
||||
scores.submit(game->score(), game->lines(), game->level(), game->elapsed());
|
||||
if (game->isGameOver())
|
||||
{
|
||||
if (game->score() > 0) {
|
||||
isNewHighScore = true;
|
||||
playerName.clear();
|
||||
SDL_StartTextInput(window);
|
||||
} else {
|
||||
isNewHighScore = false;
|
||||
ensureScoresLoaded();
|
||||
scores.submit(game->score(), game->lines(), game->level(), game->elapsed());
|
||||
}
|
||||
state = AppState::GameOver;
|
||||
stateMgr->setState(state);
|
||||
}
|
||||
state = AppState::GameOver;
|
||||
stateMgr->setState(state);
|
||||
}
|
||||
}
|
||||
else if (state == AppState::Loading)
|
||||
|
||||
Reference in New Issue
Block a user