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