all repos — katbug @ 10448750d0ae306f7a6287c6a6fab3c545a52bc6

side-scrolling infinite arcade game in C with SDL 1.2

catbug game!
Iris Lightshard nilix@nilfm.cc
PGP Signature
-----BEGIN PGP SIGNATURE-----

iQIzBAABCAAdFiEEkFh6dA+k/6CXFXU4O3+8IhROY5gFAlxYrYkACgkQO3+8IhRO
Y5gPPQ/+P8b1Ebrm77hGqyYqtOOArA9AIeDd1l8Tvim2P3Rc7UzlNEr8NIF6T67A
GR7/aGetCcmt+m1HhC149JyQDmuA3QS+Hl68R0szZIx60DXkHHMy0EyphvfHXQZu
qON2En4EB1ZlEhyyVU7Uy2UfxZAFIuTVz1qpMp/saRaUpNB5RzFcN5SrRjkkh9Zh
z/nFJBaqv6323lMXDAztG9rhGdLuWyARUpMOw+hVm7Cm0lXFl2I/aUh5v6CqnBzz
updefwBefEGuG3jlen1b+DOdPtok8/V1CacCDyKppjU5p0bSWUWm+/ZVYltydoHH
VWUXSus9cqrHa//Ng2SmMPmegIYPKYUuZWo7ucUZqonqVXlRSPnB/1Y58XT/L6FJ
z8FBEkquqFAYhPedOTDiMhDlGzSy2IQG7LJbw59bH9E1YoXy6xYYGTomUMFj0syW
GxwpoRIdvhiJcsbcu/E/nTolhOjugI0OS5hp+xiYciZiNaTXjiVFvQEzKqhB+sHi
0m16X5gbPfGYHLlBj5LOCQ4gQwKLuq7OUqYmivRVA8NuUKi0pnR8pSSXCAjirhKQ
S9quuE6D0bmeVBCudUl8mv4gkTyo1ZsNh8GiVvhb6P8tMeE9bQ0Drrgh3+xY1LQB
/twQwrIA5SLdsvJG0xgpzUpJfDH+B6WPTdS1AcBxh/Tv1vT+o0Q=
=knSh
-----END PGP SIGNATURE-----
commit

10448750d0ae306f7a6287c6a6fab3c545a52bc6

A Catbug.c

@@ -0,0 +1,72 @@

+#include <SDL/SDL.h> +#include <SDL/SDL_image.h> +#include <SDL/SDL_ttf.h> +#include "config.h" +#include "Engine.h" +#include "Catbug.h" +#include "Timer.h" +#include "Pickups.h" +#include "extern.h" +Catbug* newCatbug(int x, int y) +{ + Catbug* self = malloc(sizeof(Catbug)); + + self->x = x; + self->y = y; + + self->vX = 0; + self->vY = 0; + + self->HP = STARTING_HP; + self->frame = 0; + self->spriteSheet=loadImage("assets/catbug.png"); + + return self; +} + +void deleteCatbug(Catbug* target) +{ + SDL_FreeSurface(target->spriteSheet); + free(target); +} + +void moveCatbug(Catbug* self) +{ + self->x += self->vX; + self->y += self->vY; + + if (self->x <= 0 || self->x >= 320) + self->x -= self->vX; + if (self->y <=0 || self->y >= 180) + self->y -= self->vY; +} + +void drawCatbug(Catbug* self) +{ + SDL_Rect clip; + clip.x = 0; + clip.y = 0; + clip.w = 43; + clip.h = 39; + + switch (self->frame) + { + case 0: + clip.x = 0; + self->frame = 1; + break; + case 1: + clip.x = 43; + self->frame = 0; + break; + } + applySurface(self->x - 22, self->y - 19, self->spriteSheet, screen, &clip); +} + +int isInRect(Catbug* self, SDL_Rect* box) +{ + if (self->x >= box->x && self->x <= box->x + box->w + && self->y >= box->y && self->y <= box->y + box->h) + return 1; + else return 0; +}
A Catbug.h

@@ -0,0 +1,19 @@

+typedef struct catbug +{ + SDL_Surface* spriteSheet; + unsigned short int frame; + + int x; + int y; + int vX; + int vY; + + unsigned short int HP; +} Catbug; + +Catbug* newCatbug(int x, int y); +void deleteCatbug(Catbug* target); + +int isInRect(Catbug* self, SDL_Rect* box); +void moveCatbug(Catbug* self); +void drawCatbug(Catbug* self);
A Engine.c

