/* * Copyright (C) 2021 P. J. McDermott * * This file is part of Maze Fight * * Maze Fight is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Maze Fight 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Maze Fight. If not, see . */ #include #include #include #include #include "../char.h" #include "char.h" struct mf_char * mf_char_new(size_t size) { struct mf_char *c; c = calloc(1, size); if (c == NULL) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create character: %s", strerror(errno)); return NULL; } return c; } void mf_char_get_vector(struct mf_char *c, int *x, int *y, int *travel, int *dx, int *dy) { *x = c->cur_x; *y = c->cur_y; *travel = c->travel; switch (c->cur_dir) { case MF_CHAR_DIR_U_: *dx = 0; *dy = -1; break; case MF_CHAR_DIR_D_: *dx = 0; *dy = 1; break; case MF_CHAR_DIR_L_: *dx = -1; *dy = 0; break; case MF_CHAR_DIR_R_: *dx = 1; *dy = 0; break; default: *dx = 0; *dy = 0; break; } } int mf_char_update(struct mf_char *c) { int dx; int dy; if (c->travel > 0) { /* Currently moving */ c->travel += c->speed; if (c->travel >= c->cell_width) { /* Reached next cell */ c->cur_x = c->new_x; c->cur_y = c->new_y; c->travel -= c->cell_width; if (c->step(c) < 0) { return -1; } if (c->cur_dir == c->new_dir) { /* Want to continue straight */ } else { /* Want to stop or turn */ c->travel = 0; } } else { /* Farther to go */ goto end; } } if (c->new_dir != MF_CHAR_DIR_N_ && c->cur_dir != c->new_dir) { /* Want to turn */ c->old_dir = c->cur_dir; c->cur_dir = c->new_dir; c->new_dir = MF_CHAR_DIR_N_; c->turning = c->turn_time + 1; } if (c->turning > 0) { /* Turning */ --c->turning; } if (c->turning == 0) { /* Done turning */ if (c->new_dir != MF_CHAR_DIR_N_) { /* Want to move */ switch (c->new_dir) { case MF_CHAR_DIR_U_: dx = 0; dy = -1; break; case MF_CHAR_DIR_D_: dx = 0; dy = 1; break; case MF_CHAR_DIR_L_: dx = -1; dy = 0; break; case MF_CHAR_DIR_R_: dx = 1; dy = 0; break; default: dx = 0; dy = 0; break; } c->new_dir = MF_CHAR_DIR_N_; if (mf_maze_is_wall(c->maze, c->cur_x, c->cur_y, dx, dy)) { goto end; } c->new_x = c->cur_x + dx; c->new_y = c->cur_y + dy; c->travel += c->speed; } else { if (c->turn(c) < 0) { return -1; } } } end: return c->update(c); } int mf_char_collision(struct mf_char *c1, struct mf_char *c2, int handle) { if (c1->cur_x == c2->cur_x && c1->cur_y == c2->cur_y) goto hit; if (c1->cur_x == c2->new_x && c1->cur_y == c2->new_y) goto hit; if (c1->new_x == c2->cur_x && c1->new_y == c2->cur_y) goto hit; if (c1->new_x == c2->new_x && c1->new_y == c2->new_y) goto hit; return 0; hit: if (handle == SDL_TRUE) { c1->collide(c1); c2->collide(c2); } return 1; } int mf_char_render(struct mf_char *c, SDL_Renderer *renderer) { int e = 0; int cx; int cy; double fx; double ofx; int r; int x; int y; int i; int oy; int fr; int hy; int ox; cx = c->cur_x * c->cell_width + c->cell_width / 2; cy = c->cur_y * c->cell_width + c->cell_width / 2; switch (c->cur_dir) { case MF_CHAR_DIR_U_: cy -= c->travel; if (c->old_dir == MF_CHAR_DIR_L_) { fx = -2.0; } else { fx = 2.0; } break; case MF_CHAR_DIR_D_: cy += c->travel; fx = 0.0; break; case MF_CHAR_DIR_L_: cx -= c->travel; fx = -1.0; break; case MF_CHAR_DIR_R_: cx += c->travel; fx = 1.0; break; default: fx = 0.0; break; } switch (c->old_dir) { case MF_CHAR_DIR_U_: if (c->cur_dir == MF_CHAR_DIR_L_) { ofx = -2.0; } else { ofx = 2.0; } break; case MF_CHAR_DIR_D_: ofx = 0.0; break; case MF_CHAR_DIR_L_: ofx = -1.0; break; case MF_CHAR_DIR_R_: ofx = 1.0; break; default: ofx = 0.0; break; } r = c->cell_width / 2 - c->padding; fx = ofx + (fx - ofx) * (c->turn_time - c->turning) / c->turn_time; fx *= r; /* * Head */ if (SDL_SetRenderDrawColor(renderer, c->head_color.r, c->head_color.g, c->head_color.b, c->head_color.a) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't render character: %s", SDL_GetError()); e = -1; } #define _mf_char_px(X, Y) \ do { \ if (SDL_RenderDrawPoint(renderer, cx + X, cy + Y) < 0) { \ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \ "Couldn't render widget: %s", \ SDL_GetError()); \ e = -1; \ } \ } while (0) /* TODO: This is one pixel larger than it should be. */ for (x = 0, y = r; y > x; ++x) { y = round(sqrt((r-0.5)*(r-0.5) - (x-0.5)*(x-0.5)) + 0.5); for (i = 0; i <= y; ++i) _mf_char_px( x, i); for (i = 0; i >= 0-y; --i) _mf_char_px( x, i); for (i = 0; i <= y; ++i) _mf_char_px(0-x, i); for (i = 0; i >= 0-y; --i) _mf_char_px(0-x, i); for (i = 0; i <= y; ++i) _mf_char_px( i, x); for (i = 0; i <= y; ++i) _mf_char_px( i, 0-x); for (i = 0; i >= 0-y; --i) _mf_char_px( i, x); for (i = 0; i >= 0-y; --i) _mf_char_px( i, 0-x); } #undef _mf_char_px /* * Smile */ if (SDL_SetRenderDrawColor(renderer, c->smil_color.r, c->smil_color.g, c->smil_color.b, c->smil_color.a) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't render character: %s", SDL_GetError()); e = -1; } oy = r * c->smile_y; fr = r * c->smile_r; #define _mf_char_px(X, Y) \ do { \ hy = round(sqrt((r-0.5)*(r-0.5) - \ (X+fx-0.5)*(X+fx-0.5)) + 0.5); \ if (Y + oy < 0-hy || Y + oy > hy) continue; \ if (SDL_RenderDrawPoint(renderer, cx + fx + X, cy + oy + Y) \ < 0) { \ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \ "Couldn't render widget: %s", \ SDL_GetError()); \ e = -1; \ } \ } while (0) for (x = 0, y = fr; y > x; ++x) { y = round(sqrt((fr-0.5)*(fr-0.5) - (x-0.5)*(x-0.5)) + 0.5); for (i = 0; i <= y; ++i) _mf_char_px( x, i); for (i = 0; i <= y; ++i) _mf_char_px(0-x, i); for (i = 0; i <= y; ++i) _mf_char_px( i, x); for (i = 0; i >= 0-y; --i) _mf_char_px( i, x); } #undef _mf_char_px /* * Eyes */ if (SDL_SetRenderDrawColor(renderer, c->eyes_color.r, c->eyes_color.g, c->eyes_color.b, c->eyes_color.a) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't render character: %s", SDL_GetError()); e = -1; } ox = r * c->eye_x; oy = r * c->eye_y; fr = r * c->eye_r; #define _mf_char_px(X, Y) \ do { \ hy = round(sqrt((r-0.5)*(r-0.5) - \ (X+fx-ox-0.5)*(X+fx-ox-0.5)) + 0.5); \ if (Y - oy >= 0-hy && Y - oy <= hy) { \ if (SDL_RenderDrawPoint(renderer, \ cx + fx - ox + X, cy - oy + Y) \ < 0) { \ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \ "Couldn't render widget: %s", \ SDL_GetError()); \ e = -1; \ } \ } \ hy = round(sqrt((r-0.5)*(r-0.5) - \ (X+fx+ox-0.5)*(X+fx+ox-0.5)) + 0.5); \ if (Y - oy >= 0-hy && Y - oy <= hy) { \ if (SDL_RenderDrawPoint(renderer, \ cx + fx + ox + X, cy - oy + Y) \ < 0) { \ SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \ "Couldn't render widget: %s", \ SDL_GetError()); \ e = -1; \ } \ } \ } while (0) for (x = 0, y = fr; y > x; ++x) { y = round(sqrt((fr-0.5)*(fr-0.5) - (x-0.5)*(x-0.5)) + 0.5); for (i = 0; i <= y; ++i) _mf_char_px( x, i); for (i = 0; i >= 0-y; --i) _mf_char_px( x, i); for (i = 0; i <= y; ++i) _mf_char_px(0-x, i); for (i = 0; i >= 0-y; --i) _mf_char_px(0-x, i); for (i = 0; i <= y; ++i) _mf_char_px( i, x); for (i = 0; i <= y; ++i) _mf_char_px( i, 0-x); for (i = 0; i >= 0-y; --i) _mf_char_px( i, x); for (i = 0; i >= 0-y; --i) _mf_char_px( i, 0-x); } #undef _mf_char_px if (e < 0) { return e; } return c->render(c, renderer); } void mf_char_destroy(struct mf_char **c_p) { struct mf_char *c; if (c_p == NULL || *c_p == NULL) { return; } c = *c_p; c->destroy(c); free(c); c = NULL; }