CG Project Egg Catching
CG Project Egg Catching
Project Name
Egg Catching Game
Branch
B.E. 3rd Year – COE
Submitted By –
Kunal Ratra (102203505)
Anahit Sandhu (102203625)
Sudhanshu Dhawan (102203978)
Submitted To –
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.
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
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.
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
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.
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
// 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;
// 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);
// 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);
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);
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);
glutSwapBuffers();
}
void level_complete_screen() {
glClear(GL_COLOR_BUFFER_BIT);
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();
}
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();
}
float x, y;
int t;
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;
}
// 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);
}
}
// 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();
// 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);
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);
}
}
// Wing animation
float wingAngle = 30.0f * sin(wingState * 2.0f * PI);
18
glPushMatrix();
glTranslatef(x - 10, y + 5, 0);
glRotatef(wingAngle, 0, 0, 1);
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();
}
float x, y;
int t;
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;
}
// 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();
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);
glDisable(GL_BLEND);
}
21
particles[i].life = 100;
particles[i].color = color;
return;
}
}
}
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);
}
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;
eggs[0].active = 1;
eggs[0].y = windowHeight - 150;
24
eggs[0].speed = current_egg_speed * (0.9f + (rand() % 20) / 100.0f); // Slight speed
variation
dropped_eggs++;
last_egg_time = now;
}
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);
}
}
// 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;
// Update egg
if (eggs[0].active) {
eggs[0].y -= eggs[0].speed * game_speed * delta * 60.0f;
26
eggs[0].x >= basket_x &&
eggs[0].x <= basket_x + basket_width) {
// 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;
}
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
eggs[0].active = 0;
eggs_caught++;
28
return;
}
}
// 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;
29
break;
case POWERUP_INVINCIBLE: powerupColor = (Color){0.0f, 0.5f, 1.0f};
break;
}
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;
}
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);
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);
}
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;
}
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 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();
}
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