@@ -0,0 +1,441 @@

+#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "config.h" + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_ttf.h" + +#include "Engine.h" +#include "Timer.h" +#include "Catbug.h" +#include "Pickups.h" + +#include "extern.h" + +SDL_Surface* loadImage(char* filename) +{ + SDL_Surface* loadedImage = NULL; + SDL_Surface* optimizedImage = NULL; + + loadedImage = IMG_Load(filename); + if (loadedImage != NULL) + { + optimizedImage = SDL_DisplayFormat(loadedImage); + SDL_FreeSurface(loadedImage); + } + + return optimizedImage; +} + +void applySurface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip) +{ + SDL_Rect offset; + offset.x = x; + offset.y = y; + + SDL_BlitSurface(source, clip, destination, &offset); +} + +Uint32 getPixel(SDL_Surface* surface, int x, int y) +{ + Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * surface->format->BytesPerPixel; + return *(Uint32 *)p; +} + +void scaleScreen() +{ + Sint32 x, y; + static SDL_Rect superPixel = {0,0, SCALE_FACTOR,SCALE_FACTOR}; + + switch (SCALE_FACTOR) + { + case 1: + applySurface(0,0,screen,window,NULL); + break; + + default: + superPixel.y = 0; + for (y = 0; y < SCREEN_HEIGHT; y++) + { + superPixel.x = 0; + for (x = 0; x < SCREEN_WIDTH; x++) + { + SDL_FillRect(window, &superPixel, getPixel(screen, x, y)); + superPixel.x += SCALE_FACTOR; + } + superPixel.y += SCALE_FACTOR; + } + break; + } +} + +void updateScore() +{ + SDL_Color white = {255,255,255}; + char sPoints[10]; + sprintf(sPoints, "%d", points); + score = TTF_RenderText_Solid(font,sPoints,white); + applySurface(10, 20, score, screen, NULL); +} + +void drawHP() +{ + int i; + for (i = 0; i < player->HP; i++) + { + applySurface(10+(6*i), 10, hpwedge, screen, NULL); + } +} + +void checkHP() +{ + if (player->HP <= 0) + { + scoreSummary(); + quit = 1; + } +} + +void scoreSummary() +{ + SDL_Surface* yougot = NULL; + SDL_Surface* hiscore = NULL; + SDL_Color white = {255, 255, 255}; + char sPoints[10]; + + sprintf(sPoints,"%d", points); + yougot = TTF_RenderText_Solid(font2, "SCORE:" ,white); + hiscore = TTF_RenderText_Solid(font2, sPoints, white); + + int here = 1; + while (here) + { + timeStart(&fps); + renderBG(); + applySurface(120, 80, yougot, screen, NULL); + applySurface(120, 105, hiscore, screen, NULL); + + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + case FS_BUTTON: + toggleFullscreen(); + break; + case PAUSE_BUTTON: + here = 0; + quit = 0; + break; + case QUIT_BUTTON: + here = 0; + playing = 0; + default: + break; + } + break; + case SDL_QUIT: + here = 0; + playing = 0; + break; + default: + break; + } + } + frameAdvance(); + } + SDL_FreeSurface(yougot); + SDL_FreeSurface(hiscore); +} + +void renderBG() +{ + static int x = 0; + applySurface(x,0,bg,screen,NULL); + applySurface(x+1472,0,bg,screen,NULL); + x -= 8; + if (x == -1472) x = 0; +} + +void interact() +{ + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + case DPAD_UP: + player->vY = -PLAYER_SPD; + break; + case DPAD_LEFT: + player->vX = -PLAYER_SPD; + break; + case DPAD_DOWN: + player->vY = PLAYER_SPD; + break; + case DPAD_RIGHT: + player->vX = PLAYER_SPD; + break; + case FS_BUTTON: + toggleFullscreen(); + break; + case PAUSE_BUTTON: + pauseGame(); + break; + case QUIT_BUTTON: + quit = 1; + playing = 0; + break; + default: + break; + } + break; + case SDL_KEYUP: + switch (event.key.keysym.sym) + { + case DPAD_UP: + if (player->vY < 0) + player->vY = 0; + break; + case DPAD_LEFT: + if (player->vX < 0) + player->vX = 0; + break; + case DPAD_DOWN: + if (player->vY > 0) + player->vY = 0; + break; + case DPAD_RIGHT: + if (player->vX > 0) + player->vX = 0; + break; + default: + break; + } + break; + case SDL_QUIT: + quit = 1; + playing = 0; + break; + default: + break; + } + } +} + +int init(int argc, char* args[]) +{ + int i; + printf("Initializing SDL\n"); + + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) == -1) return 0; + printf("Initialized SDL\nCreating window\n"); + + if (argc >= 2) + { + if (strcmp(args[1], "-w")) + { + window = SDL_SetVideoMode(SCREEN_WIDTH*SCALE_FACTOR, SCREEN_HEIGHT*SCALE_FACTOR, 32, SDL_FULLSCREEN|SDL_HWSURFACE); + fullscreen = 1; + } + else window = SDL_SetVideoMode(SCREEN_WIDTH*SCALE_FACTOR, SCREEN_HEIGHT*SCALE_FACTOR, 32, SDL_HWSURFACE|SDL_RESIZABLE); + } + else + { + window = SDL_SetVideoMode(SCREEN_WIDTH*SCALE_FACTOR, SCREEN_HEIGHT*SCALE_FACTOR, 32, SDL_FULLSCREEN|SDL_HWSURFACE); + fullscreen = 1; + } + SDL_ShowCursor(0); + + screen = loadImage("assets/rawscreen.png"); + if (window == NULL || screen == NULL) return 0; + + SDL_WM_SetCaption("Catbug", NULL); + printf("Window created\nInitializing data\n"); + + if (TTF_Init() == -1) return 0; + + ticker = time(NULL); + bg = loadImage("assets/seethruzone.png"); + hpwedge = loadImage("assets/hp.png"); + + font = TTF_OpenFont("assets/charriot.ttf", 10); + font2 = TTF_OpenFont("assets/charriot.ttf", 20); + + player = newCatbug(35, 90); + stuff = malloc(10*sizeof(Pickup*)); + for (i = 0; i < 10; i++) + { + stuff[i] = NULL; + } + + return 1; +} + +void title() +{ + SDL_Surface* titletxt = loadImage("assets/titletext.png"); + int here = 1; + while (here) + { + timeStart(&fps); + renderBG(); + applySurface(0, 0, titletxt, screen, NULL); + + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + case FS_BUTTON: + toggleFullscreen(); + break; + case PAUSE_BUTTON: + here = 0; + quit = 0; + break; + case QUIT_BUTTON: + here = 0; + quit = 1; + playing = 0; + default: + break; + } + break; + case SDL_QUIT: + here = 0; + playing = 0; + quit = 1; + break; + default: + break; + } + + } + frameAdvance(); + } + SDL_FreeSurface(titletxt); +} + +void resetGame() +{ + maxHP = STARTING_HP; + player->HP = STARTING_HP; + player->x = 35; + player->y = 90; + player->vX = 0; + player->vY = 0; + points = 0; + threshold = 10; + cleanPickups(); +} + +void pauseGame() +{ + SDL_Color white = {255,255,255}; + SDL_Surface* paused = TTF_RenderText_Solid(font,"paused",white); + + int here = 1; + + while (here) + { + timeStart(&fps); + player->vX = 0; + player->vY = 0; + applySurface(10,30,paused,screen,NULL); + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_KEYDOWN: + switch (event.key.keysym.sym) + { + case FS_BUTTON: + toggleFullscreen(); + break; + case PAUSE_BUTTON: + here = 0; + break; + case QUIT_BUTTON: + here = 0; + playing = 0; + quit = 1; + default: + break; + } + break; + case SDL_QUIT: + here = 0; + playing = 0; + quit = 1; + break; + default: + break; + } + } + frameAdvance(); + } + SDL_FreeSurface(paused); +} + +void timeDilation() +{ + int i = getTicks(&fps); + if (i < 1000/30) + SDL_Delay(1000/30 - i); +} + +void frameAdvance() +{ + scaleScreen(); + SDL_Flip(window); + timeDilation(); +} + +void toggleFullscreen() +{ + if (!fullscreen) + { + SDL_SetVideoMode(SCREEN_WIDTH*SCALE_FACTOR, SCREEN_HEIGHT*SCALE_FACTOR, 32, SDL_FULLSCREEN|SDL_HWSURFACE); + fullscreen = 1; + } + else + { + SDL_SetVideoMode(SCREEN_WIDTH*SCALE_FACTOR, SCREEN_HEIGHT*SCALE_FACTOR, 32, SDL_HWSURFACE|SDL_RESIZABLE); + fullscreen = 0; + } +} + +void cleanup() +{ + if (fullscreen) + toggleFullscreen(); + + printf("Freeing assets\n"); + deleteCatbug(player); + cleanPickups(); + free(stuff); + + SDL_FreeSurface(bg); + SDL_FreeSurface(hpwedge); + SDL_FreeSurface(score); + SDL_FreeSurface(screen); + SDL_FreeSurface(window); + + TTF_CloseFont(font); + TTF_CloseFont(font2); + + printf("Closing SDL systems\n"); + TTF_Quit(); + SDL_Quit(); + printf("Cleanup complete\n"); +}
A Engine.h

