0% found this document useful (0 votes)
4 views42 pages

CG Project Egg Catching

The Egg Catching Game is a 2D arcade-style game developed in C using OpenGL, where players catch falling eggs from hens using a movable basket. The game features various egg types that affect scoring, with a limit on missed eggs and lives, and includes user controls for starting, pausing, and navigating through game states. The project is part of a Computer Graphics course at Thapar Institute, submitted by three students to Dr. Kuntal Chowdhury.

Uploaded by

asandhube22
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views42 pages

CG Project Egg Catching

The Egg Catching Game is a 2D arcade-style game developed in C using OpenGL, where players catch falling eggs from hens using a movable basket. The game features various egg types that affect scoring, with a limit on missed eggs and lives, and includes user controls for starting, pausing, and navigating through game states. The project is part of a Computer Graphics course at Thapar Institute, submitted by three students to Dr. Kuntal Chowdhury.

Uploaded by

asandhube22
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 42

Computer Graphics (UCS505)

Project Name
Egg Catching Game

Branch
B.E. 3rd Year – COE

Submitted By –
Kunal Ratra (102203505)
Anahit Sandhu (102203625)
Sudhanshu Dhawan (102203978)

Submitted To –

Dr. Kuntal Chowdhury

Computer Science and Engineering Department


Thapar Institute of Engineering and Technology

Patiala – 147001
INTRODUCTION

Project Overview:

The Egg Catching Game is a classic 2D arcade-style game developed in C with the
OpenGL graphics library. The game has a basket at the bottom of the screen that
can be moved left and right. The player needs to catch eggs falling from the top,
from the three hens. The game offers a competitive experience by changing the
speed of the falling eggs, making it more difficult as the level increases. It counts
the player's score and number of missed eggs, concluding the game after 10 misses
or when a black egg is caught. The graphics are made with simple OpenGL
primitives in the Linux operating system, and the game is controlled using
keyboard input/keypad.

Scope of the Project:

Core Gameplay:
●​ There are 3 hens at the top of the screen that are continuously moving and
dropping eggs.
●​ There is a basket at the bottom of the screen that can be moved by the user,
and should be put to use to catch the falling eggs.
●​ Catching the eggs increases the score. The score is increased depending on
the type of egg caught. Missing an egg increases the ‘missed’ counter. The
ends after 10 misses. The player gets 3 lives.
Graphics and Rendering:
●​ The game uses OpenGL for rendering 2D shapes and animation.
●​ The game elements include the basket, eggs, background, score display, and
menus.
User Interaction:
●​ The keypad is used to move the basket.
●​ ‘S’ is pressed to start the game
●​ ‘Q’ is pressed to quit the game.
1
●​ ‘R’ is pressed to restart the game
●​ ‘N’ is pressed to go to the next level.
●​ ‘P’ is pressed to pause the game.
Game States:
●​ Home Screen with the instructions.
●​ The gaming screen where you play the game.
●​ The game over screen appears once you lose a game.
Scoring and Progression:
●​ Score increases by 10 if you catch the normal white egg.
●​ Score increases by 25 if you catch the yellow egg.
●​ Score increases by 50 if you catch the rainbow egg.
●​ Your lives get reduced by 1 if you catch the bomb egg.
●​ The game gets over if you catch the black egg.
Tech Stack used:
●​ Programming Languages: C
●​ Operating System: Linux
●​ Graphics Library: OpenGL, GLUT

2
USER-DEFINED FUNCTIONS

S No. Function Name Function Description

1. void menu(int id) This function handles menu selections by calling appropriate functions based
on the selected option: starting the game, printing the score, or quitting the
game.

2. void This function handles left and right arrow key inputs to move the basket
specialKeys(int horizontally, ensuring it stays within the window boundaries.
key, int x, int y)

3. void This function handles keyboard input for game controls such as starting,
keys(unsigned pausing, resuming, quitting the game, restarting after game over, moving to
char key, int x, int the next level, and toggling day/night mode, while also resetting game
y) variables as needed.

4. void This function handles window resizing by updating the viewport and
myReshape(int w, projection matrix for 2D rendering, and adjusts the basket's position to ensure
int h) it remains within the new window width.

5. void Draws a colored, shaped power-up icon at its position with a unique
drawPowerUp() symbol and animation based on its type.

6. void myinit() This function sets up the initial OpenGL state for 2D rendering,
including defining the viewport, setting up an orthographic projection,
and preparing the matrix modes.
7. void drawText() Displays a colored text string at (x, y) using the specified font by drawing
each character with OpenGL bitmap rendering.

8. void display() This function is the core rendering routine that clears the screen and draws all
game elements—background, characters, eggs, power-ups, UI, and
effects—based on the current game state (menu, playing, paused, game over,
or level complete), ensuring the correct visuals are displayed at all times.

9. void start_screen() Displays the start screen, where there are instructions to play the game.

10. void Displays the game over screen.


gameOver_screen(
)

3
11. void Displays the pause screen.
pause_screen()
12. void Displays the level complete screen.
level_complete_sc
reen()
13. void sun(),void Draws all the elements in the game.
moon(), void
cloud(),void
ground(),void
drawEgg(),void
drawbasket(), void
drawHen()
14. void Function handles all real-time gameplay logic including egg catching, hen
updateGame(float animation, power-up effects, combo scoring, and transitioning between game
delta) states like playing and game over

15. void Helps in toggling day and night simultaneously.


toggle_day_night()
16. void Renders fading particle effects by drawing semi-transparent points at their
drawParticles() positions with alpha based on remaining life, using blending.

17. void Helps to provide power-up options like expanding the basket, extra life, and
spawnPowerUp(fl invincibility
oat x, float y)
18. void addParticle() Spawns a new particle at (x, y) with random velocity, full life, and given
color in the first available inactive slot.

19. void Updates active particles by moving them based on velocity, applying gravity,
updateParticles() and decreasing their life over time using the delta factor.

20. void Draws a progress bar at the top of the screen to note the players’ progress.
drawProgressBar(
)
21. void resetEgg() Generate and throws an egg from hen on various conditions of different color
like golden, yellow, black, rainbow, normal.

22. void highScore() Displays the high score of an individual.

23. void Prints the score at the end of the game.


print_score_to_co
nsole()

4
CODE SNIPPETS

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <GL/glut.h>
#include <unistd.h>

#define PI 3.14159265358979323846
#define HEN_COUNT 3
#define MAX_POWERUPS 3
#define MAX_PARTICLES 50

// Define Color structure first


typedef struct {
float r, g, b;
} Color;

// Particle structure for effects


typedef struct {
float x, y;
float vx, vy;
float life;
Color color;
} Particle;

// Egg types enum


typedef enum {
EGG_NORMAL,
EGG_GOLDEN,
EGG_BOMB,
EGG_BLACK,
EGG_RAINBOW
} EggType;

// Power-up types enum


typedef enum {
POWERUP_EXPAND,
POWERUP_SLOW,
POWERUP_EXTRA_LIFE,
POWERUP_INVINCIBLE
} PowerUpType;

// Game State Enum


