/*
* 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_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;
}