pong-wars

pong wars in c/raylib
git clone https://git.repetitions.de/pong-wars

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