5
typedef enum {
MENU,
PLAYING,
GAME_OVER,
PAUSED,
LEVEL_COMPLETE
} GameState;

GameState currentGameState = MENU;

// Global Variables
int eggs_caught = 0;
int active_hen_index = -1;
int missed_eggs = 0;
int level_count = 1;
int points = 0;
int dropped_eggs = 0;
int lives = 3;
int combo = 0;
int max_combo = 0;
int invincible = 0;
time_t last_combo_time = 0;
int day_mode = 1; // 1 for day, 0 for night
time_t day_night_toggle_time = 0;
Particle particles[MAX_PARTICLES];

// Egg properties
typedef struct {
float x, y;
int active;
EggType type;
float speed;
Color color;
} Egg;

Egg eggs[1]; // Only one egg can fall at a time

// Power-up properties
typedef struct {
float x, y;
int active;
PowerUpType type;
float speed;
time_t spawn_time;
} PowerUp;

PowerUp powerups[MAX_POWERUPS];

6
time_t powerup_end_time = 0;
time_t expand_end_time = 0;
time_t slow_end_time = 0;
time_t invincible_end_time = 0;

// Basket properties
float basket_x = 300;
const float basket_y = 10;
int basket_width = 60;
const int basket_height = 20;
float basket_bounce = 0;
int basket_expanded = 0;

// Screen dimensions
int windowWidth = 600;
int windowHeight = 650;

// Hen properties
float hen_positions[HEN_COUNT] = {115.0f, 255.0f, 390.0f};
float hen_velocities[HEN_COUNT] = {1.0f, -1.0f, 1.5f};
int hen_animation_frame[HEN_COUNT] = {0};
float hen_wings[HEN_COUNT] = {0.0f}; // Wing animation state

// Game settings
const float speed_levels[] = {2.5f, 3.0f, 3.5f, 4.0f, 4.5f};
const int max_level = sizeof(speed_levels) / sizeof(speed_levels[0]);
const int eggs_per_level = 5;
const int max_missed_eggs = 10;
float current_egg_speed = 1.0f;
float game_speed = 1.0f;
time_t last_egg_time = 0;
int egg_fall_delay = 2000;
int high_score = 0;

// Colors
Color background_day = {0.5f, 0.7f, 1.0f};
Color background_night = {0.1f, 0.1f, 0.3f};
Color ground_day = {0.3f, 0.6f, 0.2f};
Color ground_night = {0.1f, 0.3f, 0.1f};

void myinit();
void drawText(float x, float y, const char* text, void* font, Color color);
void start_screen();
void gameOver_screen();
void pause_screen();
void level_complete_screen();
void sun();

7
void moon();
void cloud(float baseX, float baseY, float scale);
void ground();
void drawEgg(Egg egg);
void drawBasket(float x, float y);
void drawHen(float x, float y, int frame, float wingState);
void drawPowerUp(PowerUp pu);
void drawParticles();
void addParticle(float x, float y, Color color);
void updateParticles(float delta);
void print_score_to_console();
void resetEgg();
void spawnPowerUp(float x, float y);
void updateGame(float delta);
void display(void);
void basket_set(int x, int y);
void myReshape(int w, int h);
void keys(unsigned char key, int x, int y);
void specialKeys(int key, int x, int y);
void menu(int id);
void idleFunc();
void timerFunc(int value);
void sound_effect(int type);
void toggle_day_night();
void load_high_score();
void save_high_score();
void drawProgressBar(float x, float y, float width, float height, float progress, Color
color);
void drawComboMeter(float x, float y, float width, float height, int combo);

// Initialization
void myinit() {
glViewport(0, 0, windowWidth, windowHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, (GLdouble)windowWidth, 0, (GLdouble)windowHeight);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(background_day.r, background_day.g, background_day.b, 1.0);
current_egg_speed = speed_levels[0];
basket_x = windowWidth / 2 - basket_width / 2;

// Initialize egg
eggs[0].active = 0;
eggs[0].color = (Color){1.0f, 1.0f, 1.0f};

// Initialize powerups

8
for (int i = 0; i < MAX_POWERUPS; i++) {
powerups[i].active = 0;
}

// Initialize particles
for (int i = 0; i < MAX_PARTICLES; i++) {
particles[i].life = 0;
}

srand(time(NULL));
day_night_toggle_time = time(NULL);
load_high_score();
}

void drawText(float x, float y, const char* text, void* font, Color color) {
glColor3f(color.r, color.g, color.b);
glRasterPos2f(x, y);
while (*text) {
glutBitmapCharacter(font, *text);
text++;
}
}

void start_screen() {
glClear(GL_COLOR_BUFFER_BIT);

// Title
Color titleColor = {0.0f, 0.5f, 0.0f};
drawText(windowWidth * 0.35f, windowHeight * 0.7f, "EGG CATCHING",
GLUT_BITMAP_TIMES_ROMAN_24, titleColor);

titleColor.r = 1.0f; titleColor.g = 0.8f; titleColor.b = 0.0f;


drawText(windowWidth * 0.45f, windowHeight * 0.65f, "DELUXE",
GLUT_BITMAP_TIMES_ROMAN_24, titleColor);

// High score
Color scoreColor = {1.0f, 1.0f, 0.0f};
char highScoreStr[50];
snprintf(highScoreStr, 50, "High Score: %d", high_score);
drawText(windowWidth * 0.4f, windowHeight * 0.55f, highScoreStr,
GLUT_BITMAP_HELVETICA_18, scoreColor);

// Instructions
Color textColor = {1.0f, 1.0f, 1.0f};
drawText(windowWidth * 0.3f, windowHeight * 0.45f, "Press 'S' to Start",
GLUT_BITMAP_HELVETICA_18, textColor);
drawText(windowWidth * 0.3f, windowHeight * 0.4f, "Use Mouse to Move Basket",

9
GLUT_BITMAP_HELVETICA_18, textColor);
drawText(windowWidth * 0.3f, windowHeight * 0.35f, "Press 'P' to Pause",
GLUT_BITMAP_HELVETICA_18, textColor);
drawText(windowWidth * 0.3f, windowHeight * 0.3f, "Press 'Q' to Quit",
GLUT_BITMAP_HELVETICA_18, textColor);
drawText(windowWidth * 0.3f, windowHeight * 0.2f, "Right-click for Menu",
GLUT_BITMAP_HELVETICA_18, textColor);

// Egg legend
drawText(windowWidth * 0.1f, windowHeight * 0.15f, "Egg Types:",
GLUT_BITMAP_HELVETICA_18, textColor);

// Normal egg
Egg demoEgg = {windowWidth * 0.1f + 20, windowHeight * 0.1f, 1,
EGG_NORMAL, 0, {1.0f, 1.0f, 1.0f}};
drawEgg(demoEgg);
drawText(windowWidth * 0.1f + 40, windowHeight * 0.1f, "- Normal (10 pts)",
GLUT_BITMAP_9_BY_15, textColor);

// Golden egg
demoEgg = (Egg){windowWidth * 0.3f + 20, windowHeight * 0.1f, 1,
EGG_GOLDEN, 0, {1.0f, 0.843f, 0.0f}};
drawEgg(demoEgg);
drawText(windowWidth * 0.3f + 40, windowHeight * 0.1f, "- Golden (25 pts)",
GLUT_BITMAP_9_BY_15, textColor);

// Bomb egg
demoEgg = (Egg){windowWidth * 0.5f + 20, windowHeight * 0.1f, 1, EGG_BOMB,
0, {0.5f, 0.0f, 0.0f}};
drawEgg(demoEgg);
drawText(windowWidth * 0.5f + 40, windowHeight * 0.1f, "- Bomb (-1 life)",
GLUT_BITMAP_9_BY_15, textColor);

// Black egg
demoEgg = (Egg){windowWidth * 0.7f + 20, windowHeight * 0.1f, 1, EGG_BLACK,
0, {0.1f, 0.1f, 0.1f}};
drawEgg(demoEgg);
drawText(windowWidth * 0.7f + 40, windowHeight * 0.1f, "- Black (Game Over)",
GLUT_BITMAP_9_BY_15, textColor);

// Rainbow egg
demoEgg = (Egg){windowWidth * 0.9f + 20, windowHeight * 0.1f, 1, EGG_BLACK,
0, {0.1f, 0.1f, 0.1f}};
drawEgg(demoEgg);
drawText(windowWidth * 0.9f + 10, windowHeight * 0.1f, "- Black (Game Over)",
GLUT_BITMAP_9_BY_15, textColor);

glutSwapBuffers();
10
}

