/*
* 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 "defs.h"
#include "maze.h"
#include "player.h"
enum _mf_player_dir {
MF_PLAYER_DIR_N_,
MF_PLAYER_DIR_U_,
MF_PLAYER_DIR_D_,
MF_PLAYER_DIR_L_,
MF_PLAYER_DIR_R_
};
struct mf_player {
struct mf_maze *maze;
int cell_width;
int speed;
enum _mf_player_dir cur_dir;
enum _mf_player_dir new_dir;
enum _mf_player_dir old_dir;
int turning;
int cur_x;
int cur_y;
int new_x;
int new_y;
int travel;
};
struct mf_player *
mf_player_new(struct mf_maze *maze, int cell_width)
{
struct mf_player *p;
p = calloc(1, sizeof(*p));
if (p == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Couldn't create player: %s",
strerror(errno));
return NULL;
}
p->maze = maze;
p->cell_width = cell_width;
p->speed = MF_PLAYER_SPEED;
p->new_dir = MF_PLAYER_DIR_N_;
p->turning = 0;
p->cur_x = 0;
p->cur_y = 0;
p->travel = 0;
if (mf_maze_is_wall(maze, 0, 0, 1, 0)) {
p->old_dir = p->cur_dir = MF_PLAYER_DIR_D_;
} else {
p->old_dir = p->cur_dir = MF_PLAYER_DIR_R_;
}
return p;
}
void
mf_player_get_vector(struct mf_player *p, int *x, int *y, int *travel,
int *dx, int *dy)
{
*x = p->cur_x;
*y = p->cur_y;
*travel = p->travel;
switch (p->cur_dir) {
case MF_PLAYER_DIR_U_: *dx = 0; *dy = -1; break;
case MF_PLAYER_DIR_D_: *dx = 0; *dy = 1; break;
case MF_PLAYER_DIR_L_: *dx = -1; *dy = 0; break;
case MF_PLAYER_DIR_R_: *dx = 1; *dy = 0; break;
default: *dx = 0; *dy = 0; break;
}
}
void
mf_player_key_event(struct mf_player *p, SDL_Event *e)
{
switch (e->type) {
case SDL_KEYDOWN:
switch (e->key.keysym.sym) {
case SDLK_UP:
p->new_dir = MF_PLAYER_DIR_U_;
break;
case SDLK_DOWN:
p->new_dir = MF_PLAYER_DIR_D_;
break;
case SDLK_LEFT:
p->new_dir = MF_PLAYER_DIR_L_;
break;
case SDLK_RIGHT:
p->new_dir = MF_PLAYER_DIR_R_;
break;
default:
break;
}
default:
break;
}
}
int
mf_player_update(struct mf_player *p)
{
int dx;
int dy;
int x;
int y;
if (p->travel > 0) {
/* Currently moving */
p->travel += p->speed;
if (p->travel >= p->cell_width) {
/* Reached next cell */
p->cur_x = p->new_x;
p->cur_y = p->new_y;
if (p->cur_dir == p->new_dir) {
/* Want to continue straight */
p->travel -= p->cell_width;
} else {
/* Want to stop or turn */
p->travel = 0;
}
} else {
/* Farther to go */
goto end;
}
}
if (p->new_dir != MF_PLAYER_DIR_N_ && p->cur_dir != p->new_dir) {
/* Want to turn */
p->old_dir = p->cur_dir;
p->cur_dir = p->new_dir;
p->new_dir = MF_PLAYER_DIR_N_;
p->turning = MF_PLAYER_TURN_TIME + 1;
}
if (p->turning > 0) {
/* Turning */
--p->turning;
}
if (p->turning == 0) {
/* Done turning */
if (p->new_dir != MF_PLAYER_DIR_N_) {
/* Want to move */
switch (p->new_dir) {
case MF_PLAYER_DIR_U_: dx = 0; dy = -1; break;
case MF_PLAYER_DIR_D_: dx = 0; dy = 1; break;
case MF_PLAYER_DIR_L_: dx = -1; dy = 0; break;
case MF_PLAYER_DIR_R_: dx = 1; dy = 0; break;
default: dx = 0; dy = 0; break;
}
p->new_dir = MF_PLAYER_DIR_N_;
if (mf_maze_is_wall(p->maze, p->cur_x, p->cur_y,
dx, dy)) {
goto end;
}
p->new_x = p->cur_x + dx;
p->new_y = p->cur_y + dy;
p->travel += p->speed;
}
}
end:
switch (p->cur_dir) {
case MF_PLAYER_DIR_U_: dx = 0; dy = -1; break;
case MF_PLAYER_DIR_D_: dx = 0; dy = 1; break;
case MF_PLAYER_DIR_L_: dx = -1; dy = 0; break;
case MF_PLAYER_DIR_R_: dx = 1; dy = 0; break;
default: dx = 0; dy = 0; break;
}
x = p->cur_x - dx, y = p->cur_y - dy;
do {
x += dx, y += dy;
mf_maze_reveal_wall(p->maze, x, y, -1, 0);
mf_maze_reveal_wall(p->maze, x, y, 1, 0);
mf_maze_reveal_wall(p->maze, x, y, 0, -1);
mf_maze_reveal_wall(p->maze, x, y, 0, 1);
} while (!mf_maze_is_wall(p->maze, x, y, dx, dy));
return 0;
}
int
mf_player_render(struct mf_player *p, 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 = p->cur_x * p->cell_width + p->cell_width / 2;
cy = p->cur_y * p->cell_width + p->cell_width / 2;
switch (p->cur_dir) {
case MF_PLAYER_DIR_U_:
cy -= p->travel;
if (p->old_dir == MF_PLAYER_DIR_L_) {
fx = -2.0;
} else {
fx = 2.0;
}
break;
case MF_PLAYER_DIR_D_: cy += p->travel; fx = 0.0; break;
case MF_PLAYER_DIR_L_: cx -= p->travel; fx = -1.0; break;
case MF_PLAYER_DIR_R_: cx += p->travel; fx = 1.0; break;
default: fx = 0; break;
}
switch (p->old_dir) {
case MF_PLAYER_DIR_U_:
if (p->cur_dir == MF_PLAYER_DIR_L_) {
ofx = -2.0;
} else {
ofx = 2.0;
}
break;
case MF_PLAYER_DIR_D_: ofx = 0.0; break;
case MF_PLAYER_DIR_L_: ofx = -1.0; break;
case MF_PLAYER_DIR_R_: ofx = 1.0; break;
default: ofx = 0.0; break;
}
r = p->cell_width / 2 - MF_PLAYER_P;
fx = ofx + (fx - ofx) * (MF_PLAYER_TURN_TIME - p->turning) /
MF_PLAYER_TURN_TIME;
fx *= r;
/*
* Head
*/
if (SDL_SetRenderDrawColor(renderer,
MF_COLOR_PLYR_R, MF_COLOR_PLYR_G,
MF_COLOR_PLYR_B, MF_COLOR_PLYR_A) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Couldn't render player: %s",
SDL_GetError());
e = -1;
}
#define _mf_player_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_player_px( x, i);
for (i = 0; i >= 0-y; --i) _mf_player_px( x, i);
for (i = 0; i <= y; ++i) _mf_player_px(0-x, i);
for (i = 0; i >= 0-y; --i) _mf_player_px(0-x, i);
for (i = 0; i <= y; ++i) _mf_player_px( i, x);
for (i = 0; i <= y; ++i) _mf_player_px( i, 0-x);
for (i = 0; i >= 0-y; --i) _mf_player_px( i, x);
for (i = 0; i >= 0-y; --i) _mf_player_px( i, 0-x);
}
#undef _mf_player_px
/*
* Smile
*/
if (SDL_SetRenderDrawColor(renderer,
MF_COLOR_PSML_R, MF_COLOR_PSML_G,
MF_COLOR_PSML_B, MF_COLOR_PSML_A) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Couldn't render player: %s",
SDL_GetError());
e = -1;
}
oy = r * MF_PLAYER_SMILE_Y;
fr = r * MF_PLAYER_SMILE_R;
#define _mf_player_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_player_px( x, i);
for (i = 0; i <= y; ++i) _mf_player_px(0-x, i);
for (i = 0; i <= y; ++i) _mf_player_px( i, x);
for (i = 0; i >= 0-y; --i) _mf_player_px( i, x);
}
#undef _mf_player_px
/*
* Eyes
*/
if (SDL_SetRenderDrawColor(renderer,
MF_COLOR_PEYE_R, MF_COLOR_PEYE_G,
MF_COLOR_PEYE_B, MF_COLOR_PEYE_A) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Couldn't render player: %s",
SDL_GetError());
e = -1;
}
ox = r * MF_PLAYER_EYE_X;
oy = r * MF_PLAYER_EYE_Y;
fr = r * MF_PLAYER_EYE_R;
#define _mf_player_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_player_px( x, i);
for (i = 0; i >= 0-y; --i) _mf_player_px( x, i);
for (i = 0; i <= y; ++i) _mf_player_px(0-x, i);
for (i = 0; i >= 0-y; --i) _mf_player_px(0-x, i);
for (i = 0; i <= y; ++i) _mf_player_px( i, x);
for (i = 0; i <= y; ++i) _mf_player_px( i, 0-x);
for (i = 0; i >= 0-y; --i) _mf_player_px( i, x);
for (i = 0; i >= 0-y; --i) _mf_player_px( i, 0-x);
}
#undef _mf_player_px
return e;
}
void
mf_player_destroy(struct mf_player **p_p)
{
struct mf_player *p;
if (p_p == NULL || *p_p == NULL) {
return;
}
p = *p_p;
free(p);
p = NULL;
}