From c7def3172977a8d128ff9882d67e604e480f3499 Mon Sep 17 00:00:00 2001 From: Gentoo Date: Wed, 6 Jan 2021 21:45:09 +1100 Subject: +ported to SDL2 +split cat and rainbow draw calls and functions +/- split cat and rainbow images +/- made rainbows drift off screen +spritesheets are now used instead of individual images +"fixed" indentation +replaced old argument code with getopt +added argument that lets you spawn N cats +/- made scaling work (poor results but no longer crashes) +added option to make cat follow sine wave -removed multi screen code (SDL2 fullscreen only works with one screen) --- Makefile | 27 +- README | 35 +- TODO | 14 +- draw.h | 147 ++++++++ globals.h | 66 ++++ list.h | 4 +- nyan.c | 960 +++++++++++++++++++----------------------------- res/default/cat.png | Bin 0 -> 3183 bytes res/default/data | 1 + res/default/nyan.xcf | Bin 0 -> 45424 bytes res/default/rainbow.png | Bin 0 -> 357 bytes res/default/rainbow.xcf | Bin 0 -> 1020 bytes res/default/sparkle.png | Bin 0 -> 535 bytes res/default/sparkle.xcf | Bin 0 -> 5162 bytes res/freedom/cat.png | Bin 0 -> 2030 bytes res/freedom/cat.xcf | Bin 0 -> 40734 bytes res/freedom/data | 1 + res/freedom/rainbow.png | Bin 0 -> 284 bytes res/freedom/rainbow.xcf | Bin 0 -> 4748 bytes res/freedom/sparkle.png | Bin 0 -> 555 bytes res/freedom/sparkle.xcf | Bin 0 -> 4001 bytes 21 files changed, 627 insertions(+), 628 deletions(-) create mode 100644 draw.h create mode 100644 globals.h create mode 100644 res/default/cat.png create mode 100644 res/default/nyan.xcf create mode 100644 res/default/rainbow.png create mode 100644 res/default/rainbow.xcf create mode 100644 res/default/sparkle.png create mode 100644 res/default/sparkle.xcf create mode 100644 res/freedom/cat.png create mode 100644 res/freedom/cat.xcf create mode 100644 res/freedom/rainbow.png create mode 100644 res/freedom/rainbow.xcf create mode 100644 res/freedom/sparkle.png create mode 100644 res/freedom/sparkle.xcf diff --git a/Makefile b/Makefile index 8abb154..becb260 100644 --- a/Makefile +++ b/Makefile @@ -1,24 +1,17 @@ +CC = gcc RES = /usr/share/nyancat BIN = /usr/bin/nyancat -LIBS = -lSDL -lSDL_image -lSDL_mixer -lX11 -FLAGS = -pedantic -Wall -O2 -std=gnu99 -INCS = -I. -I/usr/include ${XINERAMAINC} +LIBS = -lSDL2 -lSDL2_image -lSDL2_mixer -lm +CFLAGS = -O3 -march=native -flto -Wall +OBJ = nyan.o +HEADERS = list.h draw.h globals.h -XINERAMAINC = -I/usr/X11R6/include -XINERAMALIBS = -L/usr/X11R6/lib -lXinerama -XINERAMAFLAGS = -DXINERAMA -nyancat: nyan.c list.h - cc -g nyan.c -o nyancat ${LIBS} ${XINERAMALIBS} ${XINERAMAINC} ${FLAGS} ${XINERAMAFLAGS} +%.o: %.c $(HEADERS) + $(CC) -c -o $@ $< $(CFLAGS) $(LIBS) -install: - cp nyancat ${BIN} - mkdir --parents ${RES} - cp -rv res/* ${RES} +nyancat: $(OBJ) + $(CC) -o $@ $^ $(CFLAGS) $(LIBS) clean: - rm nyancat - -uninstall: - rm ${BIN} - rm -rv ${RES} + rm nyancat $(OBJ) diff --git a/README b/README index cb082ba..a8111cf 100644 --- a/README +++ b/README @@ -1,21 +1,18 @@ Now with full MooGNU support! -Usage: nyancat [OPTIONS] - -h, --help This help message - -f, --fullscreen Enable fullscreen mode (default) - -nf, --nofullscreen Disable fullscreen mode - -c, --catsize Choose size of cat, options are full and - small. Small is default. "Full" is not - officially supported. - -nc, --nocursor Don't show the cursor (default) - -sc, --cursor, --showcursor Show the cursor - -ns, --nosound Don't play sound - -v, --volume Sets Volume, if enabled, from 0 - 128 - -r, --resolution Make next two arguments the screen resolution - to use (0 and 0 for full resolution) - (800x600 default) - -d, --data-set Use an alternate data set. Packaged with - this program by default are "default" and - "freedom" sets. - -hw, -sw Use hardware or software SDL rendering, - respectively. Hardware is default +Usage: ./nyancat [OPTIONS] + -h, --help This help message. + -f, --fullscreen Enable fullscreen (default). + -w, --windowed Disable fullscreen. + -c, --catsize Choose size to scale cat to (between 0.1 to 10.0) + -a, --nocursor Don't show the cursor (default). + -b, --cursor Show the cursor. + -e, --nosound Don't play sound. + -v, --volume Set Volume, if enabled, from 0 - 128. + -r, --resolution Argument format: x (1200x800 default). + -d, --data-set Use an alternate data set. Packaged with + this program by default are "default" + and "freedom" sets. + -s, --sine Make cat move in a sine wave. + -n, --count Number of cats to spawn. + diff --git a/TODO b/TODO index dab926a..be9c899 100644 --- a/TODO +++ b/TODO @@ -1,14 +1,6 @@ --- Fix Xinerama support --- Add more command-line args +-- Fix multiscreen support -- Add rc file -- Add configure script --- Separate cat and rainbow into separate draw calls (and image files) -- Add man page(?) - --- Fullscreen resolution autodetect broken by most recent update --> Fixed this temporarily but CPU usage is still proving very high/cat scaling is a bit off for wide monitors --> Defaulting to the small cat until I can re-implement Xinerama support and fix scaling issues on wide setups --> -- Johnson - --- Increase efficiency of clear_screen() by not blanking the area of sparkles behind a cat --- Add support for vary long displays ( stretch_cat() function ) +-- Port to xscreensaver(?) +-- Add more cats diff --git a/draw.h b/draw.h new file mode 100644 index 0000000..ab81617 --- /dev/null +++ b/draw.h @@ -0,0 +1,147 @@ +#ifndef DRAW_H +#define DRAW_H + +void add_sparkle(void) + { + sparkle_instance* new; + + new = ec_malloc(sizeof(sparkle_instance)); + new->loc.x = SCREEN_WIDTH + 80; + new->loc.y = (rand() % (SCREEN_HEIGHT + sparkle_height)) - sparkle_height; + new->frame = 0; + new->frame_mov = 1; + new->speed = 10 + (rand() % 30); + new->layer = rand() % 2; + list_add(&new->list, &sparkle_list); + } + +void add_cat(unsigned int x, unsigned int y) + { + cat_instance* new; + + new = ec_malloc(sizeof(cat_instance)); + new->loc.x = x; + new->loc.y = y; + list_add(&new->list, &cat_list); + } + +void add_rainbow(unsigned int x, unsigned int y) + { + rainbow_instance* new; + + new = ec_malloc(sizeof(rainbow_instance)); + new->loc.x = x; + new->loc.y = y; + new->sprite = rainbow_sprite; + list_add(&new->list, &rainbow_list); + } + +void update_rainbows() + { + cat_instance *c; + + /* Rainbows need to be placed according to the cat position (which changes constantly) */ + list_for_each_entry(c, &cat_list, list) + { + add_rainbow((SCREEN_WIDTH - rainbow_width) / 2 - OFFSET /* Default position in the center */ + - ((SCREEN_WIDTH - cat_width) / 2 - c->loc.x), /* Plus the amount the cat is off the center */ + (SCREEN_HEIGHT - rainbow_height) / 2 /* Default position in the center */ + - ((SCREEN_HEIGHT - cat_height) / 2 - c->loc.y));/* Plus the amount the cat is off the center */ + } + + + rainbow_instance *r, *tmpr; + + /* Update the position of each rainbow that exists */ + list_for_each_entry_safe(r, tmpr, &rainbow_list, list) + { + r->loc.x -= rainbow_width; + + /* If rainbow if off the screen, delete and free() it */ + if ((r->loc.x + rainbow_width) < 0) + { + list_del(&r->list); + free(r); + } + } + } + +void update_sparkles() + { + + sparkle_spawn_counter += rand() % SCREEN_HEIGHT; + while (sparkle_spawn_counter >= 1000) + { + add_sparkle(); + sparkle_spawn_counter -= 1000; + } + + sparkle_instance *s, *tmps; + list_for_each_entry_safe(s, tmps, &sparkle_list, list) + { + s->loc.x -= s->speed; + s->frame += s->frame_mov; + + if (s->frame + 1 >= sparkle_count || s->frame < 1){s->frame_mov = 0 - s->frame_mov;} + + if ((s->loc.x + sparkle_width) < 0) + { + list_del(&s->list); + free(s); + } + } + } + +void handle_sine() + { + cat_instance *c; + + list_for_each_entry(c, &cat_list, list) + { + double pos = (SCREEN_HEIGHT - cat_height)/2 * sin((2*PI*444444)*t); + c->loc.y = ((SCREEN_HEIGHT - cat_height)/2 - pos); + } + + t += 20; + } + +void draw_cats() + { + cat_instance* c; + + list_for_each_entry(c, &cat_list, list) + { + SDL_Rect srcrect = {cat_sprite * cat_width, 0, cat_width, cat_height}; + SDL_Rect dstrect = {c->loc.x, c->loc.y, cat_width*cat_size, cat_height*cat_size}; + + SDL_RenderCopy(renderer, cat_texture, &srcrect, &dstrect); + } + } + +void draw_rainbows() + { + rainbow_instance* r; + + list_for_each_entry(r, &rainbow_list, list) + { + SDL_Rect srcrect = {r->sprite * rainbow_width, 0, rainbow_width, rainbow_height}; + SDL_Rect dstrect = {r->loc.x, r->loc.y, rainbow_width*cat_size, rainbow_height*cat_size}; + + SDL_RenderCopy(renderer, rainbow_texture, &srcrect, &dstrect); + } + } + +void draw_sparkles() + { + sparkle_instance* s; + + list_for_each_entry(s, &sparkle_list, list) + { + SDL_Rect srcrect = {sparkle_sprite * sparkle_width, 0, sparkle_width, sparkle_height}; + SDL_Rect dstrect = {s->loc.x, s->loc.y, sparkle_width*cat_size, sparkle_height*cat_size}; + + SDL_RenderCopy(renderer, sparkle_texture, &srcrect, &dstrect); + } + } + +#endif diff --git a/globals.h b/globals.h new file mode 100644 index 0000000..65b8aaa --- /dev/null +++ b/globals.h @@ -0,0 +1,66 @@ +#ifndef GLOBALS_H +#define GLOBALS_H + +#define FRAMERATE 14 +#define BUF_SZ 1024 +/* The amount to offset the rainbow off the center of the cat */ +#define OFFSET 25 +#define PI 3.14159265 + +/* Type definitions */ +typedef struct { + int x, y; +} coords; + +typedef struct cat_instance cat_instance; +struct cat_instance + { + coords loc; + struct list_head list; + }; + +typedef struct rainbow_instance rainbow_instance; +struct rainbow_instance + { + coords loc; + unsigned sprite; + struct list_head list; + }; + +typedef struct sparkle_instance sparkle_instance; +struct sparkle_instance + { + unsigned int frame, speed; + int frame_mov; + unsigned int layer; + coords loc; + struct list_head list; + }; + +SDL_Event event; +bool running = true, sound = true, fullscreen = true, cursor = false, sine = false; +int sound_volume = 128, catsize = 0, sparkle_spawn_counter = 0; +Mix_Music *music; + +#define BASE_PATH "res" +char *cat_dir; +LIST_HEAD(sparkle_list); +LIST_HEAD(cat_list); +LIST_HEAD(rainbow_list); + +int cat_width, cat_height, rainbow_width, rainbow_height, sparkle_width, sparkle_height; +SDL_Renderer *renderer; +SDL_Texture *cat_texture, *rainbow_texture, *sparkle_texture; +uint32_t cat_sprite, rainbow_sprite, sparkle_sprite; +int cat_count, sparkle_count, rainbow_count; +int sparkle_pos; +/* For sine */ +unsigned t; + +unsigned cat_num = 1; +double cat_size = 1; + +unsigned SCREEN_WIDTH = 1200; +unsigned SCREEN_HEIGHT = 800; + +#endif diff --git a/list.h b/list.h index 8d7d0dd..233b7de 100644 --- a/list.h +++ b/list.h @@ -1,8 +1,8 @@ #ifndef __LIST_H #define __LIST_H -/* This file is from the Linux Kernel (include/linux/list.h) - * and modified by simply removing hardware prefetching of list items. +/* This file is from the Linux Kernel (include/linux/list.h) + * and modified by simply removing hardware prefetching of list items. * Copyright, credits attributed to wherever they belong. * Kulesh Shanmugasundaram (kulesh [squiggly] isis.poly.edu) * Modified to round out my personal collection by John Anthony diff --git a/nyan.c b/nyan.c index 05a71c5..91bcfe3 100644 --- a/nyan.c +++ b/nyan.c @@ -7,599 +7,401 @@ /* This program is licensed under the GPLv3 and in support of Free and Open Source */ /* Software in general. The full license can be found at http://www.gnu.org/licenses/gpl.html */ /* ============================================================================================ */ -#include -#include -#include +#include +#include +#include #include #include +#include #include #include -#ifdef XINERAMA -#include -#include -#endif /* XINERAMA */ +#include +#include +#include #include "list.h" /* Linked list implementation */ -#define BUF_SZ 1024 - -/* Type definitions */ -typedef struct { - int x, y; -} coords; - -typedef struct cat_instance cat_instance; -struct cat_instance { - coords loc; - struct list_head list; -}; - -typedef struct sparkle_instance sparkle_instance; -struct sparkle_instance { - unsigned int frame, speed; - int frame_mov; - unsigned int layer; - coords loc; - struct list_head list; -}; - /* Predecs */ -static void add_sparkle(void); -static void add_cat(unsigned int x, unsigned int y); -static void cleanup(void); -static void clear_screen(void); -static void draw_cats(unsigned int frame); -static void draw_sparkles(void); -static void* ec_malloc(unsigned int size); -static void errout(char *str); -static void fillsquare(SDL_Surface* surf, int x, int y, int w, int h, Uint32 col); -static void handle_args(int argc, char** argv); -static void handle_input(void); -static void init(void); -static void load_images(void); -static SDL_Surface* load_image(const char* path); -static void load_resource_data(void); -static void load_music(void); -static void putpix(SDL_Surface* surf, int x, int y, Uint32 col); -static void restart_music(void); -static void run(void); -static void stretch_images(void); -static void update_sparkles(void); -static void usage(char* exname); -#ifdef XINERAMA -static void xinerama_add_cats(void); -#endif /* XINERAMA */ - -/* Globals */ -static unsigned int FRAMERATE = 14; -static unsigned int SCREEN_BPP = 32; -static unsigned int SCREEN_WIDTH = 800; -static unsigned int SCREEN_HEIGHT = 600; -static SDL_Surface* screen = NULL; -static SDL_Event event; -static int running = 1; -static int SURF_TYPE = SDL_HWSURFACE; -static int sound = 1; -static int sound_volume = 128; -static int fullscreen = 1; -static int catsize = 0; -static int cursor = 0; -#ifdef XINERAMA -static Display* dpy; -#endif /* XINERAMA */ -static int curr_frame = 0; -static int sparkle_spawn_counter = 0; -static Mix_Music* music; -static SDL_Surface** cat_img; -static SDL_Surface** sparkle_img; -static SDL_Surface** stretch_cat; -static SDL_Surface** image_set; -static Uint32 bgcolor; -static char* RESOURCE_PATH = NULL; -static char* LOC_BASE_PATH = "res"; -static char* OS_BASE_PATH = "/usr/share/nyancat"; -static int ANIM_FRAMES_FG = 0; -static int ANIM_FRAMES_BG = 0; -static LIST_HEAD(sparkle_list); -static LIST_HEAD(cat_list); - -/* Function definitions */ -static void -add_sparkle(void) { - sparkle_instance* new; - - new = ec_malloc(sizeof(sparkle_instance)); - new->loc.x = screen->w + 80; - new->loc.y = (rand() % (screen->h + sparkle_img[0]->h)) - sparkle_img[0]->h; - new->frame = 0; - new->frame_mov = 1; - new->speed = 10 + (rand() % 30); - new->layer = rand() % 2; - list_add(&new->list, &sparkle_list); -} - -static void -add_cat(unsigned int x, unsigned int y) { - cat_instance* new; - - new = ec_malloc(sizeof(cat_instance)); - new->loc.x = x; - new->loc.y = y; - list_add(&new->list, &cat_list); -} - -static void -cleanup(void) { - Mix_HaltMusic(); - Mix_FreeMusic(music); - Mix_CloseAudio(); - SDL_Quit(); -} - -static void -clear_screen(void) { - sparkle_instance *s; - cat_instance *c; - - list_for_each_entry(c, &cat_list, list) { - /* This is bad. These magic numbers are to make up for uneven image sizes */ - fillsquare(screen, - c->loc.x, - c->loc.y - (curr_frame < 2 ? 0 : 5), - image_set[curr_frame]->w + 6, - image_set[curr_frame]->h + 5, - bgcolor); - } - - list_for_each_entry(s, &sparkle_list, list) { - fillsquare(screen, - s->loc.x, - s->loc.y, - sparkle_img[s->frame]->w, - sparkle_img[s->frame]->h, - bgcolor); - } - -} - -static void -draw_cats(unsigned int frame) { - cat_instance* c; - SDL_Rect pos; - - list_for_each_entry(c, &cat_list, list) { - pos.x = c->loc.x; - pos.y = c->loc.y; - - if(frame < 2) - pos.y -= 5; - SDL_BlitSurface( image_set[frame], NULL, screen, &pos ); - } +void *ec_malloc(unsigned int size); +void errout(char *str); +void load_images(); +void load_resource_data(); +void load_music(); +void restart_music(); +void usage(char *exname); + +#include "globals.h" +#include "draw.h" + +void cleanup(void) + { + Mix_HaltMusic(); + Mix_FreeMusic(music); + Mix_CloseAudio(); + + /* Free cats */ + cat_instance *c, *tmpc; + list_for_each_entry_safe(c, tmpc, &cat_list, list){list_del(&c->list); free(c);} + /* Free rainbows */ + rainbow_instance *r, *tmpr; + list_for_each_entry_safe(r, tmpr, &rainbow_list, list){list_del(&r->list); free(r);} + /* Free sparkles */ + sparkle_instance *s, *tmps; + list_for_each_entry_safe(s, tmps, &sparkle_list, list){list_del(&s->list); free(s);} + + SDL_DestroyRenderer(renderer); + SDL_Quit(); + } + +void *ec_malloc(unsigned int size) + { + void *ptr = malloc(size); + if (!ptr){errout("In ec_malloc -- unable to allocate memory.");} + + return ptr; + } + +void errout(char *str) + { + if (str){fprintf(stderr,"%s\n",str);} + exit(1); + } + + +void handle_args(int argc, char *argv[]) + { + int c, volume; + char *x; //position of x in resolution (e.g. 800x600) + + while(1) + { + static struct option long_options[] = + { + {"help", no_argument, 0, 'h'}, + {"fullscreen", no_argument, 0, 'f'}, + {"windowed", no_argument, 0, 'w'}, + {"catsize", required_argument, 0, 'c'}, + {"nocursor", no_argument, 0, 'a'}, + {"cursor", no_argument, 0, 'b'}, + {"nosound", no_argument, 0, 'e'}, + {"volume", required_argument, 0, 'v'}, + {"resolution", required_argument, 0, 'r'}, + {"data-set", required_argument, 0, 'd'}, + {"sine", no_argument, 0, 's'}, + {"count", required_argument, 0, 'n'}, + {0, 0, 0, 0} + }; + + /* getopt_long stores the option index here. */ + int option_index = 0; + c = getopt_long(argc, argv, "hfwc:abev:r:d:sn:", long_options, &option_index); + + /* Detect the end of the options. */ + if (c == -1){break;} + + switch (c) + { + case 'h': + usage(argv[0]); + break; + case 'f': + fullscreen = true; + break; + case 'w': + fullscreen = false; + break; + case 'c': + cat_size = atof(optarg); + if (cat_size < 0.1 || cat_size > 10) + { + fprintf(stderr,"Size of cat is invalid, set to default.\n"); + cat_size = 1; + } + break; + case 'a': + cursor = false; + break; + case 'b': + cursor = true; + break; + case 'e': + sound = false; + break; + case 'v': + volume = atoi(optarg); + if(volume >= 0 && volume <= 128){sound_volume = volume;} + else + { + fprintf(stderr,"Arguments for Volume are not valid. Disabling sound.\n"); + sound = false; + } + break; + + case 'r': + x = strchr(optarg,'x'); + if (!x){fprintf(stderr,"Argument for resolution is invalid, resolution set to default.\n"); break;} + *x = '\0'; + + if (*(x+1) == '\0'){fprintf(stderr,"Argument for resolution is invalid, resolution set to default.\n"); break;} + + int dims[2] = {atoi(optarg), atoi(x+1)}; + if (dims[0] >= 0 && dims[0] < 10000 && dims[1] >= 0 && dims[1] < 5000) + { + SCREEN_WIDTH = dims[0]; + SCREEN_HEIGHT = dims[1]; + } + else {fprintf(stderr,"Argument for resolution is invalid, resolution set to default.\n");} + break; + + case 'd': + if (cat_dir){free(cat_dir);} + cat_dir = strdup(optarg); + break; + + case 's': + sine = true; + break; + + case 'n': + cat_num = atoi(optarg); + if (cat_num <= 0 || cat_num > 10000) + { + fprintf(stderr,"Number of cats are invalid, set to 1.\n"); + cat_num = 1; + } + break; + + case '?': + /* getopt_long already printed an error message. */ + break; + + default: + exit(1); + } + } + if (!cat_dir){cat_dir = "default";} } -static void -draw_sparkles() { - sparkle_instance* s; - SDL_Rect pos; +void handle_input(void) + { + while(SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_KEYDOWN: + case SDL_QUIT: + running = false; + break; + } + } + } + +void init(void) + { + srand(time(NULL)); + + SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|IMG_INIT_PNG); + + SDL_Window *window; + if (fullscreen) + { + window = SDL_CreateWindow("Nyan cat", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP); + /* Set screen width and height to fullscreen resolution*/ + SDL_DisplayMode current; + if (SDL_GetCurrentDisplayMode(0, ¤t) != 0){errout("SDL could not get display mode for screen");} + SCREEN_WIDTH = current.w; + SCREEN_HEIGHT = current.h; + } + + else {window = SDL_CreateWindow("Nyan cat", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_MAXIMIZED);} + if (!cursor){SDL_ShowCursor(0);} + + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + + load_images(); + load_resource_data(); + + /* Divide the *_width by *_count, so we don't need to calculate width/count every time */ + cat_width /= cat_count; + rainbow_width /= rainbow_count; + sparkle_width /= sparkle_count; + + SDL_SetRenderDrawColor(renderer, 0x00, 0x33, 0x66, 255); + SDL_RenderClear(renderer); + + if (sound) + { + Mix_OpenAudio(44100, AUDIO_S16, 2, 256); + load_music(); + Mix_PlayMusic(music, 0); + Mix_VolumeMusic(sound_volume); + } + + if (cat_num == 1) + { + add_cat((SCREEN_WIDTH - cat_width) / 2, (SCREEN_HEIGHT - cat_height)/2); + add_rainbow((SCREEN_WIDTH - rainbow_width) / 2 - OFFSET, (SCREEN_HEIGHT - rainbow_height) / 2); + } + else + { + for (int i = 0; i < cat_num; ++i) + { + unsigned x = rand() % (SCREEN_WIDTH - cat_width); + unsigned y = rand() % (SCREEN_HEIGHT - cat_height); + add_cat(x,y); + add_rainbow(x - OFFSET, y); + } + } + + + /* clear initial input */ + while(SDL_PollEvent(&event)); + + /* Pre-populate with sparkles */ + for (int i = 0; i < 200; ++i){update_sparkles();} - list_for_each_entry(s, &sparkle_list, list) { - pos.x = s->loc.x; - pos.y = s->loc.y; - SDL_BlitSurface( sparkle_img[s->frame], NULL, screen, &pos ); - } } -static void* -ec_malloc(unsigned int size) { - void *ptr; - ptr = malloc(size); - if (!ptr) - errout("In ec_malloc -- unable to allocate memory."); - return ptr; -} +SDL_Surface* load_image(const char* path) + { + SDL_Surface* loadedImage = NULL; + SDL_Surface* optimizedImage = NULL; -static void -errout (char *str) { - if (str) - puts(str); - exit(-1); -} + loadedImage = IMG_Load(path); + if(loadedImage) + { + SDL_PixelFormat *format = SDL_AllocFormat(SDL_PIXELFORMAT_RGBA32); + optimizedImage = SDL_ConvertSurface(loadedImage, format, 0); + SDL_FreeSurface(loadedImage); + } -static void -fillsquare(SDL_Surface* surf, int x, int y, int w, int h, Uint32 col) { - int i, e; - - if (x + w < 0 || y + h < 0 || x > surf->w || y > surf->h) - return; - - /* Sanitising of inputs. Make sure we're not drawing off of the surface */ - if (x + w > surf->w) - w = surf->w - x; - if (y + h > surf->h) - h = surf->h - y; - if (x < 0) { - w += x; - x = 0; - } - if (y < 0) { - h += y; - y = 0; - } - - for (i = x; i < x + w; i++) - for (e = y; e < y + h; e++) - putpix(surf, i, e, col); -} + return optimizedImage; + } -static void -handle_args(int argc, char **argv) { - int i; - - /* This REALLY needs to be replaced with getopt */ - - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-hw")) - SURF_TYPE = SDL_HWSURFACE; - else if (!strcmp(argv[i], "-sw")) - SURF_TYPE = SDL_SWSURFACE; - else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--fullscreen")) - fullscreen = 1; - else if(!(strcmp(argv[i], "-nf") || !strcmp(argv[i], "--nofullscreen"))) - fullscreen = 0; - else if(!strcmp(argv[i], "-nc") || !strcmp(argv[i], "--nocursor")) - cursor = 0; - else if(!strcmp(argv[i], "-sc") || !strcmp(argv[i], "--cursor") || !strcmp(argv[i], "--showcursor")) - cursor = 1; - else if(!strcmp(argv[i], "-ns") || !strcmp(argv[i], "--nosound")) - sound = 0; - else if((!strcmp(argv[i], "-v") || !strcmp(argv[i], "--volume")) && i < argc - 1) { - int vol = atoi(argv[++i]); - if(vol >= 0 && vol <= 128){ - sound_volume = vol; - } - else { - puts("Arguments for Volume are not valid. Disabling sound."); - sound = 0; - } - } - else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) - usage(argv[0]); - else if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "--catsize")) { - if (++i < argc) { - if(!strcmp(argv[i], "full")) - catsize = 1; - else if(!strcmp(argv[i], "small")) - catsize = 0; - else - printf("Unrecognised scaling option: %s - please select either 'full' or 'small' cat size.\n", argv[i]); - } - } - else if(!strcmp(argv[i], "-d") || !strcmp(argv[i], "--data-set")) { - if (++i < argc) { - if (RESOURCE_PATH) - free(RESOURCE_PATH); - RESOURCE_PATH = strdup(argv[i]); - } - } - else if((!strcmp(argv[i], "-r") && strcmp(argv[i], "--resolution")) && i < argc - 2) { - int dims[2] = { atoi(argv[++i]), atoi(argv[++i]) }; - if (dims[0] >= 0 && dims[0] < 10000 && dims[1] >= 0 && dims[1] < 5000) { // Borrowed from PixelUnsticker, changed the variable name - SCREEN_WIDTH = dims[0]; - SCREEN_HEIGHT = dims[1]; - } - else - puts("Arguments do not appear to be valid screen sizes. Defaulting."); - } - else - printf("Unrecognised option: %s\n", argv[i]); - } - - if (!RESOURCE_PATH) - RESOURCE_PATH = "default"; -} +void load_images() + { + char buffer[BUF_SZ]; + SDL_Surface *cat_img, *rainbow_img, *sparkle_img; -static void -handle_input(void) { - while( SDL_PollEvent( &event ) ) { - switch (event.type) { - case SDL_KEYDOWN: - case SDL_QUIT: - case SDL_MOUSEMOTION: - running = 0; - break; - } - } -} - -static void -init(void) { - int i; - - srand( time(NULL) ); - - SDL_Init( SDL_INIT_EVERYTHING ); - if (fullscreen) - screen = SDL_SetVideoMode( 0, 0, SCREEN_BPP, SURF_TYPE | SDL_FULLSCREEN ); - else - screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SURF_TYPE ); - if(!cursor) - SDL_ShowCursor(0); - - load_resource_data(); - load_images(); - bgcolor = SDL_MapRGB(screen->format, 0x00, 0x33, 0x66); - fillsquare(screen, 0, 0, screen->w, screen->h, bgcolor); - - if(sound) { - Mix_OpenAudio( 44100, AUDIO_S16, 2, 256 ); - load_music(); - Mix_PlayMusic(music, 0); - Mix_VolumeMusic(sound_volume); - } - - /* Choose our image set */ - if (catsize == 1) - image_set = stretch_cat; - else - image_set = cat_img; - -/* Ugly */ -#ifdef XINERAMA - if (!(dpy = XOpenDisplay(NULL))) - puts("Failed to open Xinerama display information."); - else{ - if(catsize == 1) - stretch_images(); - xinerama_add_cats(); - XCloseDisplay(dpy); - } -#else - if(catsize == 1) - add_cat(0, (screen->h - image_set[0]->h) / 2); - else { - add_cat((screen->w - cat_img[0]->w) / 2, (screen->h - cat_img[0]->h) / 2); - } -#endif /* Xinerama */ - - /* clear initial input */ - while( SDL_PollEvent( &event ) ) {} - - /* Pre-populate with sparkles */ - for (i = 0; i < 200; i++) - update_sparkles(); -} + /* Load images */ + snprintf(buffer, BUF_SZ, "%s/%s/cat.png", BASE_PATH, cat_dir); + cat_img = load_image(buffer); -static void -load_images(void) { - int i; - char buffer[BUF_SZ]; - - cat_img = ec_malloc(sizeof(SDL_Surface*) * ANIM_FRAMES_FG); - sparkle_img = ec_malloc(sizeof(SDL_Surface*) * ANIM_FRAMES_BG); - - /* Loading logic */ - for (i = 0; i < ANIM_FRAMES_FG; ++i) { - snprintf(buffer, BUF_SZ, "%s/%s/fg%02d.png", LOC_BASE_PATH, RESOURCE_PATH, i); - cat_img[i] = load_image(buffer); - if (!cat_img[i]) { - snprintf(buffer, BUF_SZ, "%s/%s/fg%02d.png", OS_BASE_PATH, RESOURCE_PATH, i); - cat_img[i] = load_image(buffer); - } - } - for (i = 0; i < ANIM_FRAMES_BG; ++i) { - snprintf(buffer, BUF_SZ, "%s/%s/bg%02d.png", LOC_BASE_PATH, RESOURCE_PATH, i); - sparkle_img[i] = load_image(buffer); - if (!sparkle_img[i]) { - snprintf(buffer, BUF_SZ, "%s/%s/bg%02d.png", OS_BASE_PATH, RESOURCE_PATH, i); - sparkle_img[i] = load_image(buffer); - } - } - - /* Check everything loaded properly */ - for (int i = 0; i < ANIM_FRAMES_FG; ++i) - if (!cat_img[i]) - errout("Error loading foreground images."); - - for (int i = 0; i < ANIM_FRAMES_BG; ++i) - if (!sparkle_img[i]) - errout("Error loading background images."); -} - -static SDL_Surface* -load_image( const char* path ) { - SDL_Surface* loadedImage = NULL; - SDL_Surface* optimizedImage = NULL; - - loadedImage = IMG_Load( path ); - if(loadedImage) { - optimizedImage = SDL_DisplayFormatAlpha( loadedImage ); - SDL_FreeSurface( loadedImage ); - } - return optimizedImage; -} - -static void -load_music(void) { - char buffer[BUF_SZ]; - - snprintf(buffer, BUF_SZ, "%s/%s/music.ogg", LOC_BASE_PATH, RESOURCE_PATH); - music = Mix_LoadMUS(buffer); - if (!music) { - snprintf(buffer, BUF_SZ, "%s/%s/music.ogg", OS_BASE_PATH, RESOURCE_PATH); - music = Mix_LoadMUS(buffer); - } - if (!music) - printf("Unable to load Ogg file: %s\n", Mix_GetError()); - else - Mix_HookMusicFinished(restart_music); -} - -static void -load_resource_data(void) { - FILE *f; - char buffer[BUF_SZ]; - - snprintf(buffer, BUF_SZ, "%s/%s/data", LOC_BASE_PATH, RESOURCE_PATH); - f = fopen(buffer, "r"); - if (!f) { - snprintf(buffer, BUF_SZ, "%s/%s/data", OS_BASE_PATH, RESOURCE_PATH); - f = fopen(buffer, "r"); - } - if (!f) - errout("Error opening resource data file"); - - ANIM_FRAMES_FG = atoi(fgets(buffer, BUF_SZ, f)); - ANIM_FRAMES_BG = atoi(fgets(buffer, BUF_SZ, f)); - - if (!ANIM_FRAMES_FG || !ANIM_FRAMES_BG) - errout("Error reading resource data file."); -} - -static void -putpix(SDL_Surface* surf, int x, int y, Uint32 col) { - Uint32 *pix = (Uint32 *) surf->pixels; - pix [ ( y * surf->w ) + x ] = col; -} - -static void -restart_music(void) { - Mix_PlayMusic(music, 0); -} - -static void -run(void) { - unsigned int last_draw, draw_time; - - while( running ) { - last_draw = SDL_GetTicks(); - - clear_screen(); - update_sparkles(); - draw_sparkles(); - draw_cats(curr_frame); - - handle_input(); - SDL_Flip(screen); - - /* Frame increment and looping */ - curr_frame++; - if (curr_frame >= ANIM_FRAMES_FG) - curr_frame = 0; - - draw_time = SDL_GetTicks() - last_draw; - if (draw_time < (1000 / FRAMERATE)) - SDL_Delay((1000 / FRAMERATE) - draw_time); - } -} - -static void -stretch_images(void) { - SDL_Rect stretchto; - stretchto.w = 0; - stretchto.h = 0; - - /* Just use the x co-ordinate for scaling for now. This does, however, - need to be changed to accomodate taller resolutions */ -#ifdef XINERAMA - int i, nn; - XineramaScreenInfo* info = XineramaQueryScreens(dpy, &nn); - - for (i = 0; i < nn; ++i) { - if(!stretchto.w || info[i].width < stretchto.w) - stretchto.w = info[i].width; - } - - XFree(info); -#endif /* XINERAMA */ - if (!stretchto.w) - stretchto.w = screen->w; - - /* Handle a slight scaling down */ - stretchto.w *= 0.9; - stretchto.h = stretchto.w * cat_img[0]->h / cat_img[0]->w; - - SDL_PixelFormat fmt = *(cat_img[0]->format); - for (int i=0; i <= ANIM_FRAMES_FG; i++) { - stretch_cat[i] = SDL_CreateRGBSurface(SURF_TYPE, stretchto.w, - stretchto.h,SCREEN_BPP,fmt.Rmask,fmt.Gmask,fmt.Bmask,fmt.Amask); - SDL_SoftStretch(cat_img[i],NULL,stretch_cat[i],NULL); - } - -} - -static void -update_sparkles(void) { - sparkle_instance *s; - sparkle_instance *tmp; - - sparkle_spawn_counter += rand() % screen->h; - while(sparkle_spawn_counter >= 1000) { - add_sparkle(); - sparkle_spawn_counter -= 1000; - } - - list_for_each_entry_safe(s, tmp, &sparkle_list, list) { - s->loc.x -= s->speed; - s->frame += s->frame_mov; - - if(s->frame + 1 >= ANIM_FRAMES_BG || s->frame < 1) - s->frame_mov = 0 - s->frame_mov; - - if (s->loc.x < 0 - sparkle_img[0]->w) { - list_del(&s->list); - free(s); - } - } -} - -static void -usage(char* exname) { - printf("Usage: %s [OPTIONS]\n\ - -h, --help This help message\n\ - -f, --fullscreen Enable fullscreen mode (default)\n\ - -nf, --nofullscreen Disable fullscreen mode\n\ - -c, --catsize Choose size of cat, options are full and \n\ - small. Small is default. \"Full\" not\n\ - officially supported.\n\ - -nc, --nocursor Don't show the cursor (default)\n\ - -sc, --cursor, --showcursor Show the cursor\n\ - -ns, --nosound Don't play sound\n\ - -v, --volume Set Volume, if enabled, from 0 - 128\n\ - -r, --resolution Make next two arguments the screen \n\ - resolution to use (0 and 0 for full \n\ - resolution) (800x600 default)\n\ - -d, --data-set Use an alternate data set. Packaged with\n\ - this program by default are \"default\"\n\ - and \"freedom\" sets.\n\ - -hw, -sw Use hardware or software SDL rendering, \n\ - respectively. Hardware is default\n", exname); - exit(0); -} - -#ifdef XINERAMA -static void -xinerama_add_cats(void) { - int i, nn; - XineramaScreenInfo* info = XineramaQueryScreens(dpy, &nn); - - for (i = 0; i < nn; ++i) { - if(fullscreen) { - add_cat(info[i].x_org + ((info[i].width - image_set[0]->w) / 2), - info[i].y_org + ((info[i].height - image_set[0]->h) / 2)); - } - else { - add_cat((SCREEN_WIDTH - image_set[0]->w) / 2, - (SCREEN_HEIGHT - image_set[0]->h) / 2); - } - } - - XFree(info); -} -#endif /* XINERAMA */ - -int main( int argc, char **argv ) { - handle_args(argc, argv); - init(); - run(); - cleanup(); - return 0; -} + snprintf(buffer, BUF_SZ, "%s/%s/rainbow.png", BASE_PATH, cat_dir); + rainbow_img = load_image(buffer); + + + snprintf(buffer, BUF_SZ, "%s/%s/sparkle.png", BASE_PATH, cat_dir); + sparkle_img = load_image(buffer); + + /* Check everything loaded properly */ + if (!cat_img||!rainbow_img||!sparkle_img){errout("Error loading images.");} + + /* Create textures from images */ + cat_texture = SDL_CreateTextureFromSurface(renderer, cat_img); + rainbow_texture = SDL_CreateTextureFromSurface(renderer, rainbow_img); + sparkle_texture = SDL_CreateTextureFromSurface(renderer, sparkle_img); + + /* Get width and height from all the textures */ + SDL_QueryTexture(cat_texture, NULL, NULL, &cat_width, &cat_height); + SDL_QueryTexture(rainbow_texture, NULL, NULL, &rainbow_width, &rainbow_height); + SDL_QueryTexture(sparkle_texture, NULL, NULL, &sparkle_width, &sparkle_height); + } + +void load_music() + { + char buffer[BUF_SZ]; + + snprintf(buffer, BUF_SZ, "%s/%s/music.ogg", BASE_PATH, cat_dir); + music = Mix_LoadMUS(buffer); + + if (!music){fprintf(stderr,"Unable to load Ogg file: %s\n", Mix_GetError());} + else {Mix_HookMusicFinished(restart_music);} + } + +void load_resource_data() + { + char buffer[BUF_SZ]; + + snprintf(buffer, BUF_SZ, "%s/%s/data", BASE_PATH, cat_dir); + FILE *fp = fopen(buffer, "r"); + + if (!fp){errout("Error opening resource data file");} + + cat_count = atoi(fgets(buffer, BUF_SZ, fp)); + rainbow_count = atoi(fgets(buffer, BUF_SZ, fp)); + sparkle_count = atoi(fgets(buffer, BUF_SZ, fp)); + + if (!cat_count||!rainbow_count||!sparkle_count){errout("Error reading resource data file.");} + + fclose(fp); + } + +void restart_music() + { + Mix_PlayMusic(music, 0); + } + +void run() + { + unsigned int last_draw, draw_time; + + while(running) + { + last_draw = SDL_GetTicks(); + + /* It's faster to just clear the whole renderer rather than individually overwriting each sprite */ + SDL_RenderClear(renderer); + + update_sparkles(); + update_rainbows(); + draw_sparkles(); + draw_rainbows(); + draw_cats(); + + handle_input(); + + if (sine){handle_sine();} + + /* Display the frame */ + SDL_RenderPresent(renderer); + + /* Animation sequence increment and looping */ + if (++cat_sprite >= cat_count){cat_sprite = 0;} + if (++rainbow_sprite >= rainbow_count){rainbow_sprite = 0;} + if (++sparkle_sprite >= sparkle_count){sparkle_sprite = 0;} + + /* Avoid wasting CPU & GPU by only drawing at the framerate */ + draw_time = SDL_GetTicks() - last_draw; + if (draw_time < (1000 / FRAMERATE)){SDL_Delay((1000 / FRAMERATE) - draw_time);} + } + } + +void usage(char* exname) + { + fprintf(stderr,"Usage: %s [OPTIONS]\n\ + -h, --help This help message.\n\ + -f, --fullscreen Enable fullscreen (default).\n\ + -w, --windowed Disable fullscreen.\n\ + -c, --catsize Choose size to scale cat to (between 0.1 to 10.0)\n\ + -a, --nocursor Don't show the cursor (default).\n\ + -b, --cursor Show the cursor.\n\ + -e, --nosound Don't play sound.\n\ + -v, --volume Set Volume, if enabled, from 0 - 128.\n\ + -r, --resolution Argument format: x (1200x800 default).\n\ + -d, --data-set Use an alternate data set. Packaged with\n\ + this program by default are \"default\"\n\ + and \"freedom\" sets.\n\ + -s, --sine Make cat move in a sine wave.\n\ + -n, --count Number of cats to spawn.\n", exname); + + exit(0); + } + +int main(int argc, char *argv[]) + { + handle_args(argc, argv); + init(); + run(); + cleanup(); + return 0; + } diff --git a/res/default/cat.png b/res/default/cat.png new file mode 100644 index 0000000..5d61f67 Binary files /dev/null and b/res/default/cat.png differ diff --git a/res/default/data b/res/default/data index fd3c81a..63fd91f 100644 --- a/res/default/data +++ b/res/default/data @@ -1,2 +1,3 @@ 5 +2 5 diff --git a/res/default/nyan.xcf b/res/default/nyan.xcf new file mode 100644 index 0000000..6addb48 Binary files /dev/null and b/res/default/nyan.xcf differ diff --git a/res/default/rainbow.png b/res/default/rainbow.png new file mode 100644 index 0000000..2866ff2 Binary files /dev/null and b/res/default/rainbow.png differ diff --git a/res/default/rainbow.xcf b/res/default/rainbow.xcf new file mode 100644 index 0000000..1b0ea7a Binary files /dev/null and b/res/default/rainbow.xcf differ diff --git a/res/default/sparkle.png b/res/default/sparkle.png new file mode 100644 index 0000000..a2cb2ac Binary files /dev/null and b/res/default/sparkle.png differ diff --git a/res/default/sparkle.xcf b/res/default/sparkle.xcf new file mode 100644 index 0000000..51ec74e Binary files /dev/null and b/res/default/sparkle.xcf differ diff --git a/res/freedom/cat.png b/res/freedom/cat.png new file mode 100644 index 0000000..da75800 Binary files /dev/null and b/res/freedom/cat.png differ diff --git a/res/freedom/cat.xcf b/res/freedom/cat.xcf new file mode 100644 index 0000000..d4ccc3d Binary files /dev/null and b/res/freedom/cat.xcf differ diff --git a/res/freedom/data b/res/freedom/data index 7290ba8..c46162e 100644 --- a/res/freedom/data +++ b/res/freedom/data @@ -1,2 +1,3 @@ 4 +2 4 diff --git a/res/freedom/rainbow.png b/res/freedom/rainbow.png new file mode 100644 index 0000000..58f1591 Binary files /dev/null and b/res/freedom/rainbow.png differ diff --git a/res/freedom/rainbow.xcf b/res/freedom/rainbow.xcf new file mode 100644 index 0000000..34a751f Binary files /dev/null and b/res/freedom/rainbow.xcf differ diff --git a/res/freedom/sparkle.png b/res/freedom/sparkle.png new file mode 100644 index 0000000..3ba85df Binary files /dev/null and b/res/freedom/sparkle.png differ diff --git a/res/freedom/sparkle.xcf b/res/freedom/sparkle.xcf new file mode 100644 index 0000000..b4ead3c Binary files /dev/null and b/res/freedom/sparkle.xcf differ -- cgit v1.2.3