@@ -0,0 +1,22 @@

+SDL_Surface* loadImage(char* filename); +void applySurface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip); +void scaleScreen(); + +void renderBG(); +void drawHP(); + +void checkHP(); +void updateScore(); + +void title(); +void resetGame(); +void pauseGame(); +void scoreSummary(); + +void interact(); + +int init(int argc, char* args[]); +void cleanup(); +void toggleFullscreen(); +void timeDilation(); +void frameAdvance();
A Makefile

@@ -0,0 +1,16 @@

+CFLAGS= -Wall -I/usr/include/SDL -L/usr/lib/ -lSDL -lSDL_image -lSDL_ttf + +.PHONY: all clean cleanobj + +all: game + +game: main.c Catbug.o Engine.o Timer.o Pickups.o + $(CC) -o katbug $^ $(CFLAGS) + +cleanobj: + rm -f *.o + +clean: + rm -f katbug *.o + +reconfigure: clean game
A Pickups.c

@@ -0,0 +1,144 @@

+#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#include <SDL/SDL.h> +#include <SDL/SDL_image.h> +#include <SDL/SDL_ttf.h> + +#include "config.h" +#include "Engine.h" +#include "Catbug.h" +#include "Pickups.h" +#include "Timer.h" +#include "extern.h" + +Pickup* newPickup() +{ + int pType; + srand(rand()); + pType = rand()%10; + Pickup* self = malloc(sizeof(Pickup)); + + self->y = 16 + rand()%148; + self->x = 320 + rand()%120; + self->vX = -4 - rand()%maxHP; + self->vY = 0; + + switch (pType) + { + case 0: + case 1: + case 2: + case 3: + case 4: + self->sprite = loadImage("assets/sugarpeas.png"); + self->points = 1; + break; + case 5: + case 6: + case 7: + self->sprite = loadImage("assets/peanutbuttersquare.png"); + self->points = 2; + break; + case 8: + case 9: + self->sprite = loadImage("assets/taco.png"); + self->points = 3; + break; + } + return self; +} + +void managePickups() +{ + int i; + int genSpeed; + srand(rand()); + + if (points > 10) + genSpeed = rand()%points; + else genSpeed = rand()%10; + + for (i = 0; i < 10; i++) + { + if (stuff[i] != NULL) + { + drawPickup(stuff[i]); + movePickup(stuff[i]); + + if (stuff[i]->x < -16) + { + invalidatePickup(stuff[i]); + player->HP--; + stuff[i] = NULL; + } + else + { + SDL_Rect box; + box.x = stuff[i]->x - 18; + box.y = stuff[i]->y - 18; + box.w = 36; + box.h = 36; + + if (isInRect(player, &box)) + { + getPickup(stuff[i]); + stuff[i] = NULL; + } + } + } + else + { + if ( (stuff[i] == NULL) && + ((points >= 40 && genSpeed*genSpeed >= points*4) || + (points <= 40 && genSpeed*genSpeed >= threshold*2)) ) + { + if (ticker != time(NULL)){ + stuff[i] = newPickup(); + ticker = time(NULL);} + } + } + } +} + +void movePickup(Pickup* self) +{ + self->x += self->vX; +} + +void drawPickup(Pickup* self) +{ + applySurface(self->x - 16, self->y - 16, self->sprite, screen, NULL); +} + +void invalidatePickup(Pickup* self) +{ + SDL_FreeSurface(self->sprite); + free(self); +} + +void getPickup(Pickup* self) +{ + points += self->points; + if (points >= threshold) + { + threshold *= 2; + maxHP++; + player->HP = maxHP; + } + invalidatePickup(self); +} + +void cleanPickups() +{ + int i; + for (i = 0; i < 10; i++) + { + if (stuff[i] != NULL) + { + invalidatePickup(stuff[i]); + stuff[i] = NULL; + } + } +}
A Pickups.h