void gameOver_screen() {
glClear(GL_COLOR_BUFFER_BIT);

Color red = {1.0f, 0.0f, 0.0f};


drawText(windowWidth * 0.4f, windowHeight * 0.6f, "GAME OVER!",
GLUT_BITMAP_TIMES_ROMAN_24, red);

Color white = {1.0f, 1.0f, 1.0f};


char finalScoreStr[50];
snprintf(finalScoreStr, 50, "Final Score: %d", points);
drawText(windowWidth * 0.4f, windowHeight * 0.5f, finalScoreStr,
GLUT_BITMAP_HELVETICA_18, white);

if (points > high_score) {


Color gold = {1.0f, 0.843f, 0.0f};
drawText(windowWidth * 0.35f, windowHeight * 0.45f, "NEW HIGH SCORE!",
GLUT_BITMAP_HELVETICA_18, gold);
}

char caughtStr[50];
snprintf(caughtStr, 50, "Eggs Caught: %d", eggs_caught);
drawText(windowWidth * 0.4f, windowHeight * 0.4f, caughtStr,
GLUT_BITMAP_HELVETICA_18, white);

char maxComboStr[50];
snprintf(maxComboStr, 50, "Max Combo: %d", max_combo);
drawText(windowWidth * 0.4f, windowHeight * 0.35f, maxComboStr,
GLUT_BITMAP_HELVETICA_18, white);

drawText(windowWidth * 0.35f, windowHeight * 0.25f, "Press 'R' to Restart",


GLUT_BITMAP_HELVETICA_18, white);
drawText(windowWidth * 0.35f, windowHeight * 0.2f, "Press 'Q' to Quit",
GLUT_BITMAP_HELVETICA_18, white);

glutSwapBuffers();
}

void pause_screen() {
// Semi-transparent overlay
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(0.0f, 0.0f, 0.0f, 0.5f);
glBegin(GL_QUADS);
glVertex2f(0, 0);
glVertex2f(windowWidth, 0);
glVertex2f(windowWidth, windowHeight);
11
glVertex2f(0, windowHeight);
glEnd();
glDisable(GL_BLEND);

Color yellow = {1.0f, 1.0f, 0.0f};


drawText(windowWidth/2 - 50, windowHeight/2, "PAUSED",
GLUT_BITMAP_TIMES_ROMAN_24, yellow);

Color white = {1.0f, 1.0f, 1.0f};


drawText(windowWidth/2 - 100, windowHeight/2 - 30, "Press 'P' to Resume",
GLUT_BITMAP_HELVETICA_18, white);
drawText(windowWidth/2 - 80, windowHeight/2 - 60, "Press 'Q' to Quit",
GLUT_BITMAP_HELVETICA_18, white);

glutSwapBuffers();
}

void level_complete_screen() {
glClear(GL_COLOR_BUFFER_BIT);

Color green = {0.0f, 1.0f, 0.0f};


char levelStr[50];
snprintf(levelStr, 50, "LEVEL %d COMPLETE!", level_count);
drawText(windowWidth * 0.35f, windowHeight * 0.6f, levelStr,
GLUT_BITMAP_TIMES_ROMAN_24, green);

Color white = {1.0f, 1.0f, 1.0f};


char scoreStr[50];
snprintf(scoreStr, 50, "Score: %d", points);
drawText(windowWidth * 0.4f, windowHeight * 0.5f, scoreStr,
GLUT_BITMAP_HELVETICA_18, white);

drawText(windowWidth * 0.35f, windowHeight * 0.4f, "Press 'N' for Next Level",


GLUT_BITMAP_HELVETICA_18, white);
drawText(windowWidth * 0.35f, windowHeight * 0.35f, "Press 'Q' to Quit",
GLUT_BITMAP_HELVETICA_18, white);

glutSwapBuffers();
}

void sun() {
float theta;
glColor3f(1.0f, 0.8f, 0.0f);
glBegin(GL_POLYGON);
for (int i = 0; i < 360; i++) {
theta = i * PI / 180.0;
glVertex2f((windowWidth * 0.85f) + 40 * cos(theta),
(windowHeight * 0.9f) + 40 * sin(theta));
12
}
glEnd();

// Sun rays
glColor3f(1.0f, 0.9f, 0.2f);
glBegin(GL_LINES);
for (int i = 0; i < 360; i += 30) {
theta = i * PI / 180.0;
glVertex2f((windowWidth * 0.85f) + 40 * cos(theta),
(windowHeight * 0.9f) + 40 * sin(theta));
glVertex2f((windowWidth * 0.85f) + 60 * cos(theta),
(windowHeight * 0.9f) + 60 * sin(theta));
}
glEnd();
}

void moon() {
float theta;
glColor3f(0.9f, 0.9f, 0.9f);
glBegin(GL_POLYGON);
for (int i = 0; i < 360; i++) {
theta = i * PI / 180.0;
glVertex2f((windowWidth * 0.85f) + 40 * cos(theta),
(windowHeight * 0.9f) + 40 * sin(theta));
}
glEnd();

// Moon craters
glColor3f(0.7f, 0.7f, 0.7f);
glBegin(GL_POLYGON);
for (int i = 0; i < 360; i++) {
theta = i * PI / 180.0;
glVertex2f((windowWidth * 0.85f) + 15 + 10 * cos(theta),
(windowHeight * 0.9f) + 15 + 10 * sin(theta));
}
glEnd();

glBegin(GL_POLYGON);
for (int i = 0; i < 360; i++) {
theta = i * PI / 180.0;
glVertex2f((windowWidth * 0.85f) - 10 + 8 * cos(theta),
(windowHeight * 0.9f) - 10 + 8 * sin(theta));
}
glEnd();

glBegin(GL_POLYGON);
for (int i = 0; i < 360; i++) {

13
theta = i * PI / 180.0;
glVertex2f((windowWidth * 0.85f) + 5 + 5 * cos(theta),
(windowHeight * 0.9f) + 20 + 5 * sin(theta));
}
glEnd();
}

