Add branding.
[jody.git] / src / main.c
blob15e8633a26ebbcf6941bc54b34cc1f1dd1493d54
1 #include <SDL.h>
2 #include <SDL_image.h>
3 #include <string.h>
4 #include <time.h>
5 #include <math.h>
6 #include <assert.h>
7 #include <stdbool.h>
9 #include "gamedefs.h"
10 #include "cursor.h"
11 #include "util.h"
12 #include "surface.h"
13 #include "sprite.h"
14 #include "datadir.h"
16 /* macros */
17 #define STREQ(a, b) (!strcmp((a),(b)))
19 /* constants */
20 #define SPRITE_PIXELS_PER_SECOND 200
21 #define NUM_MILLISECONDS 1000;
22 #define X_DIST 25
23 #define Y_DIST 1
25 /* global variables */
27 /* TODO no global variables
28 Have a type in each file?
29 Then use that type to decide if it can be branded?
30 Or only be able to brand cows.
33 enum image_idx {
34 IMG_CLOUD,
35 IMG_COW_BLACK,
36 IMG_COW_BROWN,
37 IMG_COW_ORANGE,
38 IMG_COW_RED,
39 IMG_COW_WHITE,
40 IMG_COW_YELLOW,
41 IMG_COW_YELLOW_WITH_RED_OUTLINE,
42 IMG_KANGAROO_BLACK,
43 IMG_KANGAROO_GREY,
44 IMG_KANGAROO_RED,
45 NUM_IMAGES
48 const char *image_names[NUM_IMAGES] = {
49 IMG_DIR "cloud",
50 IMG_DIR "cow_black",
51 IMG_DIR "cow_brown",
52 IMG_DIR "cow_orange",
53 IMG_DIR "cow_red",
54 IMG_DIR "cow_white",
55 IMG_DIR "cow_yellow",
56 IMG_DIR "cow_yellow_with_red_outline",
57 IMG_DIR "kangaroo_black",
58 IMG_DIR "kangaroo_grey",
59 IMG_DIR "kangaroo_red"
62 struct cursors
64 SDL_Cursor *cursor_arrow;
65 SDL_Cursor *cursor_wheelhouse;
66 SDL_Cursor *cursor_wheelhouse_transparent;
67 SDL_Cursor *cursor_wheelhouse_inverted;
68 SDL_Cursor *cursor_wheelhouse_black_with_white_lines;
71 struct brands
73 SDL_Surface *brand_wheelhouse;
74 SDL_Surface *brand_wheelhouse_transparent;
75 SDL_Surface *brand_wheelhouse_inverted;
76 SDL_Surface *brand_wheelhouse_black_with_white_lines;
79 /* Prototypes */
80 static void init_sdl(bool fullscreen);
81 static void handle_args(int argc, char *argv[], bool *fullscreen);
82 static void print_help(void);
83 static void print_usage(void);
84 static SDL_Surface *get_background();
85 static struct cursors setup_cursors(void);
86 static struct brands setup_brands(void);
87 static void close_sprites(struct sprite **sprites);
88 static void play_game(SDL_Surface *screen, SDL_Surface *back, struct sprite **sprites, struct cursors *cursors, struct brands *brands);
89 static void handle_events(SDL_Surface *screen, int *quit, int *pause, bool *mouse_clicked, int *x_mouse, int *y_mouse, struct cursors *cursors, struct brands *brands, SDL_Surface **brand);
90 static void move(struct sprite *sprites[], int *pause, double elapsed_ticks);
91 static Uint32 get_elapsed_ticks(void);
92 static void main_draw(struct sprite **sprites, SDL_Surface *brand);
93 static void check_branded(SDL_Rect *src, struct sprite **sprites, int x_mouse, int y_mouse);
95 static void set_icon(void);
96 static struct sprite **setup_img(SDL_Surface *screen);
97 static struct sprite *load_img(int i, SDL_Surface *screen, unsigned int x_scale, unsigned int y_scale);
98 static void draw_image(SDL_Surface *img, SDL_Surface *screen, int x, int y);
99 static void draw_background(SDL_Surface *back, SDL_Surface *screen);
101 int main(int argc, char *argv[])
103 bool fullscreen;
104 handle_args(argc, argv, &fullscreen);
106 init_sdl(fullscreen);
108 struct cursors cursors = setup_cursors();
109 struct brands brands = setup_brands();
111 SDL_Surface *screen = SDL_GetVideoSurface();
112 SDL_Surface *background = get_background();
114 struct sprite **sprites = setup_img(screen);
116 play_game(screen, background, sprites, &cursors, &brands);
118 close_sprites(sprites);
119 SDL_FreeSurface(screen);
120 SDL_FreeSurface(background);
121 /* SDL_FreeSurface(brand); */
123 exit(EXIT_SUCCESS);
126 static void init_sdl(bool fullscreen)
128 srand(time(0));
130 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
131 printf("Unable to initialize SDL: %s\n", SDL_GetError());
132 exit(1);
135 atexit(SDL_Quit);
137 SDL_WM_SetCaption("jody", "jody");
138 set_icon();
140 if (SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_HWSURFACE | SDL_DOUBLEBUF | (fullscreen ? SDL_FULLSCREEN : 0)) == NULL) {
141 printf("Unable to set video mode: %s\n", SDL_GetError());
142 exit(1);
146 static SDL_Surface *get_background(void)
148 char background_dir[] = {IMG_DIR "background"};
149 char *background_path;
150 if (asprintf(&background_path, "%s/1.png", background_dir) < 0) {
151 fprintf(stderr,
152 "\nError: I could not load the background image: %s\n"
153 "The Simple DirectMedia error that occurred was:\n"
154 "%s\n\n", background_dir, SDL_GetError());
155 exit(1);
157 SDL_Surface *background = load_image(background_path);
158 return background;
161 static void handle_args(int argc, char *argv[], bool *fullscreen)
163 *fullscreen = false;
165 for (int i = 1; i < argc; i++) {
166 if (STREQ(argv[i], "--fullscreen") || STREQ(argv[i], "-f")) {
167 *fullscreen = true;
168 } else if (STREQ(argv[i], "--help") || STREQ(argv[i], "-h")) {
169 print_help();
170 exit(EXIT_SUCCESS);
171 } else if (STREQ(argv[i], "--usage") || STREQ(argv[i], "-u")) {
172 print_usage();
173 exit(EXIT_SUCCESS);
174 } else {
175 print_usage();
176 exit(EXIT_FAILURE);
181 static struct cursors setup_cursors(void)
183 struct cursors cursors;
185 cursors.cursor_arrow = create_cursor_arrow();
186 if (!cursors.cursor_arrow) {
187 printf("Unable to create_cursor_arrow\n");
188 exit(EXIT_FAILURE);
191 cursors.cursor_wheelhouse = create_wheelhouse_cursor();
192 if (!cursors.cursor_wheelhouse) {
193 printf("Unable to create_cursor_arrow\n");
194 exit(EXIT_FAILURE);
197 cursors.cursor_wheelhouse_transparent = create_wheelhouse_cursor_transparent();
198 if (!cursors.cursor_wheelhouse_transparent) {
199 printf("Unable to create_cursor_arrow\n");
200 exit(EXIT_FAILURE);
203 cursors.cursor_wheelhouse_inverted = create_wheelhouse_cursor_inverted();
204 if (!cursors.cursor_wheelhouse_inverted) {
205 printf("Unable to create_wheelhouse_cursor_inverted\n");
206 exit(EXIT_FAILURE);
209 cursors.cursor_wheelhouse_black_with_white_lines = create_wheelhouse_cursor_black_with_white_lines();
210 if (!cursors.cursor_wheelhouse_black_with_white_lines) {
211 printf("Unable to create_wheelhouse_cursor_black_with_white_lines\n");
212 exit(EXIT_FAILURE);
215 return cursors;
218 static struct brands setup_brands(void)
220 struct brands brands;
222 brands.brand_wheelhouse = create_wheelhouse_image();
223 if (!brands.brand_wheelhouse) {
224 printf("Unable to create brand_wheelhouse\n");
225 exit(EXIT_FAILURE);
228 brands.brand_wheelhouse_transparent = load_image(IMG_DIR "wheelhouse_image_transparent.png");
229 if (!brands.brand_wheelhouse_transparent) {
230 printf("Unable to load brand_wheelhouse_transparent\n");
231 exit(EXIT_FAILURE);
234 brands.brand_wheelhouse_inverted = load_image(IMG_DIR "wheelhouse_image_inverted.png");
235 if (!brands.brand_wheelhouse_inverted) {
236 printf("Unable to load brand_wheelhouse_inverted\n");
237 exit(EXIT_FAILURE);
240 brands.brand_wheelhouse_black_with_white_lines = load_image(IMG_DIR "wheelhouse_image_black_with_white_lines.png");
241 if (!brands.brand_wheelhouse_black_with_white_lines) {
242 printf("Unable to brand_wheelhouse_black_with_white_lines\n");
243 exit(EXIT_FAILURE);
246 return brands;
249 static void close_sprites(struct sprite **sprites)
251 for (int i = 0; i < NUM_IMAGES; i++) {
252 if (sprites[i])
253 free_sprite(sprites[i]);
255 free(sprites);
258 void set_icon(void)
260 SDL_Surface *icon = IMG_Load(IMG_DIR "icon.png");
261 if (icon == NULL) {
262 fprintf(stderr,
263 "\nError: I could not load the icon image: %s\n"
264 "The Simple DirectMedia error that occurred was:\n"
265 "%s\n\n", IMG_DIR "icon.png", SDL_GetError());
266 exit(1);
269 SDL_WM_SetIcon(icon, NULL);
271 SDL_FreeSurface(icon);
274 static struct sprite **setup_img(SDL_Surface *screen)
276 struct sprite **sprites = xcalloc(NUM_IMAGES, sizeof(struct sprite *));
278 srand (time(0));
279 unsigned int x_scale = RAND_MAX / SCREEN_WIDTH;
280 unsigned int y_scale = RAND_MAX / SCREEN_HEIGHT;
282 for (int i = 0; i < NUM_IMAGES; i++) {
283 sprites[i] = load_img(i, screen, x_scale, y_scale);
285 return sprites;
288 static struct sprite *load_img(int i, SDL_Surface *screen, unsigned int x_scale, unsigned int y_scale)
290 printf("image_names[i] = [%s]\n", image_names[i]);
291 struct sprite_base *base = base_init(image_names[i]);
292 if (base == NULL) {
293 fprintf(stderr,
294 "\nError: I couldn't initialise:\n"
295 "%s\n"
296 "The Simple DirectMedia error that occurred was:\n"
297 "%s\n\n", image_names[i], SDL_GetError());
298 exit(1);
300 struct sprite *sprite = sprite_init(base, screen);
301 if (sprite == NULL) {
302 fprintf(stderr,
303 "\nError: I couldn't initialise the sprite:\n"
304 "%s\n"
305 "The Simple DirectMedia error that occurred was:\n"
306 "%s\n\n", image_names[i], SDL_GetError());
307 exit(1);
310 unsigned int x = random();
311 x /= x_scale;
312 unsigned int y = random();
313 y /= y_scale;
315 /* Put clouds in the sky; animals on the ground. */
316 int half = SCREEN_HEIGHT / 2;
317 /* printf("half\t%d", half); */
318 /* printf("\timage_names[i]\t%s", image_names[i]); */
319 if (i == IMG_CLOUD) {
320 printf("\tmatched!");
321 if (y > half)
322 y = base->image_height;
323 } else
324 if (y < half)
325 y = SCREEN_HEIGHT - base->image_height;
327 printf("\ty\t%d\n", y);
329 set(sprite, x, y);
330 set_speed(sprite, 1);
331 return sprite;
334 static void play_game(SDL_Surface *screen, SDL_Surface *back, struct sprite **sprites, struct cursors *cursors, struct brands *brands)
337 /* keep track of frames and time */
338 int start_time, end_time;
339 start_time = time(NULL);
340 int frames_drawn = 0;
342 /* 126 = width of 1 cow of the 3 cow tile; 95 = height of cow */
343 SDL_Rect src;
344 src.w = 126;
345 src.h = 95;
346 src.x = 0;
347 src.y = 0;
349 /* game loop logic: events, logic and rendering */
350 int quit = false;
351 int pause = false;
352 bool mouse_clicked;
353 int x_mouse = 0;
354 int y_mouse = 0;
355 SDL_Surface *brand = NULL;
356 while (!quit) {
357 mouse_clicked = false;
358 handle_events(screen, &quit, &pause, &mouse_clicked, &x_mouse, &y_mouse, cursors, brands, &brand);
360 /* ai logic */
361 /* ... check branded */
362 if (mouse_clicked)
363 check_branded(&src, sprites, x_mouse, y_mouse);
365 /* ... handle collisions */
367 /* ... set statuses */
369 /* ... moves */
370 Uint32 elapsed_ticks = get_elapsed_ticks();
371 move(sprites, &pause, elapsed_ticks);
373 // render
374 draw_background(back, screen);
375 main_draw(sprites, brand);
377 frames_drawn++;
379 if (SDL_Flip(screen) == -1)
380 quit = true;
383 end_time = time(NULL);
384 if (start_time == end_time) end_time++;
386 /* Display the average framerate. */
387 printf("Drew %i frames in %i seconds, for a framerate of %.2f fps.\n",
388 frames_drawn, end_time-start_time, (float)frames_drawn/(float)(end_time-start_time));
391 static void print_help(void)
393 printf(
394 "\njody\n"
395 "Version " VERSION "\n"
396 "Copyright 2006 Jonathan Wheelhouse\n"
397 "\n"
398 "Game controls:\n"
399 " Keys:\n"
400 " ESC or q - quit\n"
401 " SPACE or p - toggle pause\n"
402 " arrow keys - move person\n"
403 " f - toggle fullscreen\n"
404 " 1 - arrow cursor\n"
405 " 2 - wheelhouse cursor\n"
406 " 3 - transparent wheelhouse cursor\n"
407 " 4 - inverted wheelhouse cursor\n"
408 " 5 - black with white lines wheelhouse cursor\n"
409 " Mouse Movement - Move branding iron.\n"
410 " Any Mouse Button - Brand!\n"
411 "\n"
412 "Run with \"--usage or -u\" for command-line options...\n"
413 "\n");
416 static void print_usage(void)
418 printf("\nUsage: jody [--fullscreen] | [--help (-h) | --usage (-u)]\n\n"
419 " --fullscreen or -f - Display in full screen instead of a window, if possible.\n"
420 "\n");
423 static void handle_events(SDL_Surface *screen, int *quit, int *pause, bool *mouse_clicked, int *x_mouse, int *y_mouse, struct cursors *cursors, struct brands *brands, SDL_Surface **brand)
425 SDL_Event event;
426 while (SDL_PollEvent(&event) > 0) {
427 if (event.type == SDL_QUIT) {
428 *quit = true;
429 } else if (event.type == SDL_KEYDOWN) {
430 switch (event.key.keysym.sym) {
431 case SDLK_ESCAPE:
432 case SDLK_q:
433 *quit = true;
434 break;
435 case SDLK_1:
436 SDL_SetCursor(cursors->cursor_arrow);
437 break;
438 case SDLK_2:
439 SDL_SetCursor(cursors->cursor_wheelhouse);
440 *brand = brands->brand_wheelhouse;
441 break;
442 case SDLK_3:
443 SDL_SetCursor(cursors->cursor_wheelhouse_transparent);
444 *brand = brands->brand_wheelhouse_transparent;
445 break;
446 case SDLK_4:
447 SDL_SetCursor(cursors->cursor_wheelhouse_inverted);
448 *brand = brands->brand_wheelhouse_inverted;
449 break;
450 case SDLK_5:
451 SDL_SetCursor(cursors->cursor_wheelhouse_black_with_white_lines);
452 *brand = brands->brand_wheelhouse_black_with_white_lines;
453 break;
454 case SDLK_f:
455 SDL_WM_ToggleFullScreen(screen);
456 break;
457 case SDLK_p:
458 case SDLK_SPACE:
459 *pause = !*pause;
460 break;
461 default:
462 break;
464 } else if (event.type == SDL_MOUSEBUTTONDOWN) {
465 if (event.button.button == SDL_BUTTON_LEFT) {
466 *mouse_clicked = true;
467 *x_mouse = event.button.x;
468 *y_mouse = event.button.y;
474 static void check_branded(SDL_Rect *src, struct sprite **sprites, int x_mouse, int y_mouse)
476 for (int i = 0; i < NUM_IMAGES; i++) {
477 double x_rhs = sprites[i]->x + src->w;
478 double y_bot = sprites[i]->y + src->h;
480 if (sprites[i]->sprite_base->can_be_branded && !sprites[i]->is_branded)
481 if (x_mouse >= sprites[i]->x && x_mouse <= x_rhs && y_mouse >= sprites[i]->y && y_mouse <= y_bot)
482 sprites[i]->is_branded = true;
486 static void move(struct sprite *sprites[], int *pause, double elapsed_ticks)
488 for (int i = 0; i < NUM_IMAGES; i++) {
489 if (!*pause) {
490 double elapsed_seconds = elapsed_ticks / NUM_MILLISECONDS;
491 double distance = SPRITE_PIXELS_PER_SECOND * elapsed_seconds;
492 xadd(sprites[i], distance);
497 static Uint32 get_elapsed_ticks(void)
499 static Uint32 prev_ticks = 0;
500 static Uint32 cur_ticks = 0;
501 prev_ticks = cur_ticks;
502 cur_ticks = SDL_GetTicks();
503 Uint32 elapsed_ticks = cur_ticks - prev_ticks;
504 return elapsed_ticks;
507 static void main_draw(struct sprite **sprites, SDL_Surface *brand)
509 for (int i = 0; i < NUM_IMAGES; i++) {
510 draw(sprites[i], brand);
514 /* static SDL_Rect calc_dest(struct image *image) */
515 /* { */
516 /* SDL_Rect dest; */
518 /* /\* Move the cow horizontally. *\/ */
519 /* image->x += X_DIST * time_scale; */
520 /* if (image->x > SCREEN_WIDTH) */
521 /* image->x = 0; */
522 /* dest.x = image->x; */
524 /* /\* Move the cow vertically and randomly. *\/ */
525 /* /\* Add check for top and bottom; do something sensible */
526 /* like bounce off. *\/ */
528 /* unsigned int odd = random() % 2; */
529 /* int dist; */
530 /* if (odd) */
531 /* dist = Y_DIST; */
532 /* else */
533 /* dist = - Y_DIST; */
535 /* image->y += dist * time_scale; */
536 /* if (image->y > SCREEN_HEIGHT || image->y < 0) */
537 /* image->y = SCREEN_HEIGHT / 2; */
539 /* dest.y = image->y; */
541 /* return dest; */
542 /* } */
544 /* static SDL_Surface *image_load(char *file) */
545 /* { */
546 /* SDL_Surface *temp1, *temp2; */
547 /* temp1 = IMG_Load(file); */
548 /* if (temp1 == NULL) { */
549 /* fprintf(stderr, */
550 /* "\nError: 'static SDL_Surface * image_load' function couldn't load a graphics file:\n" */
551 /* "%s\n" */
552 /* "The Simple DirectMedia error that occurred was:\n" */
553 /* "%s\n\n", file, SDL_GetError()); */
554 /* return NULL; */
555 /* } */
556 /* temp2 = SDL_DisplayFormat(temp1); */
557 /* SDL_FreeSurface(temp1); */
558 /* return temp2; */
559 /* } */
561 /* static SDL_Surface *init_images() */
562 /* { */
563 /* return image_load(IMG_DIR "background.png"); */
564 /* } */
566 static void draw_image(SDL_Surface *img, SDL_Surface *screen, int x, int y)
568 SDL_Rect dest;
569 dest.x = x;
570 dest.y = y;
571 SDL_BlitSurface(img, NULL, screen, &dest);
574 static void draw_background(SDL_Surface *back, SDL_Surface *screen)
576 draw_image(back, screen, 0, 0);