pong.c (6578B)
1 /* Copyright (c) 2024 Lou Woell */ 2 3 #include <math.h> 4 #include <raylib.h> 5 #include <sys/param.h> 6 7 #if defined(PLATFORM_WEB) 8 #include <emscripten/emscripten.h> 9 #endif 10 11 // Color Scheme of my Website 12 #define DAY_COLOR CLITERAL(Color) { 238, 114, 241, 255} 13 #define NIGHT_COLOR CLITERAL(Color) { 33, 32, 44, 255} 14 15 // Alternative Neutral Colors 16 /* #define DAY_COLOR SKYBLUE */ 17 /* #define NIGHT_COLOR DARKBLUE */ 18 19 #define FONTSIZE 30 20 #define PAUSE_TEXT "PAUSED" 21 22 #define RECS 30 23 #define REC_SIZE 20 24 25 typedef enum { 26 DAY, 27 NIGHT, 28 } status; 29 30 typedef struct { 31 Vector2 Position; 32 33 Vector2 Direction; 34 float speed; 35 36 status team; 37 float radius; 38 } bBall; 39 40 typedef struct { 41 // Pause related 42 int framesCounter; 43 bool pause; 44 45 int BoardDim; 46 RenderTexture2D target; 47 48 // Board 49 status board[RECS][RECS]; 50 51 // Bouncing Balls 52 bBall DayBall; 53 bBall NightBall; 54 } Game; 55 56 Game game = {0}; 57 int pause_text_width = 0; 58 59 void MakeBoard() 60 { 61 for (int y = 0; y < RECS; y++) 62 { 63 for (int x = 0; x < RECS; x++) 64 { 65 if (RECS / 2 > x) 66 game.board[y][x] = DAY; 67 else 68 game.board[y][x] = NIGHT; 69 } 70 } 71 } 72 73 void DrawBoard() 74 { 75 ClearBackground(NIGHT_COLOR); 76 for (int j = 0; j < RECS; j++) 77 { 78 for (int i = 0; i < RECS; i++) 79 { 80 if (game.board[i][j]){ 81 DrawRectangle(REC_SIZE * j, 82 REC_SIZE * i, 83 REC_SIZE, 84 REC_SIZE, 85 DAY_COLOR); 86 } 87 } 88 } 89 } 90 91 int RandomOffset(int offset) 92 { 93 return GetRandomValue(-(offset), (offset)); 94 } 95 96 97 void MakeBouncingBall(bBall *ball, status team, float startx, float starty) 98 { 99 float xspeed; 100 float yspeed; 101 102 if (startx > game.BoardDim / 2.0f) 103 { 104 xspeed = -1; 105 yspeed = 1; 106 } 107 else 108 { 109 xspeed = 1; 110 yspeed = -1; 111 } 112 113 ball->Position = (Vector2){ startx, starty}; 114 ball->Direction = (Vector2){ xspeed, yspeed }; 115 ball->team = team; 116 ball->radius = REC_SIZE* 0.5; 117 ball->speed = REC_SIZE * 0.5 - 4; 118 } 119 120 void BouncingBallPosition(bBall *ball) 121 { 122 ball->Position.x += ball->Direction.x * ball->speed; 123 ball->Position.y += ball->Direction.y * ball->speed; 124 125 // Check walls collision 126 if ((ball->Position.x > (game.BoardDim - ball->radius)) || 127 (ball->Position.x < ball->radius)) 128 { 129 ball->Direction.x *= -1; 130 ball->Position.x += ball->Direction.x*ball->speed; 131 } 132 if ((ball->Position.y > (game.BoardDim - ball->radius)) || 133 (ball->Position.y < ball->radius)) 134 { 135 ball->Direction.y *= -1; 136 ball->Position.y += ball->Direction.y*ball->speed; 137 } 138 139 // Check Colour collision 140 for (double angle = 0; angle < (2 * PI); angle += (PI / 4)) 141 { 142 int i = floor((ball->Position.x + cos(angle) * ball->radius) / 143 REC_SIZE); 144 int j = floor((ball->Position.y + sin(angle) * ball->radius) / 145 REC_SIZE); 146 147 if (i >= 0 && i < RECS && j >= 0 && j < RECS) 148 { 149 if (game.board[j][i] == ball->team) 150 { 151 game.board[j][i] = !game.board[j][i]; 152 // Determine bounce direction based on the angle 153 if (fabs(cos(angle)) > fabs(sin(angle))) 154 ball->Direction.x *= -1; 155 else 156 ball->Direction.y *= -1; 157 } 158 } 159 } 160 } 161 162 void DrawBouncingBall(bBall *ball) 163 { 164 /* Draw Bouncing Ball */ 165 DrawCircleV(ball->Position, ball->radius, (ball->team ? DAY_COLOR : NIGHT_COLOR)); 166 } 167 168 void SetGame() 169 { 170 MakeBoard(); 171 MakeBouncingBall( 172 &game.DayBall, 173 DAY, 174 (game.BoardDim / 4.0f) * 3 + RandomOffset(game.BoardDim / 4), 175 game.BoardDim/2.0f + RandomOffset(game.BoardDim/2)); 176 MakeBouncingBall( 177 &game.NightBall, 178 NIGHT, 179 (game.BoardDim / 4.0f) + RandomOffset(game.BoardDim/ 4), 180 game.BoardDim/2.0f + RandomOffset(game.BoardDim/2)); 181 } 182 183 void DrawGame() 184 { 185 float scale = MIN((float)GetScreenWidth()/game.BoardDim, 186 (float)GetScreenHeight()/game.BoardDim); 187 188 BeginTextureMode(game.target); 189 190 DrawBoard(); 191 DrawBouncingBall(&game.DayBall); 192 DrawBouncingBall(&game.NightBall); 193 194 // On pause, we draw a blinking message 195 if (game.pause && ((game.framesCounter/30)%2)) 196 { 197 DrawText(PAUSE_TEXT, 198 (game.BoardDim - pause_text_width)/2, 199 game.BoardDim/3, 200 FONTSIZE, 201 WHITE); 202 } 203 EndTextureMode(); 204 205 BeginDrawing(); 206 ClearBackground(BLACK); 207 DrawTexturePro( 208 game.target.texture, 209 (Rectangle){ 210 0.0f, 211 0.0f, 212 (float)game.target.texture.width, 213 (float)-game.target.texture.height 214 }, 215 (Rectangle){ 216 (GetScreenWidth() - ((float)game.BoardDim*scale))*0.5f, 217 (GetScreenHeight() - ((float)game.BoardDim*scale))*0.5f, 218 (float)game.BoardDim*scale, 219 (float)game.BoardDim*scale 220 }, 221 (Vector2){0, 0}, 222 0.0f, 223 WHITE); 224 EndDrawing(); 225 } 226 227 228 void handleInput() 229 { 230 // Press P to Pause 231 if (IsKeyPressed(KEY_P)) game.pause = !game.pause; 232 // Press R to reset 233 if (IsKeyPressed(KEY_R)) SetGame(); 234 } 235 236 void UpdateDrawFrame() 237 { 238 handleInput(); 239 /* ScaleGame(); */ 240 if (!game.pause) 241 { 242 BouncingBallPosition(&game.DayBall); 243 BouncingBallPosition(&game.NightBall); 244 } 245 else game.framesCounter++; 246 247 DrawGame(); 248 } 249 250 int main() 251 { 252 game.BoardDim = RECS*REC_SIZE; 253 254 SetConfigFlags(FLAG_VSYNC_HINT | FLAG_WINDOW_RESIZABLE); 255 InitWindow(game.BoardDim,game.BoardDim, "Pong Wars"); 256 257 game.target = LoadRenderTexture(game.BoardDim, game.BoardDim); 258 SetTextureFilter(game.target.texture, TEXTURE_FILTER_POINT); 259 260 pause_text_width = MeasureText(PAUSE_TEXT, FONTSIZE); 261 262 SetGame(); 263 264 #if defined(PLATFORM_WEB) 265 emscripten_set_main_loop(UpdateDrawFrame, 0, 1); 266 #else 267 SetTargetFPS(60); 268 while (!WindowShouldClose()) 269 { 270 UpdateDrawFrame(); 271 } 272 #endif 273 274 CloseWindow(); 275 return 0; 276 } 277