void cloud(float baseX, float baseY, float scale) {


float theta;
glColor3f(1.0f, 1.0f, 1.0f);
glPushMatrix();
glTranslatef(baseX, baseY, 0);
glScalef(scale, scale, 1.0);

int circlePoints = 30;


float radius = 25.0f;

glBegin(GL_POLYGON);
for (int i = 0; i < 360; i += (360/circlePoints)) {
theta = i * PI / 180.0;
glVertex2f(0 + radius * cos(theta), 0 + radius * sin(theta));
}
glEnd();

glBegin(GL_POLYGON);
for (int i = 0; i < 360; i += (360/circlePoints)) {
theta = i * PI / 180.0;
glVertex2f(35 + radius * cos(theta), 0 + radius * sin(theta));
}
glEnd();

glBegin(GL_POLYGON);
for (int i = 0; i < 360; i += (360/circlePoints)) {
theta = i * PI / 180.0;
glVertex2f(10 + radius * cos(theta), 15 + radius * sin(theta));
}
glEnd();

glBegin(GL_POLYGON);
for (int i = 0; i < 360; i += (360/circlePoints)) {
theta = i * PI / 180.0;
glVertex2f(25 + radius * cos(theta), 18 + radius * sin(theta));
}
glEnd();

glPopMatrix();
}

14
void ground() {
glBegin(GL_QUADS);
if (day_mode) {
glColor3f(ground_day.r, ground_day.g, ground_day.b);
} else {
glColor3f(ground_night.r, ground_night.g, ground_night.b);
}
glVertex2f(0.0f, 0.0f);
glVertex2f((float)windowWidth, 0.0f);
glVertex2f((float)windowWidth, 50.0f);
glVertex2f(0.0f, 50.0f);
glEnd();

// Grass details
if (day_mode) {
glColor3f(0.2f, 0.5f, 0.1f);
} else {
glColor3f(0.1f, 0.3f, 0.1f);
}
glBegin(GL_LINES);
for (int i = 0; i < windowWidth; i += 10) {
glVertex2f(i, 50.0f);
glVertex2f(i + 5, 70.0f);
}
glEnd();
}

void drawEgg(Egg egg) {


if (!egg.active) return;

float x, y;
int t;

// Set color based on egg type


switch(egg.type) {
case EGG_NORMAL:
egg.color = (Color){1.0f, 1.0f, 1.0f};
break;
case EGG_GOLDEN:
egg.color = (Color){1.0f, 0.843f, 0.0f};
break;
case EGG_BOMB:
egg.color = (Color){0.5f, 0.0f, 0.0f};
break;
case EGG_BLACK:
egg.color = (Color){0.1f, 0.1f, 0.1f};

15
break;
case EGG_RAINBOW:
// Rainbow effect - color changes over time
egg.color.r = 0.5f + 0.5f * sin(glutGet(GLUT_ELAPSED_TIME) / 1000.0f);
egg.color.g = 0.5f + 0.5f * sin(glutGet(GLUT_ELAPSED_TIME) / 1000.0f + 2.0f);
egg.color.b = 0.5f + 0.5f * sin(glutGet(GLUT_ELAPSED_TIME) / 1000.0f + 4.0f);
break;
}

glColor3f(egg.color.r, egg.color.g, egg.color.b);


glBegin(GL_POLYGON);
for (t = 0; t <= 360; t += 5) {
x = egg.x + 8 * cos(t * PI / 180.0);
y = egg.y + 12 * sin(t * PI / 180.0);
glVertex2f(x, y);
}
glEnd();

// Add pattern to normal eggs


if (egg.type == EGG_NORMAL) {
glColor3f(0.8f, 0.8f, 0.8f);
glBegin(GL_POLYGON);
for (t = 0; t <= 360; t += 5) {
x = egg.x + 4 * cos(t * PI / 180.0);
y = egg.y + 8 * sin(t * PI / 180.0);
glVertex2f(x, y);
}
glEnd();
}

// Add fuse to bomb eggs


if (egg.type == EGG_BOMB) {
glColor3f(0.3f, 0.3f, 0.3f);
glLineWidth(2.0f);
glBegin(GL_LINE_STRIP);
glVertex2f(egg.x + 5, egg.y + 12);
glVertex2f(egg.x + 10, egg.y + 18);
glVertex2f(egg.x + 15, egg.y + 15);
glEnd();
glLineWidth(1.0f);

// Fuse spark
glColor3f(1.0f, 0.5f, 0.0f);
glPointSize(3.0f);
glBegin(GL_POINTS);
glVertex2f(egg.x + 15, egg.y + 15);
glEnd();

16
glPointSize(1.0f);
}
}

void drawBasket(float x, float y) {


if (x < 0) x = 0;
if (x > windowWidth - basket_width) x = windowWidth - basket_width;

// Apply bounce effect


float bounce_offset = basket_bounce * 5.0f;

// Basket body
glColor3f(0.6f, 0.3f, 0.1f);
glBegin(GL_QUADS);
glVertex2f(x, y + bounce_offset);
glVertex2f(x + basket_width, y + bounce_offset);
glVertex2f(x + basket_width, y + basket_height + bounce_offset);
glVertex2f(x, y + basket_height + bounce_offset);
glEnd();

// Basket weave pattern


glColor3f(0.4f, 0.2f, 0.05f);
glLineWidth(1.0);
for (int i = 0; i < basket_width; i += 10) {
glBegin(GL_LINES);
glVertex2f(x + i, y + bounce_offset);
glVertex2f(x + i, y + basket_height + bounce_offset);
glEnd();
}
for (int i = 0; i < basket_height; i += 5) {
glBegin(GL_LINES);
glVertex2f(x, y + i + bounce_offset);
glVertex2f(x + basket_width, y + i + bounce_offset);
glEnd();
}

// Rim
glColor3f(0.4f, 0.2f, 0.05f);
glLineWidth(2.0);
glBegin(GL_LINE_LOOP);
glVertex2f(x, y + basket_height + bounce_offset);
glVertex2f(x + basket_width, y + basket_height + bounce_offset);
glVertex2f(x + basket_width - 5, y + basket_height - 5 + bounce_offset);
glVertex2f(x + 5, y + basket_height - 5 + bounce_offset);
glEnd();
glLineWidth(1.0);

17
// Handle
glColor3f(0.5f, 0.25f, 0.1f);
glLineWidth(3.0);
glBegin(GL_LINE_STRIP);
glVertex2f(x + 10, y + basket_height + bounce_offset);
glVertex2f(x + basket_width/2, y + basket_height + 15 + bounce_offset);
glVertex2f(x + basket_width - 10, y + basket_height + bounce_offset);
glEnd();
glLineWidth(1.0);

// Draw invincibility effect


if (invincible) {
float time = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
float pulse = 0.5f + 0.5f * sin(time * 5.0f);

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(0.0f, 0.5f, 1.0f, 0.3f * pulse);
glBegin(GL_POLYGON);
for (int t = 0; t <= 360; t += 30) {
float rad = t * PI / 180.0f;
float offset_x = (basket_width/2 + 10) * cos(rad);
float offset_y = (basket_height + 10) * sin(rad);
glVertex2f(x + basket_width/2 + offset_x, y + basket_height/2 + offset_y +
bounce_offset);
}
glEnd();
glDisable(GL_BLEND);
}
}

