MXVK Vulkan Framework 0.24.0
C++20 Vulkan rendering framework for practical 2D and 3D application development with SDL3.
Loading...
Searching...
No Matches
acid.drop.cpp
Go to the documentation of this file.
1#include "mxvk/argz.hpp"
2#include "mxvk/mxvk.hpp"
4#include <SDL3/SDL.h>
5#include <algorithm>
6#include <climits>
7#include <cmath>
8#include <compare>
9#include <cstdlib>
10#include <cstring>
11#include <ctime>
12#include <fstream>
13#include <iostream>
14#include <random>
15#include <sstream>
16#include <string>
17#include <vector>
18
19#ifndef puzzle_ASSET_DIR
20#define puzzle_ASSET_DIR "."
21#endif
22
23constexpr int STARTX = 184;
24constexpr int STARTY = 61;
25constexpr int GRID_WIDTH = 8;
26constexpr int GRID_HEIGHT = 18;
27constexpr int BLOCK_SIZE = 31;
28constexpr int BLOCK_HEIGHT = 15;
29constexpr int BLOCK_SPACING = 1;
30constexpr int NEXT_PANEL_X = 450;
31constexpr int NEXT_PANEL_Y = 180;
32constexpr int FLASH_DURATION = 300;
33constexpr int SHADER_LEVEL_LINES = 10;
34constexpr int SHADER_LEVEL_COUNT = 4;
35
36enum {
49};
50
60
61struct GameData {
62 unsigned long score;
63 int lines;
64 int speed;
66
68 void newgame() {
69 score = 0;
70 lines = 0;
71 speed = 20;
72 lineamt = 0;
73 }
74 void addline() {
75 lines++;
76 score += 6;
77 lineamt++;
78 if (lineamt >= 10) {
79 lineamt = 0;
80 speed = std::max(5, speed - 4);
81 }
82 }
83};
84
85struct BlockColor {
86 int c1, c2, c3;
87 void randcolor() {
88 c1 = 1 + rand() % 9;
89 c2 = 1 + rand() % 9;
90 c3 = 1 + rand() % 9;
91 if (c1 == c2 && c1 == c3)
92 randcolor();
93 }
94 void shiftcolor(bool dir) {
95 int t1 = c1, t2 = c2, t3 = c3;
96 if (dir) {
97 c1 = t3;
98 c2 = t1;
99 c3 = t2;
100 } else {
101 c1 = t2;
102 c2 = t3;
103 c3 = t1;
104 }
105 }
106};
107
116
121
123
124 void init_matrix() {
125 memset(grid, 0, sizeof(grid));
126 Game.newgame();
127 block.color.randcolor();
128 nextblock.color.randcolor();
129 }
130};
131
132struct Score {
133 int score;
134 std::string name;
135 Score(std::string name, int score) : score(score), name(name) {}
136 Score() : score{0}, name{} {}
137 auto operator<=>(const Score &s) const {
138 return s.score <=> score;
139 }
140};
141
143 public:
145 read();
146 }
148 write();
149 }
150
151 void addScore(const std::string &name, int score) {
152 scores.push_back({name, score});
153 sort();
154
155 if (scores.size() > 10) {
156 scores.resize(10);
157 }
158 }
159 void sort() {
160 std::sort(scores.begin(), scores.end());
161 }
162
163 bool qualifiesForHighScore(int score) {
164 if (scores.size() < 10)
165 return true;
166 return score > scores.back().score;
167 }
168
169 const std::vector<Score> &getScores() const {
170 return scores;
171 }
172
173 void init() {
174 scores.clear();
175 for (int i = 0; i < 10; ++i)
176 scores.push_back(Score("Anonymous", (10 - i) * 100));
177 }
178
179 void read() {
180 std::fstream file;
181 file.open("./scores.dat", std::ios::in);
182 if (!file.is_open()) {
183 std::cout << "No scores file.\n";
184 init();
185 return;
186 }
187 std::string input;
188 int count = 0;
189 while (std::getline(file, input)) {
190 if (count >= 10)
191 break;
192 if (input.length() > 0) {
193 auto pos = input.find(":");
194 if (pos != std::string::npos) {
195 std::string left = input.substr(0, pos);
196 std::string right = input.substr(pos + 1);
197 scores.push_back(Score(left, std::strtol(right.c_str(), 0, 10)));
198 count++;
199 }
200 }
201 }
202 file.close();
203 sort();
204 }
205 void write() {
206 std::fstream file;
207 file.open("./scores.dat", std::ios::out);
208 if (!file.is_open()) {
209 std::cerr << "Could not open score file..\n";
210 return;
211 }
212 for (auto &s : scores) {
213 file << s.name << ":" << s.score << std::endl;
214 }
215 file.close();
216 }
217
218 private:
219 std::vector<Score> scores;
220};
221
223 private:
224 std::string current_path;
225 int w = 640;
226 int h = 480;
227 HighScores scores;
229 ScreenState currentScreen = SCREEN_INTRO;
230 Uint32 lastTick = 0;
231 int cursorPos = 0;
232 bool paused = false;
233 int optionsCursor = 0;
234 int difficultySetting = 1;
235 bool shaderEffectsEnabled = true;
236 bool enteringName = false;
237 std::string playerName;
238 int finalScore = 0;
239 mxvk::VK_Sprite *scoresBackground = nullptr;
240 mxvk::VK_Sprite *creditScreen = nullptr;
241 int matchBonus = 0;
242 int lastFontSize = 0;
243 bool flashActive = false;
244 Uint32 flashStartTime = 0;
245 bool flashGrid[GRID_WIDTH][GRID_HEIGHT] = {{false}};
246 std::vector<mxvk::VK_Sprite *> grid_blocks;
247 mxvk::VK_Sprite *gamebg[SHADER_LEVEL_COUNT] = {nullptr};
248 int shaderLevel = 0;
249 mxvk::VK_Sprite *startScreen = nullptr;
250 mxvk::VK_Sprite *introScreen = nullptr;
251 SDL_Gamepad *gamepad = nullptr;
252 SDL_JoystickID gamepadId = 0;
253 static constexpr Sint16 JOYSTICK_DEAD_ZONE = 12000;
254 Uint32 joyRepeatLeft = 0;
255 Uint32 joyRepeatRight = 0;
256 Uint32 joyRepeatDown = 0;
257 Uint32 joyRepeatUp = 0;
258 static constexpr Uint32 JOY_REPEAT_DELAY = 150;
259
260 public:
261 MasterPieceWindow(const std::string &path, int wx, int wy, bool full, bool enable_vsync)
262 : mxvk::VK_Window("-[ Acid Drop - Vulkan ]-", wx, wy, full, MXVK_VALIDATION, enable_vsync),
263 current_path((path.empty() || path == ".") ? std::string(puzzle_ASSET_DIR) : path),
264 w(wx),
265 h(wy) {
268 srand((unsigned int)time(0));
269 lastTick = SDL_GetTicks();
270 }
271
273 if (gamepad != nullptr) {
274 SDL_CloseGamepad(gamepad);
275 gamepad = nullptr;
276 }
277 }
278
279 void initGfx() {
280 std::string vertShader = current_path + "/data/sprite_vert.spv";
281 std::string fragShader = current_path + "/data/sprite_kaleidoscope.spv";
282 const char *fragShaders[SHADER_LEVEL_COUNT] = {
283 "sprite_kaleidoscope.spv",
284 "sprite_kaleidoscope2.spv",
285 "sprite_kaleidoscope3.spv",
286 "sprite_kaleidoscope4.spv"};
287 for (int i = 0; i < SHADER_LEVEL_COUNT; i++) {
288 gamebg[i] = createSprite(current_path + "/data/gamebg.png", vertShader, current_path + "/data/" + fragShaders[i]);
289 }
290 introScreen = createSprite(current_path + "/data/intro.png", current_path + "/data/sprite_vert.spv", current_path + "/data/sprite_bubble.spv");
291 startScreen = createSprite(current_path + "/data/universe.png", current_path + "/data/sprite_vert.spv", fragShader);
292 creditScreen = createSprite(current_path + "/data/logo.png", current_path + "/data/sprite_vert.spv", current_path + "/data/sprite_time.spv");
293 scoresBackground = createSprite(current_path + "/data/bg.png", current_path + "/data/sprite_vert.spv", current_path + "/data/sprite_time.spv");
294
295 const char *blockFiles[] = {
296 "block_black.png", "block_yellow.png", "block_orange.png", "block_ltblue.png",
297 "block_dblue.png", "block_purple.png", "block_pink.png", "block_gray.png",
298 "block_red.png", "block_green.png", "block_clear.png"};
299 for (int i = 0; i < BLOCK_COUNT; i++) {
300 grid_blocks.push_back(createSprite(current_path + "/data/" + blockFiles[i], current_path + "/data/sprite_vert.spv", current_path + "/data/sprite_frag.spv"));
301 }
303 }
304
306 int fontSize = (int)(20.0f * ((float)h / 480.0f));
307 fontSize = std::max(16, std::min(128, fontSize));
308 if (fontSize != lastFontSize) {
309 lastFontSize = fontSize;
310 setFont(current_path + "/data/font.ttf", fontSize);
312 }
313 }
314
315 bool openGamepad(SDL_JoystickID id) {
316 if (gamepad != nullptr && gamepadId == id) {
317 return true;
318 }
319 if (gamepad != nullptr) {
320 SDL_CloseGamepad(gamepad);
321 gamepad = nullptr;
322 gamepadId = 0;
323 }
324 gamepad = SDL_OpenGamepad(id);
325 if (gamepad == nullptr) {
326 return false;
327 }
328 gamepadId = id;
329 return true;
330 }
331
333 if (gamepad != nullptr) {
334 return;
335 }
336 int count = 0;
337 SDL_JoystickID *ids = SDL_GetGamepads(&count);
338 if (ids == nullptr || count <= 0) {
339 if (ids != nullptr) {
340 SDL_free(ids);
341 }
342 return;
343 }
344 openGamepad(ids[0]);
345 SDL_free(ids);
346 }
347
349 for (int i = 0; i < SHADER_LEVEL_COUNT; i++) {
350 if (gamebg[i])
351 gamebg[i]->setEffectsEnabled(shaderEffectsEnabled);
352 }
353 if (introScreen)
354 introScreen->setEffectsEnabled(shaderEffectsEnabled);
355 if (startScreen)
356 startScreen->setEffectsEnabled(shaderEffectsEnabled);
357 if (creditScreen)
358 creditScreen->setEffectsEnabled(shaderEffectsEnabled);
359 if (scoresBackground)
360 scoresBackground->setEffectsEnabled(shaderEffectsEnabled);
361 for (auto *block : grid_blocks) {
362 if (block)
363 block->setEffectsEnabled(shaderEffectsEnabled);
364 }
365 }
366
368 return (int)(lastFontSize * 0.5f);
369 }
370
372 return (int)(40.0f * ((float)h / 480.0f));
373 }
374
375 int scaleY(int baseY) {
376 return (int)(baseY * ((float)h / 480.0f));
377 }
378
379 int centerX(const char *text) {
380 int width, height;
381 if (getTextDimensions(text, width, height)) {
382 return w / 2 - width / 2;
383 }
384 return w / 2 - (int)(strlen(text) * getCharWidth() / 2);
385 }
386
387 void proc() override {
388 if (window != nullptr) {
389 SDL_GetWindowSizeInPixels(window.get(), &w, &h);
390 }
391 if (startScreen == nullptr) {
392 initGfx();
393 }
395 switch (currentScreen) {
396 case SCREEN_INTRO:
397 updateIntro();
398 break;
399 case SCREEN_START:
400 updateStart();
401 break;
402 case SCREEN_GAME:
403 updateGame();
404 break;
405 case SCREEN_GAMEOVER:
407 break;
408 case SCREEN_OPTIONS:
410 break;
411 case SCREEN_CREDITS:
413 break;
414 case SCREEN_SCORES:
415 updateScores();
416 break;
417 }
418 }
419
420 void updateIntro() {
421 if (introScreen) {
422 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
423 introScreen->setShaderParams(time_f, 1.0f, 0.8f, 1.2f);
424 introScreen->drawSpriteRect(0, 0, w, h);
425 }
426 Uint32 currentTick = SDL_GetTicks();
427 if (currentTick - lastTick > 5000) {
428 currentScreen = SCREEN_START;
429 lastTick = currentTick;
430 }
431 }
432
433 void updateStart() {
434 if (startScreen) {
435 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
436 startScreen->setShaderParams(time_f, 0.0f, 0.0f, 0.0f);
437 startScreen->drawSpriteRect(0, 0, w, h);
438 }
439 int titleY = scaleY(100);
440 int menuStartY = scaleY(180);
441 int spacing = getMenuSpacing();
442 const char *title = "Liquid Acid Drop";
443 printText(title, centerX(title), titleY, {255, 255, 0, 255});
444 const char *menuItems[] = {"New Game", "High Scores", "Options", "Credits", "Quit"};
445 for (int i = 0; i < 5; i++) {
446 SDL_Color col = (i == cursorPos) ? SDL_Color{255, 255, 0, 255} : SDL_Color{255, 255, 255, 255};
447 printText(menuItems[i], centerX(menuItems[i]), menuStartY + i * spacing, col);
448 }
449 printText(">>", centerX(menuItems[cursorPos]) - scaleY(30), menuStartY + cursorPos * spacing, {255, 255, 0, 255});
450 }
451
452 void updateGame() {
453 if (!paused) {
454 logic();
455 }
456 drawGame();
457 }
458
460 if (gamebg[shaderLevel]) {
461 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
462 gamebg[shaderLevel]->setShaderParams(time_f, 0.0f, 0.0f, 0.0f);
463 gamebg[shaderLevel]->drawSpriteRect(0, 0, w, h);
464 }
465
466 const char *gameOverText = "GAME OVER!";
467 printText(gameOverText, centerX(gameOverText), h / 2 - 80, {255, 0, 0, 255});
468
469 std::ostringstream stream;
470 stream << "Final Score: " << matrix.Game.score;
471 std::string scoreStr = stream.str();
472 printText(scoreStr.c_str(), centerX(scoreStr.c_str()), h / 2 - 20, {255, 255, 255, 255});
473
474 stream.str("");
475 stream << "Tabs: " << matrix.Game.lines;
476 std::string linesStr = stream.str();
477 printText(linesStr.c_str(), centerX(linesStr.c_str()), h / 2 + 40, {255, 255, 255, 255});
478
479 const char *returnText = "Press ENTER to view High Scores";
480 printText(returnText, centerX(returnText), h / 2 + 120, {255, 255, 0, 255});
481 }
482
484 finalScore = matrix.Game.score;
485 playerName = "";
486 enteringName = scores.qualifiesForHighScore(finalScore);
487 currentScreen = SCREEN_SCORES;
488 }
489
491 if (startScreen) {
492 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
493 startScreen->setShaderParams(time_f, 0.0f, 0.0f, 0.0f);
494 startScreen->drawSpriteRect(0, 0, w, h);
495 }
496
497 int titleY = scaleY(100);
498 int optStartY = scaleY(180);
499 int spacing = getMenuSpacing();
500
501 const char *optTitle = "OPTIONS";
502 printText(optTitle, centerX(optTitle), titleY, {255, 255, 0, 255});
503
504 const char *difficultyLabels[] = {"Easy", "Normal", "Hard"};
505 std::string diffText = std::string("< Difficulty: ") + difficultyLabels[difficultySetting] + " >";
506 SDL_Color diffColor = (optionsCursor == 0) ? SDL_Color{255, 255, 0, 255} : SDL_Color{255, 255, 255, 255};
507 printText(diffText.c_str(), centerX(diffText.c_str()), optStartY, diffColor);
508 if (optionsCursor == 0) {
509 int diffWidth, diffHeight;
510 if (!getTextDimensions(diffText.c_str(), diffWidth, diffHeight)) {
511 diffWidth = static_cast<int>(diffText.size()) * getCharWidth();
512 }
513 printText(">>", w / 2 - diffWidth / 2 - scaleY(30), optStartY, {255, 255, 0, 255});
514 }
515
516 std::string fxText = std::string("< Shader Effects: ") + (shaderEffectsEnabled ? "On" : "Off") + " >";
517 SDL_Color fxColor = (optionsCursor == 1) ? SDL_Color{255, 255, 0, 255} : SDL_Color{255, 255, 255, 255};
518 printText(fxText.c_str(), centerX(fxText.c_str()), optStartY + spacing, fxColor);
519 if (optionsCursor == 1) {
520 int fxWidth, fxHeight;
521 if (!getTextDimensions(fxText.c_str(), fxWidth, fxHeight)) {
522 fxWidth = static_cast<int>(fxText.size()) * getCharWidth();
523 }
524 printText(">>", w / 2 - fxWidth / 2 - scaleY(30), optStartY + spacing, {255, 255, 0, 255});
525 }
526
527 const char *backText = "Back";
528 SDL_Color backColor = (optionsCursor == 2) ? SDL_Color{255, 255, 0, 255} : SDL_Color{255, 255, 255, 255};
529 printText(backText, centerX(backText), optStartY + spacing * 2, backColor);
530 if (optionsCursor == 2) {
531 int backWidth, backHeight;
532 if (!getTextDimensions(backText, backWidth, backHeight)) {
533 backWidth = static_cast<int>(std::strlen(backText)) * getCharWidth();
534 }
535 printText(">>", w / 2 - backWidth / 2 - scaleY(30), optStartY + spacing * 2, {255, 255, 0, 255});
536 }
537
538 const char *instructions = "UP/DOWN: Select LEFT/RIGHT: Change ENTER: Confirm";
539 printText(instructions, centerX(instructions), scaleY(350), {200, 200, 200, 255});
540 }
541
543 if (creditScreen) {
544 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
545 creditScreen->setShaderParams(time_f, 0.0f, 0.0f, 0.0f);
546 creditScreen->drawSpriteRect(0, 0, w, h);
547 }
548
549 const char *credReturn = "Press Return to return";
550 printText(credReturn, centerX(credReturn), scaleY(360), {255, 255, 0, 255});
551 }
552
554
555 if (scoresBackground) {
556 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
557 scoresBackground->setShaderParams(time_f, 0.0f, 0.0f, 0.0f);
558 scoresBackground->drawSpriteRect(0, 0, w, h);
559 }
560
561 int titleY = scaleY(40);
562 int listStartY = scaleY(90);
563 int lineHeight = scaleY(32);
564
565 const char *title = "HIGH SCORES";
566 printText(title, centerX(title), titleY, {255, 255, 0, 255});
567
568 const auto &scoreList = scores.getScores();
569 for (size_t i = 0; i < scoreList.size() && i < 10; ++i) {
570 std::ostringstream stream;
571 stream << (i + 1) << ". " << scoreList[i].name << " " << scoreList[i].score;
572 std::string scoreStr = stream.str();
573
574 SDL_Color color = {255, 0, 150, 255};
575
576 if (enteringName && finalScore > 0) {
577
578 int rank = 0;
579 for (size_t j = 0; j < scoreList.size(); ++j) {
580 if (finalScore > scoreList[j].score) {
581 rank = j;
582 break;
583 }
584 rank = j + 1;
585 }
586 if ((int)i == rank) {
587 color = {0, 255, 0, 255};
588 }
589 }
590
591 printText(scoreStr.c_str(), scaleY(100), listStartY + (int)i * lineHeight, color);
592 }
593
594 if (enteringName) {
595 int entryY = scaleY(420);
596
597 std::ostringstream stream;
598 stream << "Your Score: " << finalScore;
599 std::string yourScore = stream.str();
600 printText(yourScore.c_str(), centerX(yourScore.c_str()), entryY - lineHeight * 2, {255, 255, 0, 255});
601
602 const char *prompt = "Enter your name:";
603 printText(prompt, centerX(prompt), entryY - lineHeight, {255, 255, 255, 255});
604
605 std::string displayName = playerName + "_";
606 printText(displayName.c_str(), centerX(displayName.c_str()), entryY, {0, 255, 255, 255});
607
608 const char *instructions = "Type name, ENTER to confirm, BACKSPACE to delete or Escape/Back";
609 printText(instructions, centerX(instructions), entryY + lineHeight, {200, 0, 150, 255});
610 } else {
611 const char *returnText = "Press ENTER to return to menu";
612 printText(returnText, centerX(returnText), scaleY(440), {255, 0, 0, 255});
613 }
614 }
615
616 void drawGame() {
617 float scaleX = (float)w / 640.0f;
618 float scaleY = (float)h / 480.0f;
619 if (gamebg[shaderLevel]) {
620 float time_f = static_cast<float>(SDL_GetTicks()) / 1000.0f;
621 gamebg[shaderLevel]->setShaderParams(time_f, 0.0f, 0.0f, 0.0f);
622 gamebg[shaderLevel]->drawSpriteRect(0, 0, w, h);
623 }
624 drawmatrix(scaleX, scaleY);
625 drawblock(scaleX, scaleY);
626 drawnext(scaleX, scaleY);
627 std::ostringstream stream;
628 stream << "Score:" << matrix.Game.score;
629 printText(stream.str().c_str(), (int)(200 * scaleX), (int)(80 * scaleY) - (lastFontSize + (lastFontSize / 4)), {255, 255, 255, 255});
630
631 stream.str("");
632 stream << "Tabs:" << matrix.Game.lines;
633 printText(stream.str().c_str(), (int)(310 * scaleX), (int)(80 * scaleY) - (lastFontSize + (lastFontSize / 4)), {255, 255, 255, 255});
634
635 if (paused) {
636 const char *pausedText = "PAUSED - Press P to Continue";
637 int pausedWidth, pausedHeight;
638 if (!getTextDimensions(pausedText, pausedWidth, pausedHeight)) {
639 pausedWidth = static_cast<int>(std::strlen(pausedText)) * getCharWidth();
640 }
641 printText(pausedText, w / 2 - pausedWidth / 2, h / 2, {255, 255, 0, 255});
642 }
643 }
644
645 void drawmatrix(float scaleX, float scaleY) {
646 Uint32 now = SDL_GetTicks();
647 bool showFlash = flashActive && ((now - flashStartTime) / 50 % 2 == 0);
648
649 for (int i = 0; i < GRID_WIDTH; i++) {
650 for (int j = 0; j < GRID_HEIGHT; j++) {
651 int blockType = matrix.grid[i][j];
652 if (blockType > 0 && blockType < BLOCK_COUNT &&
653 static_cast<size_t>(blockType) < grid_blocks.size()) {
654 int x = STARTX + i * (BLOCK_SIZE + BLOCK_SPACING);
655 int y = STARTY + j * (BLOCK_HEIGHT + BLOCK_SPACING) + 10;
656
657 if (flashActive && flashGrid[i][j] && !showFlash) {
658 continue;
659 }
660
661 if (grid_blocks[blockType]) {
662 grid_blocks[blockType]->drawSpriteRect(
663 (int)(x * scaleX), (int)(y * scaleY) + 10,
664 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
665 }
666 }
667 }
668 }
669 }
670
671 void drawblock(float scaleX, float scaleY) {
672 int c1 = matrix.block.color.c1;
673 int c2 = matrix.block.color.c2;
674 int c3 = matrix.block.color.c3;
675
676 if (matrix.block.horizontal) {
677
678 int screenY = STARTY + matrix.block.y * (BLOCK_HEIGHT + BLOCK_SPACING) + 10;
679 for (int i = 0; i < 3; i++) {
680 int color = (i == 0) ? c1 : (i == 1) ? c2
681 : c3;
682 int bx = STARTX + (matrix.block.x + i) * (BLOCK_SIZE + BLOCK_SPACING);
683 if (matrix.block.x + i >= 0 && matrix.block.x + i < GRID_WIDTH &&
684 color > 0 && color < BLOCK_COUNT && grid_blocks[color]) {
685 grid_blocks[color]->drawSpriteRect(
686 (int)(bx * scaleX), (int)(screenY * scaleY),
687 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
688 }
689 }
690 } else {
691
692 int bx = STARTX + matrix.block.x * (BLOCK_SIZE + BLOCK_SPACING);
693 int y0 = matrix.block.y;
694 int y1 = matrix.block.y + 1;
695 int y2 = matrix.block.y + 2;
696
697 if (y0 >= 0 && c1 > 0 && c1 < BLOCK_COUNT && grid_blocks[c1]) {
698 int screenY = STARTY + y0 * (BLOCK_HEIGHT + BLOCK_SPACING) + 10;
699 grid_blocks[c1]->drawSpriteRect(
700 (int)(bx * scaleX), (int)(screenY * scaleY),
701 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
702 }
703 if (y1 >= 0 && c2 > 0 && c2 < BLOCK_COUNT && grid_blocks[c2]) {
704 int screenY = STARTY + y1 * (BLOCK_HEIGHT + BLOCK_SPACING) + 10;
705 grid_blocks[c2]->drawSpriteRect(
706 (int)(bx * scaleX), (int)(screenY * scaleY),
707 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
708 }
709 if (y2 >= 0 && c3 > 0 && c3 < BLOCK_COUNT && grid_blocks[c3]) {
710 int screenY = STARTY + y2 * (BLOCK_HEIGHT + BLOCK_SPACING) + 10;
711 grid_blocks[c3]->drawSpriteRect(
712 (int)(bx * scaleX), (int)(screenY * scaleY),
713 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
714 }
715 }
716 }
717
718 void drawnext(float scaleX, float scaleY) {
719
720 int bx = NEXT_PANEL_X + 70;
721 int by = NEXT_PANEL_Y + 15;
722
723 int c1 = matrix.nextblock.color.c1;
724 int c2 = matrix.nextblock.color.c2;
725 int c3 = matrix.nextblock.color.c3;
726
727 if (c1 > 0 && c1 < BLOCK_COUNT && grid_blocks[c1]) {
728 grid_blocks[c1]->drawSpriteRect(
729 (int)(bx * scaleX), (int)(by * scaleY),
730 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
731 }
732 if (c2 > 0 && c2 < BLOCK_COUNT && grid_blocks[c2]) {
733 grid_blocks[c2]->drawSpriteRect(
734 (int)(bx * scaleX), (int)((by + BLOCK_HEIGHT + BLOCK_SPACING) * scaleY),
735 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
736 }
737 if (c3 > 0 && c3 < BLOCK_COUNT && grid_blocks[c3]) {
738 grid_blocks[c3]->drawSpriteRect(
739 (int)(bx * scaleX), (int)((by + (BLOCK_HEIGHT + BLOCK_SPACING) * 2) * scaleY),
740 (int)(BLOCK_SIZE * scaleX), (int)(BLOCK_HEIGHT * scaleY));
741 }
742 }
743
744 void logic() {
745 static Uint32 lastMove = 0;
746 Uint32 now = SDL_GetTicks();
747
748 if (flashActive) {
749 if (now - flashStartTime >= FLASH_DURATION) {
750
752 flashActive = false;
753 applyGravity();
754
755 if (findMatches()) {
756 startFlash();
757 }
758 }
759 return;
760 }
761
762 if (now - lastMove > (Uint32)(matrix.Game.speed * 50)) {
763 lastMove = now;
764
765 if (canMoveDown()) {
766 matrix.block.y++;
767 } else {
768
769 placeBlock();
770 applyGravity();
771
772 if (findMatches()) {
773 startFlash();
774 } else {
775
777
778 if (checkGameOver()) {
779 currentScreen = SCREEN_GAMEOVER;
780 }
781 }
782 }
783 }
784 }
785
786 bool canMoveDown() {
787 int bx = matrix.block.x;
788 int by = matrix.block.y;
789
790 if (matrix.block.horizontal) {
791
792 if (by + 1 >= GRID_HEIGHT)
793 return false;
794 for (int i = 0; i < 3; i++) {
795 if (bx + i >= 0 && bx + i < GRID_WIDTH && matrix.grid[bx + i][by + 1] > 0)
796 return false;
797 }
798 } else {
799
800 if (by + 3 >= GRID_HEIGHT)
801 return false;
802 if (matrix.grid[bx][by + 3] > 0)
803 return false;
804 }
805
806 return true;
807 }
808
809 bool canMoveLeft() {
810 if (matrix.block.x <= 0)
811 return false;
812 int bx = matrix.block.x - 1;
813 int by = matrix.block.y;
814
815 if (matrix.block.horizontal) {
816
817 if (by >= 0 && by < GRID_HEIGHT && matrix.grid[bx][by] > 0)
818 return false;
819 } else {
820
821 if (by >= 0 && by < GRID_HEIGHT && matrix.grid[bx][by] > 0)
822 return false;
823 if (by + 1 >= 0 && by + 1 < GRID_HEIGHT && matrix.grid[bx][by + 1] > 0)
824 return false;
825 if (by + 2 >= 0 && by + 2 < GRID_HEIGHT && matrix.grid[bx][by + 2] > 0)
826 return false;
827 }
828
829 return true;
830 }
831
833 int by = matrix.block.y;
834
835 if (matrix.block.horizontal) {
836
837 if (matrix.block.x + 3 >= GRID_WIDTH)
838 return false;
839 int bx = matrix.block.x + 3;
840 if (by >= 0 && by < GRID_HEIGHT && matrix.grid[bx][by] > 0)
841 return false;
842 } else {
843
844 if (matrix.block.x >= GRID_WIDTH - 1)
845 return false;
846 int bx = matrix.block.x + 1;
847 if (by >= 0 && by < GRID_HEIGHT && matrix.grid[bx][by] > 0)
848 return false;
849 if (by + 1 >= 0 && by + 1 < GRID_HEIGHT && matrix.grid[bx][by + 1] > 0)
850 return false;
851 if (by + 2 >= 0 && by + 2 < GRID_HEIGHT && matrix.grid[bx][by + 2] > 0)
852 return false;
853 }
854
855 return true;
856 }
857
858 void placeBlock() {
859 int bx = matrix.block.x;
860 int by = matrix.block.y;
861
862 if (matrix.block.horizontal) {
863
864 if (by >= 0 && by < GRID_HEIGHT) {
865 if (bx >= 0 && bx < GRID_WIDTH)
866 matrix.grid[bx][by] = matrix.block.color.c1;
867 if (bx + 1 >= 0 && bx + 1 < GRID_WIDTH)
868 matrix.grid[bx + 1][by] = matrix.block.color.c2;
869 if (bx + 2 >= 0 && bx + 2 < GRID_WIDTH)
870 matrix.grid[bx + 2][by] = matrix.block.color.c3;
871 }
872 } else {
873
874 if (by >= 0 && by < GRID_HEIGHT)
875 matrix.grid[bx][by] = matrix.block.color.c1;
876 if (by + 1 >= 0 && by + 1 < GRID_HEIGHT)
877 matrix.grid[bx][by + 1] = matrix.block.color.c2;
878 if (by + 2 >= 0 && by + 2 < GRID_HEIGHT)
879 matrix.grid[bx][by + 2] = matrix.block.color.c3;
880 }
881 }
882
883 bool findMatches() {
884
885 memset(flashGrid, false, sizeof(flashGrid));
886 bool foundMatch = false;
887 matchBonus = 0;
888
889 for (int j = 0; j < GRID_HEIGHT; j++) {
890 for (int i = 0; i < GRID_WIDTH; i++) {
891 if (matrix.grid[i][j] > 0) {
892 int color = matrix.grid[i][j];
893 int count = 1;
894
895 while (i + count < GRID_WIDTH && matrix.grid[i + count][j] == color) {
896 count++;
897 }
898 if (count >= 3) {
899 for (int k = 0; k < count; k++) {
900 flashGrid[i + k][j] = true;
901 }
902 foundMatch = true;
903
904 if (count == 4)
905 matchBonus += 25;
906 else if (count >= 5)
907 matchBonus += 50;
908 }
909 }
910 }
911 }
912
913 for (int i = 0; i < GRID_WIDTH; i++) {
914 for (int j = 0; j < GRID_HEIGHT; j++) {
915 if (matrix.grid[i][j] > 0) {
916 int color = matrix.grid[i][j];
917 int count = 1;
918
919 while (j + count < GRID_HEIGHT && matrix.grid[i][j + count] == color) {
920 count++;
921 }
922 if (count >= 3) {
923 for (int k = 0; k < count; k++) {
924 flashGrid[i][j + k] = true;
925 }
926 foundMatch = true;
927
928 if (count == 4)
929 matchBonus += 25;
930 else if (count >= 5)
931 matchBonus += 50;
932 }
933 }
934 }
935 }
936
937 for (int i = 0; i < GRID_WIDTH; i++) {
938 for (int j = 0; j < GRID_HEIGHT; j++) {
939 if (matrix.grid[i][j] > 0) {
940 int color = matrix.grid[i][j];
941 int count = 1;
942 while (i + count < GRID_WIDTH && j + count < GRID_HEIGHT &&
943 matrix.grid[i + count][j + count] == color) {
944 count++;
945 }
946 if (count >= 3) {
947 for (int k = 0; k < count; k++) {
948 flashGrid[i + k][j + k] = true;
949 }
950 foundMatch = true;
951
952 if (count == 4)
953 matchBonus += 35;
954 else if (count >= 5)
955 matchBonus += 75;
956 }
957 }
958 }
959 }
960
961 for (int i = 0; i < GRID_WIDTH; i++) {
962 for (int j = 0; j < GRID_HEIGHT; j++) {
963 if (matrix.grid[i][j] > 0) {
964 int color = matrix.grid[i][j];
965 int count = 1;
966 while (i - count >= 0 && j + count < GRID_HEIGHT &&
967 matrix.grid[i - count][j + count] == color) {
968 count++;
969 }
970 if (count >= 3) {
971 for (int k = 0; k < count; k++) {
972 flashGrid[i - k][j + k] = true;
973 }
974 foundMatch = true;
975
976 if (count == 4)
977 matchBonus += 35;
978 else if (count >= 5)
979 matchBonus += 75;
980 }
981 }
982 }
983 }
984
985 return foundMatch;
986 }
987
988 void startFlash() {
989 flashActive = true;
990 flashStartTime = SDL_GetTicks();
991 }
992
994 for (int i = 0; i < GRID_WIDTH; i++) {
995 for (int j = 0; j < GRID_HEIGHT; j++) {
996 if (flashGrid[i][j]) {
997 matrix.grid[i][j] = 0;
998 matrix.Game.addline();
999 }
1000 }
1001 }
1002 matrix.Game.score += matchBonus;
1003 matchBonus = 0;
1004 int newLevel = matrix.Game.lines / SHADER_LEVEL_LINES;
1005 if (newLevel >= SHADER_LEVEL_COUNT)
1006 newLevel = SHADER_LEVEL_COUNT - 1;
1007 if (newLevel != shaderLevel) {
1008 shaderLevel = newLevel;
1009 std::cout << "level increased to: " << shaderLevel << std::endl;
1010 }
1011 memset(flashGrid, false, sizeof(flashGrid));
1012 spawnNewBlock();
1013 if (checkGameOver()) {
1014 currentScreen = SCREEN_GAMEOVER;
1015 }
1016 }
1017
1019 bool moved;
1020 do {
1021 moved = false;
1022 for (int i = 0; i < GRID_WIDTH; i++) {
1023 for (int j = GRID_HEIGHT - 2; j >= 0; j--) {
1024 if (matrix.grid[i][j] > 0 && matrix.grid[i][j + 1] == 0) {
1025
1026 matrix.grid[i][j + 1] = matrix.grid[i][j];
1027 matrix.grid[i][j] = 0;
1028 moved = true;
1029 }
1030 }
1031 }
1032 } while (moved);
1033 }
1034
1036 matrix.block.color = matrix.nextblock.color;
1037 matrix.nextblock.color.randcolor();
1038 matrix.block.x = GRID_WIDTH / 2;
1039 matrix.block.y = 1;
1040 matrix.block.horizontal = false;
1041 }
1042
1044
1045 for (int i = 0; i < GRID_WIDTH; i++) {
1046 if (matrix.grid[i][1] > 0) {
1047 return true;
1048 }
1049 }
1050
1051 for (int i = 0; i < GRID_WIDTH; i++) {
1052 for (int j = 0; j < GRID_HEIGHT; j++) {
1053 if (matrix.grid[i][j] == 0) {
1054 return false;
1055 }
1056 }
1057 }
1058
1059 return true;
1060 }
1061
1063
1064 int baseSpeeds[] = {30, 20, 10};
1065 matrix.Game.speed = baseSpeeds[difficultySetting];
1066 }
1067
1069 if (canMoveLeft()) {
1070 matrix.block.x--;
1071 }
1072 }
1073
1075 if (canMoveRight()) {
1076 matrix.block.x++;
1077 }
1078 }
1079
1081 if (canMoveDown()) {
1082 matrix.block.y++;
1083 }
1084 }
1085
1086 void dropBlock() {
1087 while (canMoveDown()) {
1088 matrix.block.y++;
1089 }
1090 }
1091
1092 bool canRotate() {
1093 int bx = matrix.block.x;
1094 int by = matrix.block.y;
1095
1096 if (matrix.block.horizontal) {
1097
1098 if (by + 2 >= GRID_HEIGHT)
1099 return false;
1100
1101 if (by + 1 < GRID_HEIGHT && matrix.grid[bx][by + 1] > 0)
1102 return false;
1103 if (by + 2 < GRID_HEIGHT && matrix.grid[bx][by + 2] > 0)
1104 return false;
1105 } else {
1106
1107 if (bx + 2 >= GRID_WIDTH)
1108 return false;
1109
1110 if (bx + 1 < GRID_WIDTH && by >= 0 && by < GRID_HEIGHT && matrix.grid[bx + 1][by] > 0)
1111 return false;
1112 if (bx + 2 < GRID_WIDTH && by >= 0 && by < GRID_HEIGHT && matrix.grid[bx + 2][by] > 0)
1113 return false;
1114 }
1115 return true;
1116 }
1117
1119 if (canRotate()) {
1120 matrix.block.horizontal = !matrix.block.horizontal;
1121 }
1122 }
1123
1124 void event(SDL_Event &e) override {
1125 if (e.type == SDL_EVENT_QUIT || (currentScreen == SCREEN_START && e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE)) {
1126 exit();
1127 return;
1128 }
1129
1130 if (e.type == SDL_EVENT_KEY_DOWN) {
1131 gameKeypress(e.key.key);
1132 } else if (e.type == SDL_EVENT_TEXT_INPUT) {
1133 handleTextInput(e.text.text);
1134 }
1135
1136 if (e.type == SDL_EVENT_GAMEPAD_ADDED) {
1137 openGamepad(e.gdevice.which);
1138 } else if (e.type == SDL_EVENT_GAMEPAD_REMOVED) {
1139 if (gamepad != nullptr && e.gdevice.which == gamepadId) {
1140 SDL_CloseGamepad(gamepad);
1141 gamepad = nullptr;
1142 gamepadId = 0;
1143 }
1144 } else if (e.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
1145 handleControllerButton(e.gbutton.button);
1146 }
1147 }
1148
1149 void handleControllerButton(Uint8 button) {
1150 switch (currentScreen) {
1151 case SCREEN_INTRO:
1152 if (button == SDL_GAMEPAD_BUTTON_SOUTH || button == SDL_GAMEPAD_BUTTON_START) {
1153 currentScreen = SCREEN_START;
1154 }
1155 break;
1156 case SCREEN_START:
1157 if (button == SDL_GAMEPAD_BUTTON_DPAD_UP) {
1158 cursorPos = (cursorPos - 1 + 5) % 5;
1159 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_DOWN) {
1160 cursorPos = (cursorPos + 1) % 5;
1161 } else if (button == SDL_GAMEPAD_BUTTON_SOUTH || button == SDL_GAMEPAD_BUTTON_START) {
1162 if (cursorPos == 0) {
1163 matrix.init_matrix();
1164 shaderLevel = 0;
1166 matrix.block.x = GRID_WIDTH / 2;
1167 matrix.block.y = 1;
1168 matrix.block.horizontal = false;
1169 currentScreen = SCREEN_GAME;
1170 } else if (cursorPos == 1) {
1171 finalScore = 0;
1172 enteringName = false;
1173 currentScreen = SCREEN_SCORES;
1174 } else if (cursorPos == 2) {
1175 currentScreen = SCREEN_OPTIONS;
1176 } else if (cursorPos == 3) {
1177 currentScreen = SCREEN_CREDITS;
1178 } else if (cursorPos == 4) {
1179 exit();
1180 }
1181 } else if (button == SDL_GAMEPAD_BUTTON_EAST) {
1182 exit();
1183 }
1184 break;
1185 case SCREEN_GAME:
1186 if (button == SDL_GAMEPAD_BUTTON_DPAD_LEFT) {
1187 moveBlockLeft();
1188 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT) {
1190 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_DOWN) {
1191 moveBlockDown();
1192 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_UP) {
1193 matrix.block.color.shiftcolor(true);
1194 } else if (button == SDL_GAMEPAD_BUTTON_SOUTH) {
1195 rotateBlock();
1196 } else if (button == SDL_GAMEPAD_BUTTON_EAST) {
1197 matrix.block.color.shiftcolor(false);
1198 } else if (button == SDL_GAMEPAD_BUTTON_NORTH) {
1199 dropBlock();
1200 } else if (button == SDL_GAMEPAD_BUTTON_START) {
1201 paused = !paused;
1202 } else if (button == SDL_GAMEPAD_BUTTON_BACK) {
1203 currentScreen = SCREEN_START;
1204 }
1205 break;
1206 case SCREEN_GAMEOVER:
1207 if (button == SDL_GAMEPAD_BUTTON_SOUTH || button == SDL_GAMEPAD_BUTTON_START) {
1209 } else if (button == SDL_GAMEPAD_BUTTON_EAST) {
1210 currentScreen = SCREEN_START;
1211 }
1212 break;
1213 case SCREEN_OPTIONS:
1214 if (button == SDL_GAMEPAD_BUTTON_DPAD_UP) {
1215 optionsCursor = (optionsCursor - 1 + 3) % 3;
1216 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_DOWN) {
1217 optionsCursor = (optionsCursor + 1) % 3;
1218 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_LEFT) {
1219 if (optionsCursor == 0)
1220 difficultySetting = (difficultySetting - 1 + 3) % 3;
1221 if (optionsCursor == 1) {
1222 shaderEffectsEnabled = !shaderEffectsEnabled;
1224 }
1225 } else if (button == SDL_GAMEPAD_BUTTON_DPAD_RIGHT) {
1226 if (optionsCursor == 0)
1227 difficultySetting = (difficultySetting + 1) % 3;
1228 if (optionsCursor == 1) {
1229 shaderEffectsEnabled = !shaderEffectsEnabled;
1231 }
1232 } else if (button == SDL_GAMEPAD_BUTTON_SOUTH || button == SDL_GAMEPAD_BUTTON_START) {
1233 if (optionsCursor == 2)
1234 currentScreen = SCREEN_START;
1235 } else if (button == SDL_GAMEPAD_BUTTON_EAST) {
1236 currentScreen = SCREEN_START;
1237 }
1238 break;
1239 case SCREEN_CREDITS:
1240 if (button == SDL_GAMEPAD_BUTTON_SOUTH || button == SDL_GAMEPAD_BUTTON_EAST || button == SDL_GAMEPAD_BUTTON_START) {
1241 currentScreen = SCREEN_START;
1242 }
1243 break;
1244 case SCREEN_SCORES:
1245 if (!enteringName) {
1246 if (button == SDL_GAMEPAD_BUTTON_SOUTH || button == SDL_GAMEPAD_BUTTON_EAST || button == SDL_GAMEPAD_BUTTON_START) {
1247 currentScreen = SCREEN_START;
1248 }
1249 } else if (enteringName) {
1250 if (button == SDL_GAMEPAD_BUTTON_BACK)
1251 currentScreen = SCREEN_START;
1252 }
1253 break;
1254 }
1255 }
1256
1258 if (gamepad == nullptr)
1259 return;
1260 Uint32 now = SDL_GetTicks();
1261
1262 if (currentScreen == SCREEN_GAME && !paused) {
1263 Sint16 lx = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
1264 Sint16 ly = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
1265
1266 if (lx < -JOYSTICK_DEAD_ZONE) {
1267 if (now - joyRepeatLeft > JOY_REPEAT_DELAY) {
1268 moveBlockLeft();
1269 joyRepeatLeft = now;
1270 }
1271 } else {
1272 joyRepeatLeft = 0;
1273 }
1274 if (lx > JOYSTICK_DEAD_ZONE) {
1275 if (now - joyRepeatRight > JOY_REPEAT_DELAY) {
1277 joyRepeatRight = now;
1278 }
1279 } else {
1280 joyRepeatRight = 0;
1281 }
1282 if (ly > JOYSTICK_DEAD_ZONE) {
1283 if (now - joyRepeatDown > JOY_REPEAT_DELAY) {
1284 moveBlockDown();
1285 joyRepeatDown = now;
1286 }
1287 } else {
1288 joyRepeatDown = 0;
1289 }
1290 if (ly < -JOYSTICK_DEAD_ZONE) {
1291 if (now - joyRepeatUp > JOY_REPEAT_DELAY) {
1292 matrix.block.color.shiftcolor(true);
1293 joyRepeatUp = now;
1294 }
1295 } else {
1296 joyRepeatUp = 0;
1297 }
1298 }
1299 }
1300
1301 void gameKeypress(SDL_Keycode key) {
1302 switch (currentScreen) {
1303 case SCREEN_INTRO:
1304 if (key == SDLK_RETURN) {
1305 currentScreen = SCREEN_START;
1306 }
1307 break;
1308 case SCREEN_START:
1309 if (key == SDLK_UP) {
1310 cursorPos = (cursorPos - 1 + 5) % 5;
1311 } else if (key == SDLK_DOWN) {
1312 cursorPos = (cursorPos + 1) % 5;
1313 } else if (key == SDLK_RETURN) {
1314 if (cursorPos == 0) {
1315 matrix.init_matrix();
1316 shaderLevel = 0;
1318 matrix.block.x = GRID_WIDTH / 2;
1319 matrix.block.y = 1;
1320 matrix.block.horizontal = false;
1321 currentScreen = SCREEN_GAME;
1322 } else if (cursorPos == 1) {
1323 finalScore = 0;
1324 enteringName = false;
1325 currentScreen = SCREEN_SCORES;
1326 } else if (cursorPos == 2) {
1327 currentScreen = SCREEN_OPTIONS;
1328 } else if (cursorPos == 3) {
1329 currentScreen = SCREEN_CREDITS;
1330 } else if (cursorPos == 4) {
1331 exit();
1332 }
1333 }
1334 break;
1335 case SCREEN_GAME:
1336 if (key == SDLK_LEFT) {
1337 moveBlockLeft();
1338 } else if (key == SDLK_RIGHT) {
1340 } else if (key == SDLK_DOWN) {
1341 moveBlockDown();
1342 } else if (key == SDLK_UP) {
1343 matrix.block.color.shiftcolor(true);
1344 } else if (key == SDLK_SPACE) {
1345 rotateBlock();
1346 } else if (key == SDLK_Z) {
1347 matrix.block.color.shiftcolor(false);
1348 } else if (key == SDLK_P) {
1349 paused = !paused;
1350 } else if (key == SDLK_L) {
1351 /*if (shaderLevel < SHADER_LEVEL_COUNT - 1) {
1352 shaderLevel++;
1353 std::cout << "level increased to: " << shaderLevel << std::endl;
1354 }*/
1355 } else if (key == SDLK_ESCAPE) {
1356 currentScreen = SCREEN_START;
1357 }
1358 break;
1359 case SCREEN_GAMEOVER:
1360 if (key == SDLK_RETURN) {
1362 } else if (key == SDLK_ESCAPE) {
1363 currentScreen = SCREEN_START;
1364 }
1365 break;
1366 case SCREEN_OPTIONS:
1367 if (key == SDLK_UP) {
1368 optionsCursor = (optionsCursor - 1 + 3) % 3;
1369 } else if (key == SDLK_DOWN) {
1370 optionsCursor = (optionsCursor + 1) % 3;
1371 } else if (key == SDLK_LEFT) {
1372 if (optionsCursor == 0) {
1373 difficultySetting = (difficultySetting - 1 + 3) % 3;
1374 } else if (optionsCursor == 1) {
1375 shaderEffectsEnabled = !shaderEffectsEnabled;
1377 }
1378 } else if (key == SDLK_RIGHT) {
1379 if (optionsCursor == 0) {
1380 difficultySetting = (difficultySetting + 1) % 3;
1381 } else if (optionsCursor == 1) {
1382 shaderEffectsEnabled = !shaderEffectsEnabled;
1384 }
1385 } else if (key == SDLK_RETURN) {
1386 if (optionsCursor == 2) {
1387 currentScreen = SCREEN_START;
1388 }
1389 }
1390 break;
1391 case SCREEN_CREDITS:
1392 if (key == SDLK_RETURN) {
1393 currentScreen = SCREEN_START;
1394 }
1395 break;
1396 case SCREEN_SCORES:
1397 if (enteringName) {
1398 if (key == SDLK_RETURN && !playerName.empty()) {
1399
1400 scores.addScore(playerName, finalScore);
1401 enteringName = false;
1402 } else if (key == SDLK_BACKSPACE && !playerName.empty()) {
1403 playerName.pop_back();
1404 } else if (key == SDLK_ESCAPE) {
1405 enteringName = false;
1406 }
1407 } else {
1408 if (key == SDLK_RETURN || key == SDLK_ESCAPE) {
1409 currentScreen = SCREEN_START;
1410 }
1411 }
1412 break;
1413 }
1414 }
1415
1416 void handleTextInput(const char *text) {
1417 if (currentScreen == SCREEN_SCORES && enteringName) {
1418
1419 if (playerName.length() < 15) {
1420 playerName += text;
1421 }
1422 }
1423 }
1424};
1425
1426int main(int argc, char **argv) {
1427 Arguments args = proc_args(argc, argv);
1428 try {
1429 MasterPieceWindow window(args.path, args.width, args.height, args.fullscreen, args.enable_vsync);
1430 window.loop();
1431 } catch (mxvk::Exception &e) {
1432 SDL_Log("mxvk: Exception: %s\n", e.text().c_str());
1433 return EXIT_FAILURE;
1434 } catch (ArgException<std::string> &e) {
1435 std::cerr << std::format("mxvk: Argument Exception: {}\n", e.text());
1436 return EXIT_FAILURE;
1437 }
1438 return EXIT_SUCCESS;
1439}
constexpr int NEXT_PANEL_Y
Definition acid.drop.cpp:31
constexpr int FLASH_DURATION
Definition acid.drop.cpp:32
constexpr int BLOCK_HEIGHT
Definition acid.drop.cpp:28
constexpr int SHADER_LEVEL_LINES
Definition acid.drop.cpp:33
constexpr int BLOCK_SPACING
Definition acid.drop.cpp:29
ScreenState
Definition acid.drop.cpp:51
@ SCREEN_START
Definition acid.drop.cpp:53
@ SCREEN_SCORES
Definition acid.drop.cpp:58
@ SCREEN_GAME
Definition acid.drop.cpp:54
@ SCREEN_OPTIONS
Definition acid.drop.cpp:56
@ SCREEN_INTRO
Definition acid.drop.cpp:52
@ SCREEN_GAMEOVER
Definition acid.drop.cpp:55
@ SCREEN_CREDITS
Definition acid.drop.cpp:57
constexpr int GRID_HEIGHT
Definition acid.drop.cpp:26
constexpr int SHADER_LEVEL_COUNT
Definition acid.drop.cpp:34
constexpr int STARTY
Definition acid.drop.cpp:24
#define puzzle_ASSET_DIR
Definition acid.drop.cpp:20
@ BLOCK_COUNT
Definition acid.drop.cpp:48
@ BLOCK_YELLOW
Definition acid.drop.cpp:38
@ BLOCK_PURPLE
Definition acid.drop.cpp:42
@ BLOCK_GRAY
Definition acid.drop.cpp:44
@ BLOCK_GREEN
Definition acid.drop.cpp:46
@ BLOCK_PINK
Definition acid.drop.cpp:43
@ BLOCK_DBLUE
Definition acid.drop.cpp:41
@ BLOCK_RED
Definition acid.drop.cpp:45
@ BLOCK_ORANGE
Definition acid.drop.cpp:39
@ BLOCK_BLACK
Definition acid.drop.cpp:37
@ BLOCK_CLEAR
Definition acid.drop.cpp:47
@ BLOCK_LTBLUE
Definition acid.drop.cpp:40
constexpr int GRID_WIDTH
Definition acid.drop.cpp:25
constexpr int BLOCK_SIZE
Definition acid.drop.cpp:27
constexpr int NEXT_PANEL_X
Definition acid.drop.cpp:30
constexpr int STARTX
Definition acid.drop.cpp:23
Lightweight, header-only, template command-line argument parser.
Arguments proc_args(int &argc, char **argv)
Parse standard libmx2 command-line options from main()'s argv.
Definition argz.hpp:872
Exception thrown by Argz::proc() on unrecognised or malformed options.
Definition argz.hpp:178
void sort()
bool qualifiesForHighScore(int score)
void write()
void addScore(const std::string &name, int score)
void read()
void init()
const std::vector< Score > & getScores() const
void handleControllerButton(Uint8 button)
void clearFlashedBlocks()
void proc() override
Execute one processing/update step.
void drawblock(float scaleX, float scaleY)
void tryOpenFirstGamepad()
void gameKeypress(SDL_Keycode key)
bool openGamepad(SDL_JoystickID id)
void handleTextInput(const char *text)
void event(SDL_Event &e) override
Handle one SDL event.
void drawnext(float scaleX, float scaleY)
int centerX(const char *text)
int scaleY(int baseY)
virtual ~MasterPieceWindow()
void drawmatrix(float scaleX, float scaleY)
void applyShaderEffectsToggle()
MasterPieceWindow(const std::string &path, int wx, int wy, bool full, bool enable_vsync)
std::string text() const
Main Vulkan window wrapper for MXVK.
Definition mxvk.hpp:37
void loop()
Run the main event/render loop.
Definition mxvk.cpp:600
VK_Sprite * createSprite(const std::string &pngPath, const std::string &vertexShaderPath="", const std::string &fragmentShaderPath="")
Create a sprite from a PNG file and register it with this window.
Definition mxvk.cpp:3477
bool getTextDimensions(const std::string &text, int &width, int &height)
Measure text dimensions in pixels.
Definition mxvk.cpp:3069
void clearTextQueue()
Clear all queued text draw calls for the current frame.
Definition mxvk.cpp:3063
std::unique_ptr< SDL_Window, SDLWindowDeleter > window
Definition mxvk.hpp:480
void exit()
Request loop termination.
Definition mxvk.cpp:1126
VK_Window()=default
Construct an empty window object.
void setFont(const std::string &fontPath, int fontSize=24)
Set the active text-render font.
Definition mxvk.cpp:2991
void printText(const std::string &text, int x, int y, const SDL_Color &col)
Queue a text string for rendering during the current frame.
Definition mxvk.cpp:3018
int main(void)
Definition main.cpp:7
#define MXVK_VALIDATION
Definition mxvk.hpp:27
Utilities for loading and saving PNG images.
Definition mxvk.hpp:30
Plain data structure returned by proc_args() with all common libmx2 CLI options.
Definition argz.hpp:730
bool fullscreen
Whether fullscreen mode was requested.
Definition argz.hpp:736
bool enable_vsync
Enable FIFO present mode / v-sync (--enable-vsync).
Definition argz.hpp:750
int height
Viewport height in pixels (default: 720).
Definition argz.hpp:733
std::string path
Asset root; proc_args() defaults it to the executable directory.
Definition argz.hpp:735
int width
Viewport width in pixels (default: 1280).
Definition argz.hpp:732
void randcolor()
Definition acid.drop.cpp:87
void shiftcolor(bool dir)
Definition acid.drop.cpp:94
bool horizontal
BlockColor nextcolor
BlockColor color
void newgame()
Definition acid.drop.cpp:68
int lineamt
Definition acid.drop.cpp:65
void addline()
Definition acid.drop.cpp:74
unsigned long score
Definition acid.drop.cpp:62
std::string name
int score
auto operator<=>(const Score &s) const
Score(std::string name, int score)
GameBlock nextblock
GameData Game
GameBlock block
void init_matrix()
int grid[GRID_WIDTH][GRID_HEIGHT]