From 32ecb78fa6a700273b84d7fd618a61d3c111ad38 Mon Sep 17 00:00:00 2001 From: John Kisor Date: Sun, 18 Feb 2024 22:05:18 -0500 Subject: [PATCH 1/6] Build tetris example --- examples/tetris.c | 793 ++++++++++++++++++++++++++++++++++++++++++++++ nob.c | 5 + wasm/tetris.wasm | Bin 0 -> 30292 bytes 3 files changed, 798 insertions(+) create mode 100644 examples/tetris.c create mode 100755 wasm/tetris.wasm diff --git a/examples/tetris.c b/examples/tetris.c new file mode 100644 index 0000000..f3f975b --- /dev/null +++ b/examples/tetris.c @@ -0,0 +1,793 @@ +/******************************************************************************************* +* +* raylib - classic game: tetris +* +* Sample game developed by Marc Palau and Ramon Santamaria +* +* This game has been created using raylib v1.3 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2015 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#if defined(PLATFORM_WEB) + void raylib_js_set_entry(void (*entry)(void)); +#endif + +//---------------------------------------------------------------------------------- +// Some Defines +//---------------------------------------------------------------------------------- +#define SQUARE_SIZE 20 + +#define GRID_HORIZONTAL_SIZE 12 +#define GRID_VERTICAL_SIZE 20 + +#define LATERAL_SPEED 10 +#define TURNING_SPEED 12 +#define FAST_FALL_AWAIT_COUNTER 30 + +#define FADING_TIME 33 + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef enum GridSquare { EMPTY, MOVING, FULL, BLOCK, FADING } GridSquare; + +//------------------------------------------------------------------------------------ +// Global Variables Declaration +//------------------------------------------------------------------------------------ +static const int screenWidth = 800; +static const int screenHeight = 450; + +static bool gameOver = false; +static bool pause = false; + +// Matrices +static GridSquare grid [GRID_HORIZONTAL_SIZE][GRID_VERTICAL_SIZE]; +static GridSquare piece [4][4]; +static GridSquare incomingPiece [4][4]; + +// Theese variables keep track of the active piece position +static int piecePositionX = 0; +static int piecePositionY = 0; + +// Game parameters +static Color fadingColor; +//static int fallingSpeed; // In frames + +static bool beginPlay = true; // This var is only true at the begining of the game, used for the first matrix creations +static bool pieceActive = false; +static bool detection = false; +static bool lineToDelete = false; + +// Statistics +static int level = 1; +static int lines = 0; + +// Counters +static int gravityMovementCounter = 0; +static int lateralMovementCounter = 0; +static int turnMovementCounter = 0; +static int fastFallMovementCounter = 0; + +static int fadeLineCounter = 0; + +// Based on level +static int gravitySpeed = 30; + +//------------------------------------------------------------------------------------ +// Module Functions Declaration (local) +//------------------------------------------------------------------------------------ +static void InitGame(void); // Initialize game +static void UpdateGame(void); // Update game (one frame) +static void DrawGame(void); // Draw game (one frame) +static void UnloadGame(void); // Unload game +static void UpdateDrawFrame(void); // Update and Draw (one frame) + +// Additional module functions +static bool Createpiece(); +static void GetRandompiece(); +static void ResolveFallingMovement(bool *detection, bool *pieceActive); +static bool ResolveLateralMovement(); +static bool ResolveTurnMovement(); +static void CheckDetection(bool *detection); +static void CheckCompletion(bool *lineToDelete); +static int DeleteCompleteLines(); + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization (Note windowTitle is unused on Android) + //--------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "classic game: tetris"); + + InitGame(); + +#if defined(PLATFORM_WEB) + raylib_js_set_entry(UpdateDrawFrame); +#else + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update and Draw + //---------------------------------------------------------------------------------- + UpdateDrawFrame(); + //---------------------------------------------------------------------------------- + } +#endif + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadGame(); // Unload loaded data (textures, sounds, models...) + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + +//-------------------------------------------------------------------------------------- +// Game Module Functions Definition +//-------------------------------------------------------------------------------------- + +// Initialize game variables +void InitGame(void) +{ + // Initialize game statistics + level = 1; + lines = 0; + + fadingColor = GRAY; + + piecePositionX = 0; + piecePositionY = 0; + + pause = false; + + beginPlay = true; + pieceActive = false; + detection = false; + lineToDelete = false; + + // Counters + gravityMovementCounter = 0; + lateralMovementCounter = 0; + turnMovementCounter = 0; + fastFallMovementCounter = 0; + + fadeLineCounter = 0; + gravitySpeed = 30; + + // Initialize grid matrices + for (int i = 0; i < GRID_HORIZONTAL_SIZE; i++) + { + for (int j = 0; j < GRID_VERTICAL_SIZE; j++) + { + if ((j == GRID_VERTICAL_SIZE - 1) || (i == 0) || (i == GRID_HORIZONTAL_SIZE - 1)) grid[i][j] = BLOCK; + else grid[i][j] = EMPTY; + } + } + + // Initialize incoming piece matrices + for (int i = 0; i < 4; i++) + { + for (int j = 0; j< 4; j++) + { + incomingPiece[i][j] = EMPTY; + } + } +} + +// Update game (one frame) +void UpdateGame(void) +{ + if (!gameOver) + { + if (IsKeyPressed('P')) pause = !pause; + + if (!pause) + { + if (!lineToDelete) + { + if (!pieceActive) + { + // Get another piece + pieceActive = Createpiece(); + + // We leave a little time before starting the fast falling down + fastFallMovementCounter = 0; + } + else // Piece falling + { + // Counters update + fastFallMovementCounter++; + gravityMovementCounter++; + lateralMovementCounter++; + turnMovementCounter++; + + // We make sure to move if we've pressed the key this frame + if (IsKeyPressed(KEY_LEFT) || IsKeyPressed(KEY_RIGHT)) lateralMovementCounter = LATERAL_SPEED; + if (IsKeyPressed(KEY_UP)) turnMovementCounter = TURNING_SPEED; + + // Fall down + if (IsKeyDown(KEY_DOWN) && (fastFallMovementCounter >= FAST_FALL_AWAIT_COUNTER)) + { + // We make sure the piece is going to fall this frame + gravityMovementCounter += gravitySpeed; + } + + if (gravityMovementCounter >= gravitySpeed) + { + // Basic falling movement + CheckDetection(&detection); + + // Check if the piece has collided with another piece or with the boundings + ResolveFallingMovement(&detection, &pieceActive); + + // Check if we fullfilled a line and if so, erase the line and pull down the the lines above + CheckCompletion(&lineToDelete); + + gravityMovementCounter = 0; + } + + // Move laterally at player's will + if (lateralMovementCounter >= LATERAL_SPEED) + { + // Update the lateral movement and if success, reset the lateral counter + if (!ResolveLateralMovement()) lateralMovementCounter = 0; + } + + // Turn the piece at player's will + if (turnMovementCounter >= TURNING_SPEED) + { + // Update the turning movement and reset the turning counter + if (ResolveTurnMovement()) turnMovementCounter = 0; + } + } + + // Game over logic + for (int j = 0; j < 2; j++) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == FULL) + { + gameOver = true; + } + } + } + } + else + { + // Animation when deleting lines + fadeLineCounter++; + + if (fadeLineCounter%8 < 4) fadingColor = MAROON; + else fadingColor = GRAY; + + if (fadeLineCounter >= FADING_TIME) + { + int deletedLines = 0; + deletedLines = DeleteCompleteLines(); + fadeLineCounter = 0; + lineToDelete = false; + + lines += deletedLines; + } + } + } + } + else + { + if (IsKeyPressed(KEY_ENTER)) + { + InitGame(); + gameOver = false; + } + } +} + +// Draw game (one frame) +void DrawGame(void) +{ + BeginDrawing(); + + ClearBackground(RAYWHITE); + + if (!gameOver) + { + // Draw gameplay area + Vector2 offset; + offset.x = screenWidth/2 - (GRID_HORIZONTAL_SIZE*SQUARE_SIZE/2) - 50; + offset.y = screenHeight/2 - ((GRID_VERTICAL_SIZE - 1)*SQUARE_SIZE/2) + SQUARE_SIZE*2; + + offset.y -= 50; // NOTE: Harcoded position! + + int controller = offset.x; + + for (int j = 0; j < GRID_VERTICAL_SIZE; j++) + { + for (int i = 0; i < GRID_HORIZONTAL_SIZE; i++) + { + // Draw each square of the grid + if (grid[i][j] == EMPTY) + { + DrawLine(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, LIGHTGRAY ); + DrawLine(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, LIGHTGRAY ); + DrawLine(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, LIGHTGRAY ); + DrawLine(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, LIGHTGRAY ); + offset.x += SQUARE_SIZE; + } + else if (grid[i][j] == FULL) + { + DrawRectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, GRAY); + offset.x += SQUARE_SIZE; + } + else if (grid[i][j] == MOVING) + { + DrawRectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, DARKGRAY); + offset.x += SQUARE_SIZE; + } + else if (grid[i][j] == BLOCK) + { + DrawRectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, LIGHTGRAY); + offset.x += SQUARE_SIZE; + } + else if (grid[i][j] == FADING) + { + DrawRectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, fadingColor); + offset.x += SQUARE_SIZE; + } + } + + offset.x = controller; + offset.y += SQUARE_SIZE; + } + + // Draw incoming piece (hardcoded) + offset.x = 500; + offset.y = 45; + + int controler = offset.x; + + for (int j = 0; j < 4; j++) + { + for (int i = 0; i < 4; i++) + { + if (incomingPiece[i][j] == EMPTY) + { + DrawLine(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, LIGHTGRAY ); + DrawLine(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, LIGHTGRAY ); + DrawLine(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, LIGHTGRAY ); + DrawLine(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, LIGHTGRAY ); + offset.x += SQUARE_SIZE; + } + else if (incomingPiece[i][j] == MOVING) + { + DrawRectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, GRAY); + offset.x += SQUARE_SIZE; + } + } + + offset.x = controler; + offset.y += SQUARE_SIZE; + } + + DrawText("INCOMING:", offset.x, offset.y - 100, 10, GRAY); + DrawText(TextFormat("LINES: %04i", lines), offset.x, offset.y + 20, 10, GRAY); + + if (pause) DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/2 - 40, 40, GRAY); + } + else DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, GRAY); + + EndDrawing(); +} + +// Unload game variables +void UnloadGame(void) +{ + // TODO: Unload all dynamic loaded data (textures, sounds, models...) +} + +// Update and Draw (one frame) +void UpdateDrawFrame(void) +{ + UpdateGame(); + DrawGame(); +} + +//-------------------------------------------------------------------------------------- +// Additional module functions +//-------------------------------------------------------------------------------------- +static bool Createpiece() +{ + piecePositionX = (int)((GRID_HORIZONTAL_SIZE - 4)/2); + piecePositionY = 0; + + // If the game is starting and you are going to create the first piece, we create an extra one + if (beginPlay) + { + GetRandompiece(); + beginPlay = false; + } + + // We assign the incoming piece to the actual piece + for (int i = 0; i < 4; i++) + { + for (int j = 0; j< 4; j++) + { + piece[i][j] = incomingPiece[i][j]; + } + } + + // We assign a random piece to the incoming one + GetRandompiece(); + + // Assign the piece to the grid + for (int i = piecePositionX; i < piecePositionX + 4; i++) + { + for (int j = 0; j < 4; j++) + { + if (piece[i - (int)piecePositionX][j] == MOVING) grid[i][j] = MOVING; + } + } + + return true; +} + +static void GetRandompiece() +{ + int random = GetRandomValue(0, 6); + + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + incomingPiece[i][j] = EMPTY; + } + } + + switch (random) + { + case 0: { incomingPiece[1][1] = MOVING; incomingPiece[2][1] = MOVING; incomingPiece[1][2] = MOVING; incomingPiece[2][2] = MOVING; } break; //Cube + case 1: { incomingPiece[1][0] = MOVING; incomingPiece[1][1] = MOVING; incomingPiece[1][2] = MOVING; incomingPiece[2][2] = MOVING; } break; //L + case 2: { incomingPiece[1][2] = MOVING; incomingPiece[2][0] = MOVING; incomingPiece[2][1] = MOVING; incomingPiece[2][2] = MOVING; } break; //L inversa + case 3: { incomingPiece[0][1] = MOVING; incomingPiece[1][1] = MOVING; incomingPiece[2][1] = MOVING; incomingPiece[3][1] = MOVING; } break; //Recta + case 4: { incomingPiece[1][0] = MOVING; incomingPiece[1][1] = MOVING; incomingPiece[1][2] = MOVING; incomingPiece[2][1] = MOVING; } break; //Creu tallada + case 5: { incomingPiece[1][1] = MOVING; incomingPiece[2][1] = MOVING; incomingPiece[2][2] = MOVING; incomingPiece[3][2] = MOVING; } break; //S + case 6: { incomingPiece[1][2] = MOVING; incomingPiece[2][2] = MOVING; incomingPiece[2][1] = MOVING; incomingPiece[3][1] = MOVING; } break; //S inversa + } +} + +static void ResolveFallingMovement(bool *detection, bool *pieceActive) +{ + // If we finished moving this piece, we stop it + if (*detection) + { + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == MOVING) + { + grid[i][j] = FULL; + *detection = false; + *pieceActive = false; + } + } + } + } + else // We move down the piece + { + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == MOVING) + { + grid[i][j+1] = MOVING; + grid[i][j] = EMPTY; + } + } + } + + piecePositionY++; + } +} + +static bool ResolveLateralMovement() +{ + bool collision = false; + + // Piece movement + if (IsKeyDown(KEY_LEFT)) // Move left + { + // Check if is possible to move to left + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == MOVING) + { + // Check if we are touching the left wall or we have a full square at the left + if ((i-1 == 0) || (grid[i-1][j] == FULL)) collision = true; + } + } + } + + // If able, move left + if (!collision) + { + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) // We check the matrix from left to right + { + // Move everything to the left + if (grid[i][j] == MOVING) + { + grid[i-1][j] = MOVING; + grid[i][j] = EMPTY; + } + } + } + + piecePositionX--; + } + } + else if (IsKeyDown(KEY_RIGHT)) // Move right + { + // Check if is possible to move to right + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == MOVING) + { + // Check if we are touching the right wall or we have a full square at the right + if ((i+1 == GRID_HORIZONTAL_SIZE - 1) || (grid[i+1][j] == FULL)) + { + collision = true; + + } + } + } + } + + // If able move right + if (!collision) + { + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = GRID_HORIZONTAL_SIZE - 1; i >= 1; i--) // We check the matrix from right to left + { + // Move everything to the right + if (grid[i][j] == MOVING) + { + grid[i+1][j] = MOVING; + grid[i][j] = EMPTY; + } + } + } + + piecePositionX++; + } + } + + return collision; +} + +static bool ResolveTurnMovement() +{ + // Input for turning the piece + if (IsKeyDown(KEY_UP)) + { + GridSquare aux; + bool checker = false; + + // Check all turning possibilities + if ((grid[piecePositionX + 3][piecePositionY] == MOVING) && + (grid[piecePositionX][piecePositionY] != EMPTY) && + (grid[piecePositionX][piecePositionY] != MOVING)) checker = true; + + if ((grid[piecePositionX + 3][piecePositionY + 3] == MOVING) && + (grid[piecePositionX + 3][piecePositionY] != EMPTY) && + (grid[piecePositionX + 3][piecePositionY] != MOVING)) checker = true; + + if ((grid[piecePositionX][piecePositionY + 3] == MOVING) && + (grid[piecePositionX + 3][piecePositionY + 3] != EMPTY) && + (grid[piecePositionX + 3][piecePositionY + 3] != MOVING)) checker = true; + + if ((grid[piecePositionX][piecePositionY] == MOVING) && + (grid[piecePositionX][piecePositionY + 3] != EMPTY) && + (grid[piecePositionX][piecePositionY + 3] != MOVING)) checker = true; + + if ((grid[piecePositionX + 1][piecePositionY] == MOVING) && + (grid[piecePositionX][piecePositionY + 2] != EMPTY) && + (grid[piecePositionX][piecePositionY + 2] != MOVING)) checker = true; + + if ((grid[piecePositionX + 3][piecePositionY + 1] == MOVING) && + (grid[piecePositionX + 1][piecePositionY] != EMPTY) && + (grid[piecePositionX + 1][piecePositionY] != MOVING)) checker = true; + + if ((grid[piecePositionX + 2][piecePositionY + 3] == MOVING) && + (grid[piecePositionX + 3][piecePositionY + 1] != EMPTY) && + (grid[piecePositionX + 3][piecePositionY + 1] != MOVING)) checker = true; + + if ((grid[piecePositionX][piecePositionY + 2] == MOVING) && + (grid[piecePositionX + 2][piecePositionY + 3] != EMPTY) && + (grid[piecePositionX + 2][piecePositionY + 3] != MOVING)) checker = true; + + if ((grid[piecePositionX + 2][piecePositionY] == MOVING) && + (grid[piecePositionX][piecePositionY + 1] != EMPTY) && + (grid[piecePositionX][piecePositionY + 1] != MOVING)) checker = true; + + if ((grid[piecePositionX + 3][piecePositionY + 2] == MOVING) && + (grid[piecePositionX + 2][piecePositionY] != EMPTY) && + (grid[piecePositionX + 2][piecePositionY] != MOVING)) checker = true; + + if ((grid[piecePositionX + 1][piecePositionY + 3] == MOVING) && + (grid[piecePositionX + 3][piecePositionY + 2] != EMPTY) && + (grid[piecePositionX + 3][piecePositionY + 2] != MOVING)) checker = true; + + if ((grid[piecePositionX][piecePositionY + 1] == MOVING) && + (grid[piecePositionX + 1][piecePositionY + 3] != EMPTY) && + (grid[piecePositionX + 1][piecePositionY + 3] != MOVING)) checker = true; + + if ((grid[piecePositionX + 1][piecePositionY + 1] == MOVING) && + (grid[piecePositionX + 1][piecePositionY + 2] != EMPTY) && + (grid[piecePositionX + 1][piecePositionY + 2] != MOVING)) checker = true; + + if ((grid[piecePositionX + 2][piecePositionY + 1] == MOVING) && + (grid[piecePositionX + 1][piecePositionY + 1] != EMPTY) && + (grid[piecePositionX + 1][piecePositionY + 1] != MOVING)) checker = true; + + if ((grid[piecePositionX + 2][piecePositionY + 2] == MOVING) && + (grid[piecePositionX + 2][piecePositionY + 1] != EMPTY) && + (grid[piecePositionX + 2][piecePositionY + 1] != MOVING)) checker = true; + + if ((grid[piecePositionX + 1][piecePositionY + 2] == MOVING) && + (grid[piecePositionX + 2][piecePositionY + 2] != EMPTY) && + (grid[piecePositionX + 2][piecePositionY + 2] != MOVING)) checker = true; + + if (!checker) + { + aux = piece[0][0]; + piece[0][0] = piece[3][0]; + piece[3][0] = piece[3][3]; + piece[3][3] = piece[0][3]; + piece[0][3] = aux; + + aux = piece[1][0]; + piece[1][0] = piece[3][1]; + piece[3][1] = piece[2][3]; + piece[2][3] = piece[0][2]; + piece[0][2] = aux; + + aux = piece[2][0]; + piece[2][0] = piece[3][2]; + piece[3][2] = piece[1][3]; + piece[1][3] = piece[0][1]; + piece[0][1] = aux; + + aux = piece[1][1]; + piece[1][1] = piece[2][1]; + piece[2][1] = piece[2][2]; + piece[2][2] = piece[1][2]; + piece[1][2] = aux; + } + + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == MOVING) + { + grid[i][j] = EMPTY; + } + } + } + + for (int i = piecePositionX; i < piecePositionX + 4; i++) + { + for (int j = piecePositionY; j < piecePositionY + 4; j++) + { + if (piece[i - piecePositionX][j - piecePositionY] == MOVING) + { + grid[i][j] = MOVING; + } + } + } + + return true; + } + + return false; +} + +static void CheckDetection(bool *detection) +{ + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if ((grid[i][j] == MOVING) && ((grid[i][j+1] == FULL) || (grid[i][j+1] == BLOCK))) *detection = true; + } + } +} + +static void CheckCompletion(bool *lineToDelete) +{ + int calculator = 0; + + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + calculator = 0; + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + // Count each square of the line + if (grid[i][j] == FULL) + { + calculator++; + } + + // Check if we completed the whole line + if (calculator == GRID_HORIZONTAL_SIZE - 2) + { + *lineToDelete = true; + calculator = 0; + // points++; + + // Mark the completed line + for (int z = 1; z < GRID_HORIZONTAL_SIZE - 1; z++) + { + grid[z][j] = FADING; + } + } + } + } +} + +static int DeleteCompleteLines() +{ + int deletedLines = 0; + + // Erase the completed line + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + while (grid[1][j] == FADING) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + grid[i][j] = EMPTY; + } + + for (int j2 = j-1; j2 >= 0; j2--) + { + for (int i2 = 1; i2 < GRID_HORIZONTAL_SIZE - 1; i2++) + { + if (grid[i2][j2] == FULL) + { + grid[i2][j2+1] = FULL; + grid[i2][j2] = EMPTY; + } + else if (grid[i2][j2] == FADING) + { + grid[i2][j2+1] = FADING; + grid[i2][j2] = EMPTY; + } + } + } + + deletedLines++; + } + } + + return deletedLines; +} diff --git a/nob.c b/nob.c index 15ef36d..e7d5126 100644 --- a/nob.c +++ b/nob.c @@ -48,6 +48,11 @@ Example examples[] = { .bin_path = "./build/text_writing_anim", .wasm_path = "./wasm/text_writing_anim.wasm", }, + { + .src_path = "./examples/tetris.c", + .bin_path = "./build/tetris", + .wasm_path = "./wasm/tetris.wasm", + }, }; bool build_native(void) diff --git a/wasm/tetris.wasm b/wasm/tetris.wasm new file mode 100755 index 0000000000000000000000000000000000000000..52fe9b6c0ba43411c866a1776713d9a107760b6a GIT binary patch literal 30292 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 literal 0 HcmV?d00001 From ee346276dc4d870992e79ff91f4d70b06ce16d4c Mon Sep 17 00:00:00 2001 From: John Kisor Date: Sun, 18 Feb 2024 22:05:39 -0500 Subject: [PATCH 2/6] Add to wasm paths in html --- index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/index.html b/index.html index 960a2cd..4888e0a 100644 --- a/index.html +++ b/index.html @@ -72,6 +72,7 @@ "shapes": ["shapes_colors_palette"], "text": ["text_writing_anim"], "textures": ["textures_logo_raylib"], + "games": ["tetris"], } const defaultWasm = Object.values(wasmPaths)[0][0]; From 0b9aad154d7b67642f7b7f5b8c6529a665a53019 Mon Sep 17 00:00:00 2001 From: John Kisor Date: Sun, 18 Feb 2024 22:06:32 -0500 Subject: [PATCH 3/6] Implement GetRandomValue --- raylib.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/raylib.js b/raylib.js index a6a0154..84a4f20 100644 --- a/raylib.js +++ b/raylib.js @@ -136,6 +136,10 @@ class RaylibJs { return Math.min(this.dt, 1.0/this.targetFPS); } + GetRandomValue(min, max) { + return Math.random() * (max - min) + min; + } + BeginDrawing() {} EndDrawing() { From 1c6065cdbb62425e2ed12b30554ba2681c4436ea Mon Sep 17 00:00:00 2001 From: John Kisor Date: Sun, 18 Feb 2024 22:07:26 -0500 Subject: [PATCH 4/6] define CloseWindow --- raylib.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/raylib.js b/raylib.js index 84a4f20..f8ca911 100644 --- a/raylib.js +++ b/raylib.js @@ -117,6 +117,8 @@ class RaylibJs { return false; } + CloseWindow() {} + SetTargetFPS(fps) { console.log(`The game wants to run at ${fps} FPS, but in Web we gonna just ignore it.`); this.targetFPS = fps; From c005184d72ba60e0b6191836dc03eb0e64774b2b Mon Sep 17 00:00:00 2001 From: John Kisor Date: Sun, 18 Feb 2024 22:10:36 -0500 Subject: [PATCH 5/6] Implement DrawLines --- raylib.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/raylib.js b/raylib.js index f8ca911..89003ae 100644 --- a/raylib.js +++ b/raylib.js @@ -182,6 +182,22 @@ class RaylibJs { } } + // RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color); // Draw a line + DrawLine(startX, startY, endX, endY, color_ptr) { + const buffer = this.wasm.instance.exports.memory.buffer; + const color = getColorFromMemory(buffer, color_ptr); + + this.ctx.beginPath(); + + this.ctx.moveTo(startX, startY); + this.ctx.lineTo(endX, endY); + + this.ctx.strokeStyle = color; + this.ctx.lineWidth = 1; + + this.ctx.stroke(); + } + // RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color); // Draw a color-filled rectangle DrawRectangle(posX, posY, width, height, color_ptr) { const buffer = this.wasm.instance.exports.memory.buffer; From 355617d6bf9767eb0423fda5e2c24af6b2207783 Mon Sep 17 00:00:00 2001 From: John Kisor Date: Sun, 18 Feb 2024 22:13:19 -0500 Subject: [PATCH 6/6] Sharpen lines with half pixel offset --- raylib.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/raylib.js b/raylib.js index 89003ae..7f8dd0e 100644 --- a/raylib.js +++ b/raylib.js @@ -189,8 +189,9 @@ class RaylibJs { this.ctx.beginPath(); - this.ctx.moveTo(startX, startY); - this.ctx.lineTo(endX, endY); + // Add 0.5 for sharp lines: http://diveintohtml5.info/canvas.html#pixel-madness + this.ctx.moveTo(startX + 0.5, startY + 0.5); + this.ctx.lineTo(endX + 0.5, endY + 0.5); this.ctx.strokeStyle = color; this.ctx.lineWidth = 1;