void drawHen(float x, float y, int frame, float wingState) {


// Body
glColor3f(0.9f, 0.9f, 0.9f);
glBegin(GL_POLYGON);
glVertex2f(x - 20, y + 0);
glVertex2f(x - 10, y + 15);
glVertex2f(x + 5, y + 25);
glVertex2f(x + 15, y + 20);
glVertex2f(x + 20, y + 10);
glVertex2f(x + 10, y + 0);
glVertex2f(x + 0, y - 10);
glVertex2f(x - 15, y - 5);
glEnd();

// Wing animation
float wingAngle = 30.0f * sin(wingState * 2.0f * PI);

18
glPushMatrix();
glTranslatef(x - 10, y + 5, 0);
glRotatef(wingAngle, 0, 0, 1);

glColor3f(0.8f, 0.8f, 0.8f);


glBegin(GL_POLYGON);
glVertex2f(0, 0);
glVertex2f(-10, 5);
glVertex2f(-5, -5);
glEnd();

glPopMatrix();

// Beak
glColor3f(1.0f, 0.6f, 0.0f);
glBegin(GL_TRIANGLES);
glVertex2f(x + 18, y + 12);
glVertex2f(x + 25, y + 8);
glVertex2f(x + 16, y + 6);
glEnd();

// Eye
glColor3f(0.0f, 0.0f, 0.0f);
glPointSize(3.0f);
glBegin(GL_POINTS);
glVertex2f(x + 12, y + 15);
glEnd();
glPointSize(1.0f);

// Comb
glColor3f(1.0f, 0.0f, 0.0f);
glBegin(GL_POLYGON);
glVertex2f(x + 10, y + 25);
glVertex2f(x + 15, y + 30);
glVertex2f(x + 10, y + 28);
glVertex2f(x + 5, y + 30);
glVertex2f(x + 0, y + 25);
glEnd();
}

void drawPowerUp(PowerUp pu) {


if (!pu.active) return;

float x, y;
int t;

// Set color based on power-up type

19
switch(pu.type) {
case POWERUP_EXPAND:
glColor3f(0.0f, 0.0f, 1.0f);
break;
case POWERUP_SLOW:
glColor3f(0.0f, 1.0f, 0.0f);
break;
case POWERUP_EXTRA_LIFE:
glColor3f(1.0f, 0.0f, 0.0f);
break;
case POWERUP_INVINCIBLE:
// Pulse effect for invincibility power-up
float pulse = 0.5f + 0.5f * sin(glutGet(GLUT_ELAPSED_TIME) / 200.0f);
glColor3f(0.0f, pulse, 1.0f);
break;
}

// Draw star shape


glBegin(GL_POLYGON);
for (t = 0; t < 360; t += 72) {
// Outer point
x = pu.x + 10 * cos(t * PI / 180.0);
y = pu.y + 10 * sin(t * PI / 180.0);
glVertex2f(x, y);

// Inner point
x = pu.x + 5 * cos((t + 36) * PI / 180.0);
y = pu.y + 5 * sin((t + 36) * PI / 180.0);
glVertex2f(x, y);
}
glEnd();

// Add symbol inside


glColor3f(1.0f, 1.0f, 1.0f);
switch(pu.type) {
case POWERUP_EXPAND: // Expand (plus)
glBegin(GL_LINES);
glVertex2f(pu.x - 5, pu.y);
glVertex2f(pu.x + 5, pu.y);
glVertex2f(pu.x, pu.y - 5);
glVertex2f(pu.x, pu.y + 5);
glEnd();
break;
case POWERUP_SLOW: // Slow (minus)
glBegin(GL_LINES);
glVertex2f(pu.x - 5, pu.y);
glVertex2f(pu.x + 5, pu.y);

20
glEnd();
break;
case POWERUP_EXTRA_LIFE: // Extra life (heart)
glBegin(GL_POLYGON);
glVertex2f(pu.x, pu.y + 3);
glVertex2f(pu.x - 3, pu.y);
glVertex2f(pu.x, pu.y - 3);
glVertex2f(pu.x + 3, pu.y);
glEnd();
break;
case POWERUP_INVINCIBLE: // Invincible (shield)
glBegin(GL_LINE_LOOP);
glVertex2f(pu.x, pu.y + 5);
glVertex2f(pu.x - 5, pu.y);
glVertex2f(pu.x, pu.y - 5);
glVertex2f(pu.x + 5, pu.y);
glEnd();
break;
}
}

void drawParticles() {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

for (int i = 0; i < MAX_PARTICLES; i++) {


if (particles[i].life > 0) {
float alpha = particles[i].life / 100.0f;
glColor4f(particles[i].color.r, particles[i].color.g, particles[i].color.b, alpha);
glPointSize(3.0f);
glBegin(GL_POINTS);
glVertex2f(particles[i].x, particles[i].y);
glEnd();
glPointSize(1.0f);
}
}

glDisable(GL_BLEND);
}

void addParticle(float x, float y, Color color) {


for (int i = 0; i < MAX_PARTICLES; i++) {
if (particles[i].life <= 0) {
particles[i].x = x;
particles[i].y = y;
particles[i].vx = (rand() % 100 - 50) / 50.0f;
particles[i].vy = (rand() % 100) / 50.0f;

21
particles[i].life = 100;
particles[i].color = color;
return;
}
}
}

void updateParticles(float delta) {


for (int i = 0; i < MAX_PARTICLES; i++) {
if (particles[i].life > 0) {
particles[i].x += particles[i].vx * delta * 60.0f;
particles[i].y += particles[i].vy * delta * 60.0f;
particles[i].vy -= 0.1f * delta * 60.0f; // Gravity
particles[i].life -= 1.0f * delta * 60.0f;
}
}
}

void drawProgressBar(float x, float y, float width, float height, float progress, Color
color) {
// Background
glColor3f(0.3f, 0.3f, 0.3f);
glBegin(GL_QUADS);
glVertex2f(x, y);
glVertex2f(x + width, y);
glVertex2f(x + width, y + height);
glVertex2f(x, y + height);
glEnd();

// Progress
glColor3f(color.r, color.g, color.b);
glBegin(GL_QUADS);
glVertex2f(x, y);
glVertex2f(x + width * progress, y);
glVertex2f(x + width * progress, y + height);
glVertex2f(x, y + height);
glEnd();

// Border
glColor3f(0.0f, 0.0f, 0.0f);
glLineWidth(1.0f);
glBegin(GL_LINE_LOOP);
glVertex2f(x, y);
glVertex2f(x + width, y);
glVertex2f(x + width, y + height);
glVertex2f(x, y + height);
glEnd();

22
glLineWidth(1.0f);
}

