summaryrefslogtreecommitdiffstats
path: root/nyan.c
diff options
context:
space:
mode:
authorDiffieHellman <DiffieHellman@endianness.com>2024-04-30 00:45:48 +1000
committerDiffieHellman <DiffieHellman@endianness.com>2024-04-30 00:52:03 +1000
commit87f05f8d0dff3688da35af625cb2bd66aa0d52d2 (patch)
tree732c882af3fd143f6f4169a4ea1d3db9513ba594 /nyan.c
parent976183462c22191a1ef6f91ccd7eae084ffd0a81 (diff)
downloadnyancat-master.tar.gz
nyancat-master.tar.bz2
nyancat-master.zip
Restructed source into indididual object files, cleaned up licensing headers adding correct copyright years, added licenses, added malformed input handling and added bouncing cats featureHEADmaster
new file: LICENSE-AGPLv3 new file: LICENSE-GPLv3 modified: Makefile modified: README deleted: draw.h new file: globals.c modified: globals.h new file: main.c new file: main.h modified: nyan.c new file: nyan.h deleted: res/default/bg00.png deleted: res/default/bg01.png deleted: res/default/bg02.png deleted: res/default/bg03.png deleted: res/default/bg04.png deleted: res/default/fg00.png deleted: res/default/fg01.png deleted: res/default/fg02.png deleted: res/default/fg03.png deleted: res/default/fg04.png
Diffstat (limited to 'nyan.c')
-rw-r--r--nyan.c673
1 files changed, 199 insertions, 474 deletions
diff --git a/nyan.c b/nyan.c
index 36b252a..ca5f1f2 100644
--- a/nyan.c
+++ b/nyan.c
@@ -1,474 +1,199 @@
-/* ============================================================================================ */
-/* This software is created by John Anthony and comes with no warranty of any kind. */
-/* */
-/* If you like this software and would like to contribute to its continued improvement */
-/* then please feel free to submit bug reports here: www.github.com/JohnAnthony */
-/* */
-/* This program is licensed under the GPLv3 and is Free Software */
-/* The full license can be found at http://www.gnu.org/licenses/gpl.html */
-/* ============================================================================================ */
-#include <SDL2/SDL.h>
-#include <SDL2/SDL_image.h>
-#include <SDL2/SDL_mixer.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <time.h>
-#include <string.h>
-#include <getopt.h>
-#include <stdint.h>
-#include <math.h>
-#include <bsd/sys/queue.h> // linked list implementation
-
-#include <stdio.h>
-#include <X11/extensions/Xrandr.h>
-
-/* Predecs */
-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 get_screen_info()
-{
-int iscres, icrtc;
-
-Display *disp = XOpenDisplay(0);
-XRRScreenResources *screen = XRRGetScreenResources(disp, DefaultRootWindow(disp));
-for (iscres = screen->noutput; iscres > 0; )
- {
- --iscres;
-
- XRROutputInfo *info = XRRGetOutputInfo (disp, screen, screen->outputs[iscres]);
- if (info->connection == RR_Connected)
- {
- for (icrtc = info->ncrtc; icrtc > 0;)
- {
- --icrtc;
-
- XRRCrtcInfo *crtc_info = XRRGetCrtcInfo (disp, screen, screen->crtcs[icrtc]);
- fprintf(stderr, "==> %dx%d+%dx%d\n", crtc_info->x, crtc_info->y, crtc_info->width, crtc_info->height);
-
- XRRFreeCrtcInfo(crtc_info);
- }
- }
- XRRFreeOutputInfo (info);
- }
- XRRFreeScreenResources(screen);
-}
-
-
-
-
-
-
-void cleanup(void)
- {
- Mix_HaltMusic();
- Mix_FreeMusic(music);
- Mix_CloseAudio();
-
- /* free cats */
- cat_instance *c, *ctmp;
- LIST_FOREACH_SAFE(c, &cat_list, entries, ctmp)
- {
- LIST_REMOVE(c, entries);
- free(c);
- }
-
- /* free rainbows */
- rainbow_instance *r, *rtmp;
- LIST_FOREACH_SAFE(r, &rainbow_list, entries, rtmp)
- {
- LIST_REMOVE(r, entries);
- free(r);
- }
-
-
- /* free sparkles */
- sparkle_instance *s, *stmp;
- LIST_FOREACH_SAFE(s, &sparkle_list, entries, stmp)
- {
- LIST_REMOVE(s, entries);
- 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'},
- {"version", no_argument, 0, 'V'},
- {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:V", 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 'V':
- printf("Copyright (C) 2020 John Antony, 2022 DiffieHellman\n"
- "License GPLv3: GNU GPL version 3 <http://gnu.org/licenses/gpl.html>\n\n"
- "This is free software; you are free to change and redistribute it.\n"
- "There is NO WARRANTY, to the extent permitted by law.\n");
- exit(0);
-
- case '?':
- /* getopt_long already printed an error message. */
- break;
-
- default:
- exit(1);
- }
- }
- if (!cat_dir){cat_dir = "default";}
-}
-
-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, &current) != 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);
- }
-
- /* init the linked lists */
- LIST_INIT(&cat_list);
- LIST_INIT(&rainbow_list);
- LIST_INIT(&sparkle_list);
-
- 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();}
-
-}
-
-SDL_Surface* load_image(const char* path)
- {
- SDL_Surface* loadedImage = NULL;
- SDL_Surface* optimizedImage = NULL;
-
- loadedImage = IMG_Load(path);
- if(loadedImage)
- {
- SDL_PixelFormat *format = SDL_AllocFormat(SDL_PIXELFORMAT_RGBA32);
- optimizedImage = SDL_ConvertSurface(loadedImage, format, 0);
- SDL_FreeSurface(loadedImage);
- }
-
- return optimizedImage;
- }
-
-void load_images()
- {
- char buffer[BUF_SZ];
- SDL_Surface *cat_img, *rainbow_img, *sparkle_img;
-
- /* Load images */
- snprintf(buffer, BUF_SZ, "%s/%s/cat.png", BASE_PATH, cat_dir);
- cat_img = load_image(buffer);
-
- 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 or parsing 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: <width>x<height> (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\
- -V, --version Version and copyright information.\n", exname);
-
- exit(0);
- }
-
-int main(int argc, char *argv[])
- {
-// get_screen_info();
-// exit(1);
- handle_args(argc, argv);
- init();
- run();
- cleanup();
- return 0;
- }
+ /* nyancat in GNU C
+ Copyright © 2024 DiffieHellman
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_image.h>
+#include <SDL2/SDL_mixer.h>
+#include <stdbool.h>
+
+#include "nyan.h"
+#include <bsd/sys/queue.h> // linked list implementation
+#include "main.h"
+
+void add_sparkle(struct head_sparkle *sparkle_list)
+ {
+ sparkle_instance *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_INSERT_HEAD(sparkle_list, new, entries);
+ }
+
+void add_cat(unsigned int x, unsigned int y, int bx, int by, struct head_cat *cat_list)
+ {
+ cat_instance *new = ec_malloc(sizeof(cat_instance));
+
+ new->loc.x = x;
+ new->loc.y = y;
+
+ new->bounce_direction.x = bx;
+ new->bounce_direction.y = by;
+
+ LIST_INSERT_HEAD(cat_list, new, entries);
+ }
+
+void add_rainbow(unsigned int x, unsigned int y, struct head_rainbow *rainbow_list)
+ {
+ rainbow_instance *new = ec_malloc(sizeof(rainbow_instance));
+
+ new->loc.x = x;
+ new->loc.y = y;
+ new->sprite = rainbow_sprite;
+
+ LIST_INSERT_HEAD(rainbow_list, new, entries);
+ }
+
+void update_rainbows(struct head_cat *cat_list, struct head_rainbow *rainbow_list)
+ {
+ cat_instance *c;
+
+ /* rainbows need to be placed according to the cat position (which changes constantly) */
+ LIST_FOREACH(c, cat_list, entries)
+ {
+ 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), rainbow_list);/* Plus the amount the cat is off the center */
+ }
+
+
+ rainbow_instance *r, *rtmp;
+
+ /* update the position of each rainbow left */
+ LIST_FOREACH_SAFE(r, rainbow_list, entries, rtmp)
+ {
+ r->loc.x -= rainbow_width;
+
+ /* if rainbow is off screen, delete and free() it */
+ if ((r->loc.x + rainbow_width) < 0)
+ {
+ LIST_REMOVE(r, entries);
+ free(r);
+ }
+ }
+ }
+
+void update_sparkles(struct head_sparkle *sparkle_list)
+ {
+
+ /* ensure screen has enough sparkles at all times */
+ sparkle_spawn_counter += rand() % screen_height;
+ while (sparkle_spawn_counter >= 1000)
+ {
+ add_sparkle(sparkle_list);
+ sparkle_spawn_counter -= 1000;
+ }
+
+ sparkle_instance *s, *stmp;
+ LIST_FOREACH_SAFE(s, sparkle_list, entries, stmp)
+ {
+ /* move sparkle left */
+ s->loc.x -= s->speed;
+ s->frame += s->frame_mov;
+
+ /* reset sparkle frame */
+ if (s->frame + 1 >= sparkle_count || s->frame < 1){s->frame_mov = 0 - s->frame_mov;}
+
+ /* if sparkle offscreen, delete it */
+ if ((s->loc.x + sparkle_width) < 0)
+ {
+ LIST_REMOVE(s, entries);
+ free(s);
+ }
+ }
+ }
+
+void handle_sine(struct head_cat *cat_list)
+ {
+ cat_instance *c;
+
+ LIST_FOREACH(c, cat_list, entries)
+ {
+ /* sine magic */
+ double pos = (screen_height - cat_height)/2 * sin((2*PI*444444)*t);
+ c->loc.y = ((screen_height - cat_height)/2 - pos);
+
+// double posx = (screen_width - cat_height)/2 * sin((2*PI*444444)*t);
+// c->loc.x = ((screen_width - cat_height)/2 - posx);
+ }
+
+ t += 20;
+ }
+
+void handle_bounce(struct head_cat *cat_list)
+ {
+ cat_instance *c;
+
+ LIST_FOREACH(c, cat_list, entries)
+ {
+ /* bounce magic */
+ if (c->loc.x < 0 || c->loc.x > (screen_width-cat_width)){c->bounce_direction.x = -(c->bounce_direction.x);}
+ if (c->loc.y < 0 || c->loc.y > (screen_height-(cat_width/2))){c->bounce_direction.y = -(c->bounce_direction.y);}
+ c->loc.x += (c->bounce_direction.x * (screen_width/64));
+ c->loc.y += (c->bounce_direction.y * (screen_height/64));
+ }
+ }
+
+void draw_cats(struct head_cat *cat_list)
+ {
+ cat_instance* c;
+
+ LIST_FOREACH(c, cat_list, entries)
+ {
+ /* source sprite dimensions */
+ SDL_Rect srcrect = {cat_sprite * cat_width, 0, cat_width, cat_height};
+ /* dimensions of destination rectangle */
+ 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(struct head_rainbow *rainbow_list)
+ {
+ rainbow_instance* r;
+
+ LIST_FOREACH(r, rainbow_list, entries)
+ {
+ /* source sprite dimensions */
+ SDL_Rect srcrect = {r->sprite * rainbow_width, 0, rainbow_width, rainbow_height};
+ /* dimensions of destination rectangle */
+ 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(struct head_sparkle *sparkle_list)
+ {
+ sparkle_instance* s;
+
+ LIST_FOREACH(s, sparkle_list, entries)
+ {
+ /* source sprite dimensions */
+ SDL_Rect srcrect = {sparkle_sprite * sparkle_width, 0, sparkle_width, sparkle_height};
+ /* dimensions of destination rectangle */
+ SDL_Rect dstrect = {s->loc.x, s->loc.y, sparkle_width*cat_size, sparkle_height*cat_size};
+
+ SDL_RenderCopy(renderer, sparkle_texture, &srcrect, &dstrect);
+ }
+ }