From f28c4e70339a68935811c7df0dc8075ec79ebdab Mon Sep 17 00:00:00 2001 From: tolik518 Date: Tue, 28 Apr 2026 20:44:48 +0200 Subject: [PATCH 1/5] let the ball go in an angle --- code/source/ball.c | 160 ++++++++++++++------------------------------ code/source/ball.h | 20 ++---- code/source/scene.c | 15 ++--- 3 files changed, 60 insertions(+), 135 deletions(-) diff --git a/code/source/ball.c b/code/source/ball.c index b57ad40..b8d6281 100644 --- a/code/source/ball.c +++ b/code/source/ball.c @@ -6,7 +6,7 @@ #include <../build/soundbank.h> #include <../build/soundbank_bin.h> -// Collisions of the wall and the walls +// Collisions of the ball and the walls #define BALL_COLLISION_TOP self->x - (self->h / 2) <= 0 #define BALL_COLLISION_LEFT self->y - (self->h / 2) <= 0 #define BALL_COLLISION_RIGHT self->y + (self->h / 2) >= SCREEN_WIDTH - 1 @@ -43,122 +43,60 @@ bool checkCollisionWithPaddle(const Ball *self, Paddle *player) return true; } -//return 1 = left player lost -//return 2 = right player lost -static int moveTopLeft(Ball *self, Paddle *player) -{ - self->x -= self->speedX; - self->y -= self->speedY; - - if (BALL_COLLISION_TOP) { - mmEffect( SFX_CLICK ); - self->dir = BALL_MOVES_BOTTOMLEFT; - return 0; - } - - if (checkCollisionWithPaddle(self, player)) { - mmEffect( SFX_CLICK ); - self->dir = BALL_MOVES_TOPRIGHT; - return 0; - } - - if (BALL_COLLISION_LEFT) { - return 1; - } - - return 0; -} - -static int moveBottomLeft(Ball *self, Paddle *player) +// Adjust vertical speed (dx) based on where the ball hit the paddle. +// Hitting the center returns the ball nearly horizontal; +// hitting the edges gives a steeper angle. +static void _adjustBounceAngle(Ball *self, Paddle *paddle) { - self->x += self->speedX; - self->y -= self->speedY; - - if (BALL_COLLISION_BOTTOM) { - mmEffect( SFX_CLICK ); - self->dir = BALL_MOVES_TOPLEFT; - return 0; - } - - if (checkCollisionWithPaddle(self, player)) { - mmEffect( SFX_CLICK ); - self->dir = BALL_MOVES_BOTTOMRIGHT; - return 0; - } - - if (BALL_COLLISION_LEFT) { - return 1; - } + int ball_center = self->x; + int paddle_center = paddle->x + paddle->h / 2; + int offset = ball_center - paddle_center; + int half_h = paddle->h / 2; - return 0; + self->dx = offset * BALL_MAX_DX / half_h; } -static int moveBottomRight(Ball *self, Paddle *player) -{ - self->x += self->speedX; - self->y += self->speedY; - - if (BALL_COLLISION_BOTTOM) { - mmEffect( SFX_CLICK ); - self->dir = BALL_MOVES_TOPRIGHT; - return 0; - } - - if (checkCollisionWithPaddle(self, player)) { - mmEffect( SFX_CLICK ); - self->dir = BALL_MOVES_BOTTOMLEFT; - return 0; - } - - if (BALL_COLLISION_RIGHT) { - return 2; - } - return 0; -} - -static int moveTopRight(Ball *self, Paddle *player) -{ - self->x -= self->speedX; - self->y += self->speedY; - - if (BALL_COLLISION_TOP) { - mmEffect( SFX_CLICK ); - self->dir = BALL_MOVES_BOTTOMRIGHT; - return 0; - } - - if (checkCollisionWithPaddle(self, player)) { - mmEffect( SFX_CLICK ); - self->dir = BALL_MOVES_TOPLEFT; - return 0; - } - - if (BALL_COLLISION_RIGHT) { - return 2; - } - - return 0; -} - -//game->ball, game->p1, game->p2 -//int Ball_moveAndCollide(Ball *self, Paddle *p1, Paddle *p2) +//return 1 = left player lost +//return 2 = right player lost int Ball_moveAndCollide(Game *game) { - Ball *self = game->ball; - switch (game->ball->dir) - { - case BALL_MOVES_TOPLEFT: - return moveTopLeft(self, game->p1); - - case BALL_MOVES_BOTTOMLEFT: - return moveBottomLeft(self, game->p1); - - case BALL_MOVES_BOTTOMRIGHT: - return moveBottomRight(self, game->p2); - - case BALL_MOVES_TOPRIGHT: - return moveTopRight(self, game->p2); - } + Ball *self = game->ball; + + // Apply velocity + self->x += self->dx; + self->y += self->dy; + + // Top/bottom wall bounce (flip vertical component) + if (BALL_COLLISION_TOP) { + self->x = self->h / 2; + self->dx = -self->dx; + mmEffect(SFX_CLICK); + } else if (BALL_COLLISION_BOTTOM) { + self->x = SCREEN_HEIGHT - 1 - self->h / 2; + self->dx = -self->dx; + mmEffect(SFX_CLICK); + } + + // Paddle collision / scoring (horizontal axis) + if (self->dy < 0) { + // Moving left -> check P1 paddle + if (checkCollisionWithPaddle(self, game->p1)) { + _adjustBounceAngle(self, game->p1); + self->dy = -self->dy; + mmEffect(SFX_CLICK); + } else if (BALL_COLLISION_LEFT) { + return 1; + } + } else { + // Moving right -> check P2 paddle + if (checkCollisionWithPaddle(self, game->p2)) { + _adjustBounceAngle(self, game->p2); + self->dy = -self->dy; + mmEffect(SFX_CLICK); + } else if (BALL_COLLISION_RIGHT) { + return 2; + } + } return 0; } diff --git a/code/source/ball.h b/code/source/ball.h index 86b2cfd..4a1f572 100644 --- a/code/source/ball.h +++ b/code/source/ball.h @@ -4,19 +4,8 @@ #include <../include/tonc.h> typedef struct game Game; -/*********-TOP-**********\ -*.1....................4.* -*........................* -LEFT.................RIGHT -*........................* -*.2....................3.* -\********-BOTTOM-********/ - -// Directions the ball can go -#define BALL_MOVES_TOPLEFT 0 -#define BALL_MOVES_BOTTOMLEFT 1 -#define BALL_MOVES_BOTTOMRIGHT 2 -#define BALL_MOVES_TOPRIGHT 3 +// Maximum vertical speed the ball can reach after a paddle bounce +#define BALL_MAX_DX 3 typedef struct ball { @@ -25,10 +14,9 @@ typedef struct ball int prev_x; // crucial for cleaing up previous pixels int prev_y; int h; //height - int dir; //direction 0 to 3 + int dx; // vertical velocity (positive = down, negative = up) + int dy; // horizontal velocity (positive = right, negative = left) COLOR color; - int speedX; - int speedY; } Ball; int Ball_moveAndCollide(Game *game); diff --git a/code/source/scene.c b/code/source/scene.c index 322220e..a1741dc 100644 --- a/code/source/scene.c +++ b/code/source/scene.c @@ -35,7 +35,6 @@ // --- Link message payload masks --- #define MASK_BALL_X 0x7FFF #define MASK_BALL_Y 0x3FFF -#define MASK_DIR 0x000F // --- Round-end status codes (from Ball_moveAndCollide) --- #define STATUS_NONE 0 @@ -71,8 +70,8 @@ // --- Ball defaults --- #define BALL_SIZE 7 #define BALL_COLOR 27 -#define BALL_SPEED_X 1 -#define BALL_SPEED_Y 2 +#define BALL_INIT_DX 1 +#define BALL_SPEED_H 2 // --- AI --- #define AI_DEADZONE 5 @@ -233,10 +232,9 @@ void Scene_showGamescreen(int *frame, LinkConnection *conn) prev_x: SCREEN_HEIGHT/2, prev_y: SCREEN_WIDTH/2, h: BALL_SIZE, - dir: randDir, + dx: (randDir & 2) ? BALL_INIT_DX : -BALL_INIT_DX, + dy: (randDir & 1) ? BALL_SPEED_H : -BALL_SPEED_H, color: BALL_COLOR, - speedX: BALL_SPEED_X, - speedY: BALL_SPEED_Y }; Game _game = { @@ -298,7 +296,8 @@ void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, int randomNube if (!is_master) self->ball->y = (int)(msg & MASK_BALL_Y); } else if (msg & MSG_DIR) { if (!is_master && !round_synced) { - self->ball->dir = (int)(msg & MASK_DIR); + self->ball->dx = (int)(((msg >> 4) & 0x0F)) - 4; + self->ball->dy = (int)((msg & 0x0F)) - 4; round_synced = true; } } else if (msg & MSG_READY) { @@ -334,7 +333,7 @@ void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, int randomNube // Master keeps sending ball direction until ball starts, // So slave picks it up even if it enters the round late if (is_master && !self->isRunning) { - lc_send(conn, MSG_DIR | (u16)(self->ball->dir & MASK_DIR)); + lc_send(conn, MSG_DIR | (u16)((((self->ball->dx + 4) & 0x0F) << 4) | ((self->ball->dy + 4) & 0x0F))); round_synced = true; } // Keep signaling ready until ball actually starts From ddaee601685de40579c1dfaad41086ae31628171 Mon Sep 17 00:00:00 2001 From: tolik518 Date: Tue, 28 Apr 2026 20:51:19 +0200 Subject: [PATCH 2/5] ball should not start in the center --- code/source/scene.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/code/source/scene.c b/code/source/scene.c index a1741dc..fbe0e2a 100644 --- a/code/source/scene.c +++ b/code/source/scene.c @@ -81,7 +81,7 @@ #define LINE_COLOR 24 #define BORDER_COLOR 24 -void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, int randomNuber, LinkConnection *conn); +void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, LinkConnection *conn); void _move_paddle_to(int direction, Paddle *paddle); void _ai_decision(Game *self, int *last_enemy_move, int correct_move_chance); void _renderGame(Game *self, LinkConnection *conn); @@ -194,6 +194,7 @@ void Scene_showGamescreen(int *frame, LinkConnection *conn) { int scoreP1 = 0; int scoreP2 = 0; + int lastLoser = 0; // 0 = game start (P1 serves), STATUS_P1_LOST or STATUS_P2_LOST // main loop while(true) @@ -222,18 +223,22 @@ void Scene_showGamescreen(int *frame, LinkConnection *conn) score: scoreP2 }; - // Randomized direction - #pragma GCC diagnostic ignored "-Wuninitialized" - int randDir = (((*frame) + randDir) % 4); + // Ball spawns in front of the serving player's paddle + // Game start or P2 lost → P1 serves (ball goes right) + // P1 lost → P2 serves (ball goes left) + bool p2_serves = (lastLoser == STATUS_P1_LOST); + int ball_start_y = p2_serves + ? (_p2.y - BALL_SIZE / 2 - 1) + : (_p1.y + _p1.w + BALL_SIZE / 2 + 1); Ball _ball = { x: SCREEN_HEIGHT/2 - 1, - y: SCREEN_WIDTH/2 - 1, + y: ball_start_y, prev_x: SCREEN_HEIGHT/2, - prev_y: SCREEN_WIDTH/2, + prev_y: ball_start_y, h: BALL_SIZE, - dx: (randDir & 2) ? BALL_INIT_DX : -BALL_INIT_DX, - dy: (randDir & 1) ? BALL_SPEED_H : -BALL_SPEED_H, + dx: BALL_INIT_DX, + dy: p2_serves ? -BALL_SPEED_H : BALL_SPEED_H, color: BALL_COLOR, }; @@ -246,11 +251,16 @@ void Scene_showGamescreen(int *frame, LinkConnection *conn) Game *self = &_game; - _runGame(self, frame, &scoreP1, &scoreP2, randDir, conn); + int prevScoreP1 = scoreP1; + + _runGame(self, frame, &scoreP1, &scoreP2, conn); + + // The player whose opponent scored is the loser → they serve next + lastLoser = (scoreP1 > prevScoreP1) ? STATUS_P2_LOST : STATUS_P1_LOST; } } -void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, int randomNuber, LinkConnection *conn) +void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, LinkConnection *conn) { int status = 0; int last_enemy_move = IDLE; From eef5e8f8e63f6614a8c16375161f15eea79069a4 Mon Sep 17 00:00:00 2001 From: tolik518 Date: Tue, 28 Apr 2026 20:57:24 +0200 Subject: [PATCH 3/5] press A to start the game --- code/source/game.c | 6 +++--- code/source/scene.c | 33 +++++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/code/source/game.c b/code/source/game.c index 60b8836..a808aa5 100644 --- a/code/source/game.c +++ b/code/source/game.c @@ -70,10 +70,10 @@ void Game_setPauseText() (void) initializeScoreWriter(); //show text centered on the screen - tte_set_pos((SCREEN_WIDTH/2)-30, (SCREEN_HEIGHT/2)-6); - tte_erase_rect((SCREEN_WIDTH/2)-30, (SCREEN_HEIGHT/2)-5, (SCREEN_WIDTH/2)+30, (SCREEN_HEIGHT/2)+6); + tte_set_pos((SCREEN_WIDTH/2)-24, (SCREEN_HEIGHT/2)-6); + tte_erase_rect((SCREEN_WIDTH/2)-24, (SCREEN_HEIGHT/2)-5, (SCREEN_WIDTH/2)+24, (SCREEN_HEIGHT/2)+6); - tte_write("PRESS START"); + tte_write("PRESS A"); } void Game_removePauseText() diff --git a/code/source/scene.c b/code/source/scene.c index fbe0e2a..a0c79e7 100644 --- a/code/source/scene.c +++ b/code/source/scene.c @@ -269,6 +269,8 @@ void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, LinkConnection bool round_synced = !is_multiplayer; // In multiplayer, track whether the remote player is in the game bool remote_ready = !is_multiplayer; + // Wait for the local player to press A before launching the ball + bool local_ready = false; // Drain any leftover messages from the previous round if (is_multiplayer) { @@ -324,6 +326,11 @@ void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, LinkConnection Paddle *local_paddle = is_master ? self->p1 : self->p2; Paddle *remote_paddle = is_master ? self->p2 : self->p1; + // --- Check for A press to launch ball --- + if (!local_ready && key_is_down(KEY_A)) { + local_ready = true; + } + // --- Move local player --- int move_dir = IDLE; if (key_is_down(KEY_DOWN)) { @@ -347,7 +354,7 @@ void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, LinkConnection round_synced = true; } // Keep signaling ready until ball actually starts - if (!self->isRunning) + if (!self->isRunning && local_ready) lc_send(conn, MSG_READY); lc_send(conn, move_dir); } @@ -363,9 +370,14 @@ void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, LinkConnection _ai_decision(self, &last_enemy_move, (*frame % AI_CORRECT_PERIOD) < 1); } + // --- Before launch: ball follows the serving paddle vertically --- + if (!self->isRunning) { + // Determine which paddle is serving + Paddle *serve_paddle = (self->ball->dy > 0) ? self->p1 : self->p2; + self->ball->x = serve_paddle->x + serve_paddle->h / 2; + } + // --- Ball physics: master only --- - // Master runs Ball_moveAndCollide and broadcasts the result. - // Slave detects scoring from ball position -> no MSG_STATUS needed. if (self->isRunning && is_master) { status = Ball_moveAndCollide(self); if (is_multiplayer) { @@ -384,12 +396,21 @@ void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, LinkConnection _renderGame(self, conn); + // Show "PRESS A" prompt while waiting to launch + if (!self->isRunning) { + Game_setPauseText(); + } + // Start the ball: - // Solo: immediately on first frame - // Multiplayer: wait until both players are in the game + // Solo: when A is pressed + // Multiplayer: when both players pressed A and round is synced if (!self->isRunning) { - if (!is_multiplayer || (round_synced && remote_ready)) { + bool ready = local_ready; + if (is_multiplayer) + ready = local_ready && round_synced && remote_ready; + if (ready) { + Game_removePauseText(); mmEffect(SFX_LOST); self->isRunning = true; } From 8cd90f1a9fc7d07b5552f174c467f4e873ab51bd Mon Sep 17 00:00:00 2001 From: tolik518 Date: Tue, 28 Apr 2026 21:18:43 +0200 Subject: [PATCH 4/5] added pause and fixed tunneling bug --- code/source/ball.c | 22 ++++++++++++++++++++-- code/source/game.c | 10 ++++++++++ code/source/game.h | 1 + code/source/scene.c | 29 +++++++++++++++++++++++++---- 4 files changed, 56 insertions(+), 6 deletions(-) diff --git a/code/source/ball.c b/code/source/ball.c index b8d6281..a58a1c6 100644 --- a/code/source/ball.c +++ b/code/source/ball.c @@ -61,6 +61,7 @@ static void _adjustBounceAngle(Ball *self, Paddle *paddle) int Ball_moveAndCollide(Game *game) { Ball *self = game->ball; + int prev_y = self->y; // Apply velocity self->x += self->dx; @@ -78,9 +79,18 @@ int Ball_moveAndCollide(Game *game) } // Paddle collision / scoring (horizontal axis) + // Use swept check: did the ball's leading edge cross the paddle front this frame? if (self->dy < 0) { // Moving left -> check P1 paddle - if (checkCollisionWithPaddle(self, game->p1)) { + int front = game->p1->y + game->p1->w; + int ball_left_prev = prev_y - (self->h / 2); + int ball_left_now = BALL_POSITION_LEFT; + // Ball crossed (or touched) the paddle's right edge this frame + if (ball_left_now <= front && ball_left_prev >= front && + BALL_POSITION_BOTTOM > game->p1->x && + BALL_POSITION_TOP < (game->p1->x + game->p1->h)) { + // Snap ball to paddle surface to prevent overlap + self->y = front + self->h / 2; _adjustBounceAngle(self, game->p1); self->dy = -self->dy; mmEffect(SFX_CLICK); @@ -89,7 +99,15 @@ int Ball_moveAndCollide(Game *game) } } else { // Moving right -> check P2 paddle - if (checkCollisionWithPaddle(self, game->p2)) { + int front = game->p2->y; + int ball_right_prev = prev_y + (self->h / 2); + int ball_right_now = BALL_POSITION_RIGHT; + // Ball crossed (or touched) the paddle's left edge this frame + if (ball_right_now >= front && ball_right_prev <= front && + BALL_POSITION_BOTTOM > (game->p2->x) && + BALL_POSITION_TOP < (game->p2->x + game->p2->h)) { + // Snap ball to paddle surface to prevent overlap + self->y = front - self->h / 2; _adjustBounceAngle(self, game->p2); self->dy = -self->dy; mmEffect(SFX_CLICK); diff --git a/code/source/game.c b/code/source/game.c index a808aa5..da2f317 100644 --- a/code/source/game.c +++ b/code/source/game.c @@ -81,6 +81,16 @@ void Game_removePauseText() tte_erase_rect((SCREEN_WIDTH/2)-40, (SCREEN_HEIGHT/2)-5, (SCREEN_WIDTH/2)+40, (SCREEN_HEIGHT/2)+6); } +void Game_showPausedText() +{ + (void) initializeScoreWriter(); + + tte_set_pos((SCREEN_WIDTH/2)-22, (SCREEN_HEIGHT/2)-6); + tte_erase_rect((SCREEN_WIDTH/2)-22, (SCREEN_HEIGHT/2)-5, (SCREEN_WIDTH/2)+22, (SCREEN_HEIGHT/2)+6); + + tte_write("PAUSED"); +} + void Game_gameLoop(LinkConnection *conn) { int _frame = 0; diff --git a/code/source/game.h b/code/source/game.h index a738fb2..0e67c61 100644 --- a/code/source/game.h +++ b/code/source/game.h @@ -26,6 +26,7 @@ void Game_renderBall(Ball *ball); void Game_updateScore(const Paddle *p1, const Paddle *p2); void Game_removePauseText(); void Game_setPauseText(); +void Game_showPausedText(); #endif diff --git a/code/source/scene.c b/code/source/scene.c index a0c79e7..87a1a45 100644 --- a/code/source/scene.c +++ b/code/source/scene.c @@ -87,7 +87,6 @@ void _ai_decision(Game *self, int *last_enemy_move, int correct_move_chance); void _renderGame(Game *self, LinkConnection *conn); static bool _link_connected = false; -static int _link_check_timer = 0; static void _draw_link_indicator(LinkConnection *conn) { @@ -271,6 +270,8 @@ void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, LinkConnection bool remote_ready = !is_multiplayer; // Wait for the local player to press A before launching the ball bool local_ready = false; + // Pause state (solo only) + bool paused = false; // Drain any leftover messages from the previous round if (is_multiplayer) { @@ -285,6 +286,22 @@ void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, LinkConnection VBlankIntrWait(); key_poll(); + // --- Pause toggle (solo mode only, only while ball is running) --- + if (!is_multiplayer && self->isRunning && key_hit(KEY_START)) { + paused = !paused; + if (paused) { + Game_showPausedText(); + } else { + Game_removePauseText(); + } + } + + // While paused, skip all game logic + if (paused) { + (*frame)++; + continue; + } + Game_updateScore(self->p1, self->p2); bool is_master = !is_multiplayer || (conn->state.current_player_id == MASTER_ID); @@ -375,6 +392,10 @@ void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, LinkConnection // Determine which paddle is serving Paddle *serve_paddle = (self->ball->dy > 0) ? self->p1 : self->p2; self->ball->x = serve_paddle->x + serve_paddle->h / 2; + // Broadcast ball position to slave during pre-launch too + if (is_multiplayer && is_master) { + lc_send(conn, MSG_BALL_X | (u16)self->ball->x); + } } // --- Ball physics: master only --- @@ -389,9 +410,9 @@ void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, LinkConnection // Slave: detect scoring from the ball position received from master if (self->isRunning && !is_master && is_multiplayer) { if (self->ball->y - (self->ball->h / 2) <= 0) - status = STATUS_P1_LOST; - if (self->ball->y + (self->ball->h / 2) >= SCREEN_WIDTH - 1) - status = STATUS_P2_LOST; + status = STATUS_P1_LOST; + if (self->ball->y + (self->ball->h / 2) >= SCREEN_WIDTH - 1) + status = STATUS_P2_LOST; } _renderGame(self, conn); From ff4c447d90edea4e60cf693f62e091734751d89c Mon Sep 17 00:00:00 2001 From: tolik518 Date: Tue, 28 Apr 2026 21:47:40 +0200 Subject: [PATCH 5/5] speed up ball after X hits --- code/source/ball.c | 15 +++++++++++++++ code/source/ball.h | 5 +++++ code/source/scene.c | 6 ++++++ 3 files changed, 26 insertions(+) diff --git a/code/source/ball.c b/code/source/ball.c index a58a1c6..634414c 100644 --- a/code/source/ball.c +++ b/code/source/ball.c @@ -56,6 +56,19 @@ static void _adjustBounceAngle(Ball *self, Paddle *paddle) self->dx = offset * BALL_MAX_DX / half_h; } +// After a paddle hit, increment hit counter and speed up if interval reached +static void _maybeSpeedUp(Ball *self) +{ + self->hits++; + if (self->hits % BALL_SPEEDUP_INTERVAL == 0) { + int sign = (self->dy > 0) ? 1 : -1; + int speed = (self->dy > 0) ? self->dy : -self->dy; + if (speed < BALL_MAX_DY) { + self->dy = sign * (speed + 1); + } + } +} + //return 1 = left player lost //return 2 = right player lost int Ball_moveAndCollide(Game *game) @@ -93,6 +106,7 @@ int Ball_moveAndCollide(Game *game) self->y = front + self->h / 2; _adjustBounceAngle(self, game->p1); self->dy = -self->dy; + _maybeSpeedUp(self); mmEffect(SFX_CLICK); } else if (BALL_COLLISION_LEFT) { return 1; @@ -110,6 +124,7 @@ int Ball_moveAndCollide(Game *game) self->y = front - self->h / 2; _adjustBounceAngle(self, game->p2); self->dy = -self->dy; + _maybeSpeedUp(self); mmEffect(SFX_CLICK); } else if (BALL_COLLISION_RIGHT) { return 2; diff --git a/code/source/ball.h b/code/source/ball.h index 4a1f572..5ffd10e 100644 --- a/code/source/ball.h +++ b/code/source/ball.h @@ -7,6 +7,10 @@ typedef struct game Game; // Maximum vertical speed the ball can reach after a paddle bounce #define BALL_MAX_DX 3 +// Ball speeds up every BALL_SPEEDUP_INTERVAL paddle hits +#define BALL_SPEEDUP_INTERVAL 4 +#define BALL_MAX_DY 4 + typedef struct ball { int x; @@ -16,6 +20,7 @@ typedef struct ball int h; //height int dx; // vertical velocity (positive = down, negative = up) int dy; // horizontal velocity (positive = right, negative = left) + int hits; // paddle hit counter for speed-up COLOR color; } Ball; diff --git a/code/source/scene.c b/code/source/scene.c index 87a1a45..c51b807 100644 --- a/code/source/scene.c +++ b/code/source/scene.c @@ -238,6 +238,7 @@ void Scene_showGamescreen(int *frame, LinkConnection *conn) h: BALL_SIZE, dx: BALL_INIT_DX, dy: p2_serves ? -BALL_SPEED_H : BALL_SPEED_H, + hits: 0, color: BALL_COLOR, }; @@ -350,16 +351,21 @@ void _runGame(Game *self, int *frame, int *scoreP1, int *scoreP2, LinkConnection // --- Move local player --- int move_dir = IDLE; + bool sprinting = key_is_down(KEY_B); if (key_is_down(KEY_DOWN)) { if (NO_COLLISION_BOTTOM(local_paddle)) { move_dir = BOTTOM; _move_paddle_to(BOTTOM, local_paddle); + if (sprinting && NO_COLLISION_BOTTOM(local_paddle)) + _move_paddle_to(BOTTOM, local_paddle); } } if (key_is_down(KEY_UP)) { if (NO_COLLISION_TOP(local_paddle)) { move_dir = TOP; _move_paddle_to(TOP, local_paddle); + if (sprinting && NO_COLLISION_TOP(local_paddle)) + _move_paddle_to(TOP, local_paddle); } }