void drawComboMeter(float x, float y, float width, float height, int combo) {


if (combo < 2) return;

float comboProgress = (combo > 10) ? 1.0f : combo / 10.0f;


Color comboColor;

if (combo < 5) {
comboColor.r = 0.0f; comboColor.g = 1.0f; comboColor.b = 0.0f; // Green
} else if (combo < 8) {
comboColor.r = 1.0f; comboColor.g = 1.0f; comboColor.b = 0.0f; // Yellow
} else {
comboColor.r = 1.0f; comboColor.g = 0.0f; comboColor.b = 0.0f; // Red
}

// Meter background
glColor3f(0.2f, 0.2f, 0.2f);
glBegin(GL_QUADS);
glVertex2f(x, y);
glVertex2f(x + width, y);
glVertex2f(x + width, y + height);
glVertex2f(x, y + height);
glEnd();

// Combo level
glColor3f(comboColor.r, comboColor.g, comboColor.b);
glBegin(GL_QUADS);
glVertex2f(x, y);
glVertex2f(x + width * comboProgress, y);
glVertex2f(x + width * comboProgress, y + height);
glVertex2f(x, y + height);
glEnd();

// Combo text
char comboText[20];
snprintf(comboText, 20, "COMBO x%d", combo);
Color textColor = {1.0f, 1.0f, 1.0f};
drawText(x + width/2 - 30, y + height/2 - 5, comboText,
GLUT_BITMAP_HELVETICA_10, textColor);

// Border
glColor3f(0.0f, 0.0f, 0.0f);
glLineWidth(1.0f);
glBegin(GL_LINE_LOOP);
glVertex2f(x, y);

23
glVertex2f(x + width, y);
glVertex2f(x + width, y + height);
glVertex2f(x, y + height);
glEnd();
glLineWidth(1.0f);
}

void print_score_to_console() {
printf("\n----- GAME STATS -----\n");
printf("Level: %d\n", level_count);
printf("Eggs Caught: %d\n", eggs_caught);
printf("Eggs Missed: %d\n", missed_eggs);
printf("Lives: %d\n", lives);
printf("Max Combo: %d\n", max_combo);
printf("Total Score: %d\n", points);
printf("----------------------\n");
}

void resetEgg() {
// Only reset if no egg is currently active
if (eggs[0].active) return;

// Check if it's time to drop a new egg


time_t now = glutGet(GLUT_ELAPSED_TIME);
if (now - last_egg_time < egg_fall_delay) return;

eggs[0].active = 1;
eggs[0].y = windowHeight - 150;

// Choose random hen position


int hen_index = rand() % HEN_COUNT;
eggs[0].x = hen_positions[hen_index];

// Random egg type


// Normal: 60%, Golden: 15%, Bomb: 10%, Black: 5%, Rainbow: 10%
int r = rand() % 100;
if (r < 60) {
eggs[0].type = EGG_NORMAL;
} else if (r < 75) {
eggs[0].type = EGG_GOLDEN;
} else if (r < 85) {
eggs[0].type = EGG_BOMB;
} else if (r < 90) {
eggs[0].type = EGG_BLACK;
} else {
eggs[0].type = EGG_RAINBOW;
}

24
eggs[0].speed = current_egg_speed * (0.9f + (rand() % 20) / 100.0f); // Slight speed
variation

dropped_eggs++;
last_egg_time = now;
}

void spawnPowerUp(float x, float y) {


// Find inactive power-up
for (int i = 0; i < MAX_POWERUPS; i++) {
if (!powerups[i].active) {
powerups[i].active = 1;
powerups[i].x = x;
powerups[i].y = y;

// Weighted random power-up type


int r = rand() % 100;
if (r < 40) {
powerups[i].type = POWERUP_EXPAND;
} else if (r < 70) {
powerups[i].type = POWERUP_SLOW;
} else if (r < 90) {
powerups[i].type = POWERUP_EXTRA_LIFE;
} else {
powerups[i].type = POWERUP_INVINCIBLE;
}

powerups[i].speed = 2.0f;
powerups[i].spawn_time = glutGet(GLUT_ELAPSED_TIME);
return;
}
}
}

void toggle_day_night() {
time_t now = time(NULL);
if (now - day_night_toggle_time > 30) { // Toggle every 30 seconds
day_mode = !day_mode;
day_night_toggle_time = now;
if (day_mode) {
glClearColor(background_day.r, background_day.g, background_day.b, 1.0);
} else {
glClearColor(background_night.r, background_night.g, background_night.b, 1.0);
}
}
}

25
void load_high_score() {
FILE *file = fopen("highscore.dat", "rb");
if (file) {
fread(&high_score, sizeof(int), 1, file);
fclose(file);
}
}

void save_high_score() {
FILE *file = fopen("highscore.dat", "wb");
if (file) {
fwrite(&high_score, sizeof(int), 1, file);
fclose(file);
}
}

