diff --git a/.gitignore b/.gitignore index 34b43b3..66c7095 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ build/ star-invaders *.kra *.xcf -*.png \ No newline at end of file +*.png +*.*~ \ No newline at end of file diff --git a/Makefile b/Makefile index 8118678..31a8d6d 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ # Simple Makefile using sdl-config for SDL flags -CC := gcc +CC := clang SDL_CFLAGS := $(shell sdl2-config --cflags) SDL_LIBS := $(shell sdl2-config --libs) -CFLAGS := -Wall -Wextra -std=c99 $(SDL_CFLAGS) +CFLAGS := -Wall -Wextra -std=c99 -g $(SDL_CFLAGS) SRCDIR := src SOURCES := $(wildcard $(SRCDIR)/*.c) BUILD_DIR := build diff --git a/res/bullet.bmp b/res/bullet.bmp new file mode 100644 index 0000000..d312f73 Binary files /dev/null and b/res/bullet.bmp differ diff --git a/src/bullet.c b/src/bullet.c index 98049e5..8b9959c 100644 --- a/src/bullet.c +++ b/src/bullet.c @@ -1,9 +1,21 @@ #include "bullet.h" +#include "game.h" +#include +#include +#include + +void initBullet(Bullet *bullet, int x, int y) { + bullet->rect.x = x; + bullet->rect.y = y; + bullet->rect.w = 50; // Bullet width + bullet->rect.h = 50; // Bullet height + bullet->active = 0; +} void updateBullets(Bullet *bullets, int *bulletCount, SDL_Renderer* renderer) { int bulletSpeed = 7; - for (int i = 0; i < *bulletCount; i++) { + for (int i = 0; i < MAX_BULLETS; i++) { if (bullets[i].active) { // Move bullet upward bullets[i].rect.y -= bulletSpeed; @@ -11,32 +23,31 @@ void updateBullets(Bullet *bullets, int *bulletCount, SDL_Renderer* renderer) { // Deactivate if bullet goes off screen if (bullets[i].rect.y < 0) { bullets[i].active = 0; + (*bulletCount)--; } else { - // Draw bullet - SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255); - SDL_RenderFillRect(renderer, &bullets[i].rect); + // Draw bullet using global textures + SDL_RenderCopy(renderer, textures.bulletTexture, NULL, &bullets[i].rect); } } } } void fireBullet(Bullet *bullets, int *bulletCount, SDL_Rect* rect) { - if (*bulletCount < MAX_BULLETS) { - bullets[*bulletCount].rect.x = rect->x + rect->w / 2 - 2; - bullets[*bulletCount].rect.y = rect->y - 10; - bullets[*bulletCount].rect.w = 4; - bullets[*bulletCount].rect.h = 10; - bullets[*bulletCount].active = 1; - (*bulletCount)++; + for (int i = 0; i < MAX_BULLETS; i++) { + if (!bullets[i].active) { + bullets[i].rect.x = rect->x; + bullets[i].rect.y = rect->y; + bullets[i].active = 1; + (*bulletCount)++; + break; + } } } -void cleanupUnrenderedBullets(Bullet* bullets, int* bulletCount) { - for (int i = 0; i < *bulletCount; i++){ - if(bullets[i].active == 0){ - memmove(&bullets[i], &bullets[i + 1], sizeof(Bullet) * (*bulletCount - i - 1)); - (*bulletCount)--; - i--; - } +void cleanupBullets(Bullet *bullets, int *bulletCount) { + // Deactivate all bullets and reset count + for (int i = 0; i < MAX_BULLETS; i++) { + bullets[i].active = 0; } + *bulletCount = 0; } \ No newline at end of file diff --git a/src/bullet.h b/src/bullet.h index ea287b7..fb1e0e6 100644 --- a/src/bullet.h +++ b/src/bullet.h @@ -10,8 +10,11 @@ typedef struct { int active; } Bullet; +/* Initialize an existing Bullet struct */ +void initBullet(Bullet *bullet, int x, int y); + void updateBullets(Bullet *bullets, int *bulletCount, SDL_Renderer *renderer); void fireBullet(Bullet *bullets, int *bulletCount, SDL_Rect *rect); -void cleanupUnrenderedBullets(Bullet* bullets, int* bulletCount); +void cleanupBullets(Bullet *bullets, int *bulletCount); #endif diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000..a27aacb --- /dev/null +++ b/src/game.h @@ -0,0 +1,25 @@ +#ifndef GAME_H +#define GAME_H + +#include + +#define FPS 60 +#define FRAME_DELAY (1000 / FPS) // Milliseconds per frame +#define SCREEN_WIDTH 800 +#define SCREEN_HEIGHT 600 + +typedef struct { + char *spaceshipPath; + char *backgroundPath; + char *bulletPath; +} AssetPaths; + +typedef struct { + SDL_Texture *bulletTexture; + SDL_Texture *playerTexture; + SDL_Texture *backgroundTexture; +} Textures; + +extern Textures textures; + +#endif /* GAME_H */ \ No newline at end of file diff --git a/src/main.c b/src/main.c index 09f9c8f..e929cf7 100644 --- a/src/main.c +++ b/src/main.c @@ -1,25 +1,150 @@ #include "bullet.h" +#include "game.h" #include "player.h" #include #include #include #include -#define FPS 60 -#define FRAME_DELAY (1000 / FPS) // Milliseconds per frame -#define SCREEN_WIDTH 800 -#define SCREEN_HEIGHT 600 +Textures textures = {0}; // global textures storage (initialized to NULL pointers) -void handlePlayer(SDL_Renderer *renderer, Player *player, int speed, const Uint8 *keys) { - if (keys[SDL_SCANCODE_LEFT] || keys[SDL_SCANCODE_A]) { - movePlayerLeft(player, speed); - } - if (keys[SDL_SCANCODE_RIGHT] || keys[SDL_SCANCODE_D]) { - movePlayerRight(player, speed, SCREEN_WIDTH); +int loadTextures(SDL_Renderer *renderer, AssetPaths *paths) { + SDL_Surface *bulletSurface = SDL_LoadBMP(paths->bulletPath); + if (!bulletSurface) { + fprintf(stderr, "Could not load bullet image: %s\n", SDL_GetError()); + return 0; } - // Render player flipped vertically - renderPlayerFlipped(renderer, player, SDL_FLIP_VERTICAL); + textures.bulletTexture = SDL_CreateTextureFromSurface(renderer, bulletSurface); + SDL_FreeSurface(bulletSurface); + if (!textures.bulletTexture) { + fprintf(stderr, "Could not create bullet texture: %s\n", SDL_GetError()); + return 0; + } + + SDL_Surface *playerSurface = SDL_LoadBMP(paths->spaceshipPath); + if (!playerSurface) { + fprintf(stderr, "Could not load player image: %s\n", SDL_GetError()); + SDL_DestroyTexture(textures.bulletTexture); + textures.bulletTexture = NULL; + return 0; + } + + textures.playerTexture = SDL_CreateTextureFromSurface(renderer, playerSurface); + SDL_FreeSurface(playerSurface); + if (!textures.playerTexture) { + fprintf(stderr, "Could not create player texture: %s\n", SDL_GetError()); + SDL_DestroyTexture(textures.bulletTexture); + textures.bulletTexture = NULL; + return 0; + } + + /* Load background (non-fatal) */ + SDL_Surface *bgSurface = SDL_LoadBMP(paths->backgroundPath); + if (!bgSurface) { + fprintf(stderr, "Could not load background %s: %s\n", paths->backgroundPath, SDL_GetError()); + textures.backgroundTexture = NULL; + } else { + textures.backgroundTexture = SDL_CreateTextureFromSurface(renderer, bgSurface); + SDL_FreeSurface(bgSurface); + if (!textures.backgroundTexture) { + fprintf(stderr, "Could not create background texture: %s\n", SDL_GetError()); + textures.backgroundTexture = NULL; + } + } + + return 1; +} + +/* + * Parse command-line arguments. Returns: + * 0 -> continue normally + * 1 -> printed help/version, should exit success + * -1 -> error in args, should exit failure + */ +static int handleArguments(int argc, char *argv[], char **resLocation) { + for (int args = 1; args < argc; args++) { + if (strcmp(argv[args], "--help") == 0 || strcmp(argv[args], "-h") == 0) { + printf("Star Invaders Help:\n"); + printf("Use arrow keys or A/D to move the spaceship left and right.\n"); + printf("Press SPACE to fire bullets.\n"); + printf("Press ESC or close the window to exit the game.\n"); + printf("Use --res-location or -rl to specify the resource location.\n"); + return 1; + } else if (strcmp(argv[args], "--version") == 0 || strcmp(argv[args], "-v") == 0) { + printf("Star Invaders Version 1.0.0\n"); + return 1; + } else if (strcmp(argv[args], "--res-location") == 0 || strcmp(argv[args], "-rl") == 0) { + if (args + 1 >= argc) { + fprintf(stderr, "Missing value for %s\n", argv[args]); + printf("Use --help or -h for usage information.\n"); + return -1; + } + *resLocation = argv[++args]; + } else { + printf("Unknown argument: %s\n", argv[args]); + printf("Use --help or -h for usage information.\n"); + return -1; + } + } + return 0; +} + +AssetPaths *getResources(const char *resLocation) { + AssetPaths *paths = (AssetPaths *)malloc(sizeof(AssetPaths)); + if (!paths) { + fprintf(stderr, "Out of memory\n"); + return NULL; + } + + size_t pathLen; + const char *shipFile = "spaceship.bmp"; + const char *bgFile = "background.bmp"; + const char *bulletFile = "bullet.bmp"; + + pathLen = strlen(resLocation) + strlen(shipFile) + 2; + paths->spaceshipPath = (char *)malloc(pathLen); + if (!paths->spaceshipPath) { + free(paths); + fprintf(stderr, "Out of memory\n"); + return NULL; + } + if (resLocation[strlen(resLocation) - 1] == '/') { + snprintf(paths->spaceshipPath, pathLen, "%s%s", resLocation, shipFile); + } else { + snprintf(paths->spaceshipPath, pathLen, "%s/%s", resLocation, shipFile); + } + + pathLen = strlen(resLocation) + strlen(bgFile) + 2; + paths->backgroundPath = (char *)malloc(pathLen); + if (!paths->backgroundPath) { + free(paths->spaceshipPath); + free(paths); + fprintf(stderr, "Out of memory\n"); + return NULL; + } + if (resLocation[strlen(resLocation) - 1] == '/') { + snprintf(paths->backgroundPath, pathLen, "%s%s", resLocation, bgFile); + } else { + snprintf(paths->backgroundPath, pathLen, "%s/%s", resLocation, bgFile); + } + + pathLen = strlen(resLocation) + strlen(bulletFile) + 2; + paths->bulletPath = (char *)malloc(pathLen); + if (!paths->bulletPath) { + free(paths->backgroundPath); + free(paths->spaceshipPath); + free(paths); + fprintf(stderr, "Out of memory\n"); + return NULL; + } + if (resLocation[strlen(resLocation) - 1] == '/') { + snprintf(paths->bulletPath, pathLen, "%s%s", resLocation, bulletFile); + } else { + snprintf(paths->bulletPath, pathLen, "%s/%s", resLocation, bulletFile); + } + + return paths; } int main(int argc, char *argv[]) { @@ -29,74 +154,31 @@ int main(int argc, char *argv[]) { } char *resLocation = "./res/"; - - for (int args = 1; args < argc; args++) { - if (strcmp(argv[args], "--help") == 0 || - strcmp(argv[args], "-h") == 0) { - printf("Star Invaders Help:\n"); - printf("Use arrow keys or A/D to move the spaceship left and right.\n"); - printf("Press SPACE to fire bullets.\n"); - printf("Press ESC or close the window to exit the game.\n"); - printf("Use --res-location or -rl to specify the resource location.\n"); - SDL_Quit(); - return EXIT_SUCCESS; - } else if (strcmp(argv[args], "--version") == 0 || - strcmp(argv[args], "-v") == 0) { - printf("Star Invaders Version 1.0.0\n"); - SDL_Quit(); - return EXIT_SUCCESS; - } else if (strcmp(argv[args], "--res-location") == 0 || - strcmp(argv[args], "-rl") == 0) { - resLocation = argv[args + 1]; - args++; - } else { - printf("Unknown argument: %s\n", argv[args]); - printf("Use --help or -h for usage information.\n"); - SDL_Quit(); - return EXIT_FAILURE; - } - } - printf("Using resource location: %s\n", resLocation); - - // Build full paths for assets - size_t pathLen; - char *spaceshipPath; - char *backgroundPath; - const char *shipFile = "spaceship.bmp"; - const char *bgFile = "background.bmp"; - - pathLen = strlen(resLocation) + strlen(shipFile) + 2; - spaceshipPath = (char *)malloc(pathLen); - if (!spaceshipPath) { - fprintf(stderr, "Out of memory\n"); + int argResult = handleArguments(argc, argv, &resLocation); + if (argResult == 1) { + SDL_Quit(); + return EXIT_SUCCESS; + } else if (argResult == -1) { SDL_Quit(); return EXIT_FAILURE; } - if (resLocation[strlen(resLocation) - 1] == '/') { - snprintf(spaceshipPath, pathLen, "%s%s", resLocation, shipFile); - } else { - snprintf(spaceshipPath, pathLen, "%s/%s", resLocation, shipFile); - } - pathLen = strlen(resLocation) + strlen(bgFile) + 2; - backgroundPath = (char *)malloc(pathLen); - if (!backgroundPath) { - free(spaceshipPath); - fprintf(stderr, "Out of memory\n"); + AssetPaths *paths = getResources(resLocation); + if (!paths) { + fprintf(stderr, "Could not load resources\n"); SDL_Quit(); return EXIT_FAILURE; } - if (resLocation[strlen(resLocation) - 1] == '/') { - snprintf(backgroundPath, pathLen, "%s%s", resLocation, bgFile); - } else { - snprintf(backgroundPath, pathLen, "%s/%s", resLocation, bgFile); - } SDL_Window *window = SDL_CreateWindow("Star Invaders", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); if (!window) { fprintf(stderr, "Could not create window: %s\n", SDL_GetError()); + free(paths->backgroundPath); + free(paths->bulletPath); + free(paths->spaceshipPath); + free(paths); SDL_Quit(); return EXIT_FAILURE; } @@ -106,42 +188,46 @@ int main(int argc, char *argv[]) { if (!renderer) { fprintf(stderr, "Could not create renderer: %s\n", SDL_GetError()); SDL_DestroyWindow(window); + free(paths->backgroundPath); + free(paths->bulletPath); + free(paths->spaceshipPath); + free(paths); SDL_Quit(); return EXIT_FAILURE; } - int speed = 5; + if (!loadTextures(renderer, paths)) { + fprintf(stderr, "Could not create game textures: %s\n", SDL_GetError()); + free(paths->backgroundPath); + free(paths->bulletPath); + free(paths->spaceshipPath); + free(paths); + SDL_Quit(); + return EXIT_FAILURE; + } // Bullets array - Bullet bullets[MAX_BULLETS]; int bulletCount = 0; + Bullet bullets[MAX_BULLETS]; for (int i = 0; i < MAX_BULLETS; i++) { - bullets[i].active = 0; + initBullet(&bullets[i], 0, 0); } // Create player - Player *player = createPlayer(renderer, spaceshipPath, 400, 500); + Player *player = createPlayer(400, 500); if (!player) { SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); - free(spaceshipPath); - free(backgroundPath); + free(paths->spaceshipPath); + free(paths->backgroundPath); + free(paths); + if (textures.bulletTexture) SDL_DestroyTexture(textures.bulletTexture); + if (textures.playerTexture) SDL_DestroyTexture(textures.playerTexture); SDL_Quit(); return EXIT_FAILURE; } - // Load background - SDL_Texture *background = NULL; - SDL_Surface *bgSurface = SDL_LoadBMP(backgroundPath); - if (!bgSurface) { - fprintf(stderr, "Could not load background %s: %s\n", backgroundPath, SDL_GetError()); - } else { - background = SDL_CreateTextureFromSurface(renderer, bgSurface); - SDL_FreeSurface(bgSurface); - if (!background) { - fprintf(stderr, "Could not create background texture: %s\n", SDL_GetError()); - } - } + /* background is loaded into textures.backgroundTexture by loadTextures */ // Main loop flag int running = 1; @@ -171,16 +257,16 @@ int main(int argc, char *argv[]) { SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); // Draw background if available - if (background) { - SDL_RenderCopy(renderer, background, NULL, NULL); + if (textures.backgroundTexture) { + SDL_RenderCopy(renderer, textures.backgroundTexture, NULL, NULL); } // Update and draw bullets updateBullets(bullets, &bulletCount, renderer); - cleanupUnrenderedBullets(bullets, &bulletCount); + // cleanupUnrenderedBullets(bullets, &bulletCount); // removed // Handle player movement and rendering - handlePlayer(renderer, player, speed, keys); + handlePlayer(renderer, player, playerSpeed, keys); // Present the rendered frame SDL_RenderPresent(renderer); @@ -193,11 +279,17 @@ int main(int argc, char *argv[]) { } destroyPlayer(player); - if (background) { - SDL_DestroyTexture(background); - } - free(spaceshipPath); - free(backgroundPath); + + // Cleanup bullets (deactivate all) + cleanupBullets(bullets, &bulletCount); + + if (textures.bulletTexture) SDL_DestroyTexture(textures.bulletTexture); + if (textures.playerTexture) SDL_DestroyTexture(textures.playerTexture); + if (textures.backgroundTexture) SDL_DestroyTexture(textures.backgroundTexture); + + free(paths->spaceshipPath); + free(paths->backgroundPath); + free(paths); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); diff --git a/src/player.c b/src/player.c index 4b3cb5a..e22f94d 100644 --- a/src/player.c +++ b/src/player.c @@ -1,27 +1,25 @@ #include "player.h" +#include "game.h" #include #include -Player *createPlayer(SDL_Renderer *renderer, const char *imagePath, int x, - int y) { +int playerSpeed = 5; + +void handlePlayer(SDL_Renderer *renderer, Player *player, int playerSpeed, const Uint8 *keys) { + if (keys[SDL_SCANCODE_LEFT] || keys[SDL_SCANCODE_A]) { + movePlayerLeft(player, playerSpeed); + } + if (keys[SDL_SCANCODE_RIGHT] || keys[SDL_SCANCODE_D]) { + movePlayerRight(player, playerSpeed, SCREEN_WIDTH); + } + + // Render player flipped vertically + renderPlayerFlipped(renderer, player, SDL_FLIP_VERTICAL); +} + +Player *createPlayer(int x, int y) { Player *player = (Player *)malloc(sizeof(Player)); - SDL_Surface *surface = SDL_LoadBMP(imagePath); - if (!surface) { - fprintf(stderr, "Could not load image %s: %s\n", imagePath, SDL_GetError()); - free(player); - return NULL; - } - - player->texture = SDL_CreateTextureFromSurface(renderer, surface); - SDL_FreeSurface(surface); - - if (!player->texture) { - fprintf(stderr, "Could not create texture: %s\n", SDL_GetError()); - free(player); - return NULL; - } - player->rect.x = x; player->rect.y = y; player->rect.w = 50; @@ -31,30 +29,29 @@ Player *createPlayer(SDL_Renderer *renderer, const char *imagePath, int x, } void renderPlayer(SDL_Renderer *renderer, Player *player) { - SDL_RenderCopy(renderer, player->texture, NULL, &player->rect); + SDL_RenderCopy(renderer, textures.playerTexture, NULL, &player->rect); } void renderPlayerFlipped(SDL_Renderer *renderer, Player *player, SDL_RendererFlip flip) { - SDL_RenderCopyEx(renderer, player->texture, NULL, &player->rect, 0, NULL, + SDL_RenderCopyEx(renderer, textures.playerTexture, NULL, &player->rect, 0, NULL, flip); } -void movePlayerLeft(Player *player, int speed) { - if (player->rect.x - speed >= 0) { - player->rect.x -= speed; +void movePlayerLeft(Player *player, int playerSpeed) { + if (player->rect.x - playerSpeed >= 0) { + player->rect.x -= playerSpeed; } } -void movePlayerRight(Player *player, int speed, int screenWidth) { - if (player->rect.x + player->rect.w + speed <= screenWidth) { - player->rect.x += speed; +void movePlayerRight(Player *player, int playerSpeed, int screenWidth) { + if (player->rect.x + player->rect.w + playerSpeed <= screenWidth) { + player->rect.x += playerSpeed; } } void destroyPlayer(Player *player) { if (player) { - SDL_DestroyTexture(player->texture); free(player); } } diff --git a/src/player.h b/src/player.h index c55a0fe..2da2628 100644 --- a/src/player.h +++ b/src/player.h @@ -4,17 +4,19 @@ #include typedef struct { - SDL_Texture *texture; SDL_Rect rect; } Player; -Player *createPlayer(SDL_Renderer *renderer, const char *imagePath, int x, - int y); +extern int playerSpeed; + +void handlePlayer(SDL_Renderer *renderer, Player *player, int playerSpeed, const Uint8 *keys); + +Player *createPlayer(int x, int y); void renderPlayer(SDL_Renderer *renderer, Player *player); void renderPlayerFlipped(SDL_Renderer *renderer, Player *player, SDL_RendererFlip flip); -void movePlayerLeft(Player *player, int speed); -void movePlayerRight(Player *player, int speed, int screenWidth); +void movePlayerLeft(Player *player, int playerSpeed); +void movePlayerRight(Player *player, int playerSpeed, int screenWidth); void destroyPlayer(Player *player); #endif