@@ -0,0 +1,19 @@

+typedef struct pickup +{ + int x; + int y; + int vX; + int vY; + + SDL_Surface* sprite; + int points; +} Pickup; + +Pickup* newPickup(); + +void managePickups(); +void movePickup(Pickup* self); +void drawPickup(Pickup* self); +void invalidatePickup(Pickup* self); +void getPickup(Pickup* self); +void cleanPickups();
A Timer.c

@@ -0,0 +1,25 @@

+#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_ttf.h" + +#include "Timer.h" + +Timer* newTimer() +{ + Timer* self = (Timer*)malloc(sizeof(Timer)); + self->startTicks = 0; + return self; +} + +void timeStart(Timer* self) +{ + self->startTicks = SDL_GetTicks(); +} + +int getTicks(Timer* self) +{ + if (self->startTicks) + return SDL_GetTicks() - self->startTicks; + else return 0; +} +
A Timer.h

@@ -0,0 +1,8 @@

+typedef struct timer +{ + int startTicks; + +} Timer; + +void timeStart(Timer* self); +int getTicks(Timer* self);
A config.h

@@ -0,0 +1,19 @@

+// key configuration +// see https://sdl.beuc.net/sdl.wiki/SDLKey for keysyms +// for joypad support use a tool like antimicro +#define DPAD_UP SDLK_UP +#define DPAD_DOWN SDLK_DOWN +#define DPAD_LEFT SDLK_LEFT +#define DPAD_RIGHT SDLK_RIGHT + +#define PAUSE_BUTTON SDLK_SPACE +#define QUIT_BUTTON SDLK_q +#define FS_BUTTON SDLK_f + +// difficulty settings: 3/6/7 is a good balance +#define STARTING_HP 3 +#define PLAYER_SPD 6 +#define SPEED_MOD 7 + +// video scaling factor +#define SCALE_FACTOR 2
A extern.h