void updateGame(float delta) {


if (currentGameState != PLAYING) return;

// Toggle day/night mode periodically


toggle_day_night();

// Update basket bounce effect


if (basket_bounce > 0) {
basket_bounce -= delta * 10.0f;
if (basket_bounce < 0) basket_bounce = 0;
}

// Update hens
for (int i = 0; i < HEN_COUNT; i++) {
hen_positions[i] += hen_velocities[i] * game_speed * delta * 60.0f;
hen_animation_frame[i] = (hen_animation_frame[i] + 1) % 20;
hen_wings[i] += delta * 2.0f;

if (hen_positions[i] <= 50 || hen_positions[i] >= windowWidth - 50) {


hen_velocities[i] *= -1;
}
}

// Update egg
if (eggs[0].active) {
eggs[0].y -= eggs[0].speed * game_speed * delta * 60.0f;

// Check for catch


if (eggs[0].y - 12 <= basket_y + basket_height &&
eggs[0].y - 12 > basket_y &&

26
eggs[0].x >= basket_x &&
eggs[0].x <= basket_x + basket_width) {

// Handle different egg types


if (eggs[0].type == EGG_BOMB && !invincible) { // Bomb
lives--;
sound_effect(3); // Bad sound
combo = 0;

// Explosion particles
for (int i = 0; i < 20; i++) {
Color red = {1.0f, 0.0f, 0.0f};
addParticle(eggs[0].x, eggs[0].y, red);
}

if (lives <= 0) {
currentGameState = GAME_OVER;
if (points > high_score) {
high_score = points;
save_high_score();
}
print_score_to_console();
return;
}
} else if (eggs[0].type == EGG_BLACK && !invincible) { // Black egg
currentGameState = GAME_OVER;
if (points > high_score) {
high_score = points;
save_high_score();
}
print_score_to_console();
return;
} else {
// Normal, golden or rainbow egg
int points_gained = 10;
Color particleColor = {1.0f, 1.0f, 1.0f};

if (eggs[0].type == EGG_GOLDEN) {
points_gained = 25;
particleColor.r = 1.0f; particleColor.g = 0.843f; particleColor.b = 0.0f;
} else if (eggs[0].type == EGG_RAINBOW) {
points_gained = 50;
particleColor.r = 1.0f; particleColor.g = 0.0f; particleColor.b = 1.0f;
}

// Add particles for catch effect


for (int i = 0; i < 10; i++) {

27
addParticle(eggs[0].x, eggs[0].y, particleColor);
}

points += points_gained;

// Combo system
time_t now = time(NULL);
if (now - last_combo_time <= 2) { // 2 seconds for combo
combo++;
if (combo > max_combo) max_combo = combo;
points_gained += combo * 2; // Bonus points for combo

// After 3 consecutive catches, chance for special effects


if (combo >= 3 && rand() % 3 == 0) { // 1 in 3 chance
if (rand() % 2 == 0) {
eggs[0].type = EGG_GOLDEN; // Make it golden
} else {
game_speed = 0.5f; // Slow down
slow_end_time = time(NULL) + 5; // For 5 seconds
}
}
} else {
combo = 1;
}
last_combo_time = now;

sound_effect(1); // Good sound


basket_bounce = 1.0f; // Bounce effect

// Chance to spawn power-up


if (rand() % 8 == 0) { // 12.5% chance
spawnPowerUp(eggs[0].x, eggs[0].y);
}
}

eggs[0].active = 0;
eggs_caught++;

// Check for level up


if (eggs_caught % eggs_per_level == 0 &&
level_count < max_level) {
level_count++;
current_egg_speed = speed_levels[level_count - 1];
egg_fall_delay = 2000 - (level_count * 300); // Reduce delay between eggs as
level increases
if (egg_fall_delay < 500) egg_fall_delay = 500; // Minimum delay
currentGameState = LEVEL_COMPLETE;

28
return;
}
}

// Check for miss


if (eggs[0].y < 0) {
eggs[0].active = 0;
if (eggs[0].type != EGG_BLACK) { // Don't count black eggs as missed
missed_eggs++;
}
combo = 0;
sound_effect(2); // Bad sound

if (missed_eggs >= max_missed_eggs) {


currentGameState = GAME_OVER;
if (points > high_score) {
high_score = points;
save_high_score();
}
print_score_to_console();
return;
}
}
} else {
resetEgg();
}

// Update power-ups
for (int i = 0; i < MAX_POWERUPS; i++) {
if (powerups[i].active) {
powerups[i].y -= powerups[i].speed * game_speed * delta * 60.0f;

// Check for collection


if (powerups[i].y - 10 <= basket_y + basket_height &&
powerups[i].y - 10 > basket_y &&
powerups[i].x >= basket_x - 10 &&
powerups[i].x <= basket_x + basket_width + 10) {

sound_effect(4); // Power-up sound

// Add collection particles


Color powerupColor;
switch(powerups[i].type) {
case POWERUP_EXPAND: powerupColor = (Color){0.0f, 0.0f, 1.0f};
break;
case POWERUP_SLOW: powerupColor = (Color){0.0f, 1.0f, 0.0f}; break;
case POWERUP_EXTRA_LIFE: powerupColor = (Color){1.0f, 0.0f, 0.0f};

29
break;
case POWERUP_INVINCIBLE: powerupColor = (Color){0.0f, 0.5f, 1.0f};
break;
}

for (int j = 0; j < 15; j++) {


addParticle(powerups[i].x, powerups[i].y, powerupColor);
}

// Apply power-up effect


switch(powerups[i].type) {
case POWERUP_EXPAND: // Expand
basket_width = 90;
basket_expanded = 1;
expand_end_time = time(NULL) + 10; // 10 seconds
break;
case POWERUP_SLOW: // Slow
game_speed = 0.5f;
slow_end_time = time(NULL) + 8; // 8 seconds
break;
case POWERUP_EXTRA_LIFE: // Extra life
lives++;
if (lives > 5) lives = 5; // Cap at 5 lives
break;
case POWERUP_INVINCIBLE: // Invincible
invincible = 1;
invincible_end_time = time(NULL) + 15; // 15 seconds
break;
}

powerups[i].active = 0;
}

// Check if fell off screen


if (powerups[i].y < 0) {
powerups[i].active = 0;
}

// Check if power-up has been around too long (10 seconds)


if (glutGet(GLUT_ELAPSED_TIME) - powerups[i].spawn_time > 10000) {
powerups[i].active = 0;
}
}
}

// Update particles
updateParticles(delta);

30
// Check if power-up effects should end
time_t now = time(NULL);
if (basket_expanded && now >= expand_end_time) {
basket_width = 60;
basket_expanded = 0;
}
if (game_speed < 1.0f && now >= slow_end_time) {
game_speed = 1.0f;
}
if (invincible && now >= invincible_end_time) {
invincible = 0;
}
}

void display(void) {
glClear(GL_COLOR_BUFFER_BIT);

switch (currentGameState) {
case MENU:
start_screen();
return;
case GAME_OVER:
gameOver_screen();
return;
case PAUSED:
// Draw game first, then overlay pause screen
break;
case LEVEL_COMPLETE:
level_complete_screen();
return;
case PLAYING:
// Continue to draw game
break;
}

// Draw background elements


ground();
if (day_mode) {
sun();
} else {
moon();
}
cloud(windowWidth * 0.1f, windowHeight * 0.85f, 1);
cloud(windowWidth * 0.6f, windowHeight * 0.75f, 1.2);

31
// Draw hens on their line
for (int i = 0; i < HEN_COUNT; i++) {
drawHen(hen_positions[i] - 40, windowHeight - 120, hen_animation_frame[i],
hen_wings[i]);
}

// Draw egg
drawEgg(eggs[0]);

// Draw power-ups
for (int i = 0; i < MAX_POWERUPS; i++) {
drawPowerUp(powerups[i]);
}

// Draw particles
drawParticles();

// Draw basket
drawBasket(basket_x, basket_y);

// Draw UI
Color textColor;
if (day_mode) {
textColor.r = 0.0f; textColor.g = 0.0f; textColor.b = 0.0f;
} else {
textColor.r = 1.0f; textColor.g = 1.0f; textColor.b = 1.0f;
}

char scoreStr[50];
snprintf(scoreStr, 50, "Score: %d", points);
drawText(10, windowHeight - 20, scoreStr, GLUT_BITMAP_HELVETICA_18,
textColor);

char levelStr[50];
snprintf(levelStr, 50, "Level: %d", level_count);
drawText(10, windowHeight - 40, levelStr, GLUT_BITMAP_HELVETICA_18,
textColor);

char missedStr[50];
snprintf(missedStr, 50, "Missed: %d/%d", missed_eggs, max_missed_eggs);
drawText(windowWidth - 150, windowHeight - 20, missedStr,
GLUT_BITMAP_HELVETICA_18, textColor);

char livesStr[50];
snprintf(livesStr, 50, "Lives: %d", lives);
drawText(windowWidth - 150, windowHeight - 40, livesStr,

32
GLUT_BITMAP_HELVETICA_18, textColor);

// Draw combo meter


drawComboMeter(windowWidth/2 - 50, windowHeight - 40, 100, 20, combo);

// Draw active power-up timers


time_t now = time(NULL);
if (basket_expanded) {
float remaining = expand_end_time - now;
if (remaining > 0) {
float progress = remaining / 10.0f;
Color blue = {0.0f, 0.0f, 1.0f};
drawProgressBar(10, windowHeight - 70, 100, 10, progress, blue);
drawText(10, windowHeight - 60, "Expand", GLUT_BITMAP_9_BY_15,
textColor);
}
}

if (game_speed < 1.0f) {


float remaining = slow_end_time - now;
if (remaining > 0) {
float progress = remaining / 8.0f;
Color green = {0.0f, 1.0f, 0.0f};
drawProgressBar(120, windowHeight - 70, 100, 10, progress, green);
drawText(120, windowHeight - 60, "Slow", GLUT_BITMAP_9_BY_15,
textColor);
}
}

if (invincible) {
float remaining = invincible_end_time - now;
if (remaining > 0) {
float progress = remaining / 15.0f;
Color cyan = {0.0f, 0.5f, 1.0f};
drawProgressBar(230, windowHeight - 70, 100, 10, progress, cyan);
drawText(230, windowHeight - 60, "Invincible", GLUT_BITMAP_9_BY_15,
textColor);
}
}

if (currentGameState == PAUSED) {
pause_screen();
} else {
glutSwapBuffers();
}
}