@@ -0,0 +1,27 @@

+extern int playing; +extern int quit; +extern int fullscreen; +extern int points; +extern int threshold; +extern int maxHP; + +extern time_t ticker; +extern Timer fps; +extern SDL_Event event; + +extern SDL_Surface* screen; +extern SDL_Surface* window; +extern SDL_Surface* bg; +extern SDL_Surface* hpwedge; +extern SDL_Surface* score; +extern TTF_Font* font; +extern TTF_Font* font2; + +extern Catbug* player; +extern Pickup** stuff; + +enum screenDimension +{ + SCREEN_WIDTH = 320, + SCREEN_HEIGHT = 180 +};
A main.c

@@ -0,0 +1,66 @@

+#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <SDL/SDL.h> +#include <SDL/SDL_image.h> +#include <SDL/SDL_ttf.h> +#include "config.h" +#include "Engine.h" +#include "Catbug.h" +#include "Timer.h" +#include "Pickups.h" + +int playing = 1; +int quit = 0; +int fullscreen = 0; +int points = 0; +int threshold = 10; +int maxHP = STARTING_HP; + +time_t ticker; +Timer fps; +SDL_Event event; + +SDL_Surface* screen = NULL; +SDL_Surface* window = NULL; + +SDL_Surface* score = NULL; +SDL_Surface* bg = NULL; +SDL_Surface* hpwedge = NULL; +TTF_Font* font = NULL; +TTF_Font* font2 = NULL; + +Catbug* player; +Pickup** stuff; + + +int main(int argc, char* args[]) +{ + if (!init(argc, args)) + { + printf("Init failed\n"); + return 1; + } + printf("Init complete\n"); + + while (playing) + { + title(); + resetGame(); + while(!quit) + { + timeStart(&fps); + renderBG(); + drawCatbug(player); + moveCatbug(player); + managePickups(); + updateScore(); + drawHP(); + interact(); + frameAdvance(); + checkHP(); + } + } + cleanup(); + return 0; +}