33
void basket_set(int x, int y) {
basket_x = x - (basket_width / 2);
}

void myReshape(int w, int h) {


windowWidth = w;
windowHeight = h;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// Adjust basket position if window is resized


if (basket_x > w - basket_width) {
basket_x = w - basket_width;
}
}

void keys(unsigned char key, int x, int y) {


switch (key) {
case 'q':
case 'Q':
if (currentGameState != GAME_OVER) {
printf("\n\n\n\t\tQUIT BY PLAYER\n\n");
print_score_to_console();
}
exit(0);
break;
case 's':
case 'S':
if (currentGameState == MENU || currentGameState == GAME_OVER) {
// Reset game state
currentGameState = PLAYING;
eggs_caught = 0;
missed_eggs = 0;
level_count = 1;
points = 0;
dropped_eggs = 0;
lives = 3;
combo = 0;
max_combo = 0;
current_egg_speed = speed_levels[0];
game_speed = 1.0f;
basket_width = 60;
basket_expanded = 0;

34
basket_bounce = 0;
egg_fall_delay = 2000;
day_mode = 1;
day_night_toggle_time = time(NULL);
invincible = 0;

// Reset egg
eggs[0].active = 0;
eggs[0].color = (Color){1.0f, 1.0f, 1.0f};

// Reset power-ups
for (int i = 0; i < MAX_POWERUPS; i++) {
powerups[i].active = 0;
}

// Reset particles
for (int i = 0; i < MAX_PARTICLES; i++) {
particles[i].life = 0;
}

basket_x = windowWidth / 2 - basket_width / 2;


printf("Game Started!\n");
}
break;
case 'p':
case 'P':
if (currentGameState == PLAYING) {
currentGameState = PAUSED;
printf("Game Paused.\n");
} else if (currentGameState == PAUSED) {
currentGameState = PLAYING;
printf("Game Resumed.\n");
}
break;
case 'r':
case 'R':
if (currentGameState == GAME_OVER) {
keys('s', 0, 0); // Simulate start
}
break;
case 'n':
case 'N':
if (currentGameState == LEVEL_COMPLETE) {
currentGameState = PLAYING;
printf("Starting Level %d\n", level_count);
}
break;

35
case 'd':
case 'D':
// For testing: toggle day/night mode
day_mode = !day_mode;
if (day_mode) {
glClearColor(background_day.r, background_day.g, background_day.b, 1.0);
} else {
glClearColor(background_night.r, background_night.g, background_night.b,
1.0);
}
break;
}
}

void specialKeys(int key, int x, int y) {


// Arrow keys can be used as alternative controls
switch (key) {
case GLUT_KEY_LEFT:
basket_x -= 20;
if (basket_x < 0) basket_x = 0;
break;
case GLUT_KEY_RIGHT:
basket_x += 20;
if (basket_x > windowWidth - basket_width) basket_x = windowWidth -
basket_width;
break;
}
}

void menu(int id) {


switch (id) {
case 1: // Start Game
keys('s', 0, 0);
break;
case 2: // Score
print_score_to_console();
break;
case 3: // Quit
keys('q', 0, 0);
break;
}
}

void idleFunc() {
static int last_time = 0;
int current_time = glutGet(GLUT_ELAPSED_TIME);
float delta = (current_time - last_time) / 1000.0f;

36
last_time = current_time;

if (currentGameState == PLAYING) {
updateGame(delta);
}
glutPostRedisplay();
}

void timerFunc(int value) {


glutPostRedisplay();
glutTimerFunc(16, timerFunc, 0); // ~60 FPS
}

int main(int argc, char** argv) {


printf("\n\t\t\t\t EGG CATCHING GAME - DELUXE\n\n");
printf("\n\tHow to Play..?\n\n <1> The objective of the game is to catch the falling
eggs in the basket.\n");
printf("\t To move the Basket use the Mouse cursor's horizontal position.\n");
printf("\n <2> To Start, press key 'S' or use the Right-Click menu.\n");
printf("\n <3> To Pause/Resume during play, press key 'P'.\n");
printf("\n <4> To Quit, press key 'Q' or use the Right-Click menu.\n");
printf("\n <5> Arrow keys can also be used to move the basket.\n");
printf("\n\n RULES: \n");
printf("- You have 3 lives. Lose them all and it's game over!\n");
printf("- Catch 5 eggs to advance to the next level (speed increases).\n");
printf("- Golden eggs are worth more points!\n");
printf("- Rainbow eggs are worth even more!\n");
printf("- Bomb eggs cost you a life (unless invincible)!\n");
printf("- Black eggs end the game immediately (unless invincible)!\n");
printf("- Catch eggs quickly in succession for combo bonuses!\n");
printf("- Collect power-ups for special abilities!\n");
printf("- Day and night will alternate every 30 seconds.\n");
printf("\n\nBEST OF LUCK\n");

glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(windowWidth, windowHeight);
glutInitWindowPosition(100, 100);
glutCreateWindow("EGG CATCHING GAME - DELUXE");

myinit();

// Register Callbacks
glutDisplayFunc(display);
glutReshapeFunc(myReshape);
glutKeyboardFunc(keys);
glutSpecialFunc(specialKeys);

37
glutPassiveMotionFunc(basket_set);
glutMotionFunc(basket_set); // For when mouse button is pressed
glutIdleFunc(idleFunc);
glutTimerFunc(0, timerFunc, 0);

// Create Menu
glutCreateMenu(menu);
glutAddMenuEntry("Start Game", 1);
glutAddMenuEntry("Show Score (Console)", 2);
glutAddMenuEntry("Quit", 3);
glutAttachMenu(GLUT_RIGHT_BUTTON);

glutMainLoop();
return 0;
}

38
();

SCREENSHOTS

39
40
References

1.​ https://youtu.be/yCyX8JoXbmc?si=uwgdInIVJGQ8q-_n
2.​ https://youtu.be/HsfrKDjYUiw?si=1pklOYZNgHiyQepF

41

You might also like