/* * Copyright (C) 2021 P. J. McDermott * * This file is part of Dodge Balls * * Dodge Balls 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. * * Dodge Balls 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 Dodge Balls. If not, see . */ #include #include #include "ball.h" #include "collision.h" #include "map.h" #include "tileset.h" #include "output.h" struct db_ball { int cx; int cy; double x; double y; int r; double a; int d; int sr; double s; struct db_tileset *tilesets; int gid; struct db_map *map; struct db_ball *next; }; struct db_ball * db_ball_new(int x, int y, int r, int a, int d, int sr, double s, struct db_tileset *tilesets, int gid, struct db_map *map, struct db_ball *prev) { struct db_ball *ball; ball = calloc(1, sizeof(*ball)); if (ball == NULL) { db_err("Failed to allocate memory"); return NULL; } db_dbg("Ball at (%d,%d)", x, y); if (sr == 0) { ball->x = x; ball->y = y; } else { ball->cx = x; ball->cy = y; ball->x = x + cos(a * (M_PI / 180)) * sr; ball->y = y + sin(a * (M_PI / 180)) * sr; } ball->r = r; ball->a = a; ball->d = d; ball->sr = sr; ball->s = s; ball->tilesets = tilesets; ball->gid = gid; ball->map = map; if (prev != NULL) { prev->next = ball; } return ball; } void db_balls_move(struct db_ball *ball) { if (ball->sr == 0) { ball->x += cos(ball->a * (M_PI / 180)) * ball->s; ball->y += sin(ball->a * (M_PI / 180)) * ball->s; } else { ball->a = fmod(ball->a + ball->d * ball->s, 360); ball->x = ball->cx + cos(ball->a * (M_PI / 180)) * ball->sr; ball->y = ball->cy + sin(ball->a * (M_PI / 180)) * ball->sr; db_dbg("Spinning ball angle %f (speed %f, direction %d)", ball->a, ball->s, ball->d); } if (ball->next != NULL) { db_balls_move(ball->next); } } static void _db_ball_bounce(struct db_ball *ball, double col_x, double col_y) { double col_a; col_a = atan2(col_y - ball->y, col_x - ball->x) * 180 / M_PI; ball->a = fmod(2 * col_a - ball->a - 180, 360); } void db_balls_collisions(struct db_ball *ball) { struct db_ball *other; double col_x; double col_y; struct db_map_line *line; double prev_col_x; double prev_col_y; int line_x1; int line_y1; int line_x2; int line_y2; /* Spinning balls shouldn't collide with any other balls */ if (ball->sr == 0) { for (other = ball->next; other != NULL; other = other->next) { if (other->sr == 0 && db_col_pt_cir_cir( ball->x, ball->y, ball->r, other->x, other->y, other->r, &col_x, &col_y)) { _db_ball_bounce(ball , col_x, col_y); _db_ball_bounce(other, col_x, col_y); } } } /* Spinning balls shouldn't bounce off walls */ if (ball->sr == 0) { prev_col_x = prev_col_y = -1; for (line = db_map_get_lines(ball->map); line != NULL; line = db_map_line_get_next(line)) { db_map_line_get_coords(line, &line_x1, &line_y1, &line_x2, &line_y2); col_x = col_y = 0; /* Shut up GCC */ if (db_col_cir_line(ball->x, ball->y, ball->r, line_x1, line_y1, line_x2, line_y2, &col_x, &col_y)){ if (col_x == prev_col_x || col_y == prev_col_y){ continue; } _db_ball_bounce(ball, col_x, col_y); prev_col_x = col_x; prev_col_y = col_y; } } } if (ball->next != NULL) { db_balls_collisions(ball->next); } } int db_balls_player_collisions(struct db_ball *ball, int player_x, int player_y, int player_r) { if (db_col_cir_cir(ball->x, ball->y, ball->r, player_x, player_y, player_r)) { return 1; } if (ball->next != NULL) { return db_balls_player_collisions(ball->next, player_x, player_y, player_r); } else { return 0; } } void db_balls_render(struct db_ball *ball, SDL_Renderer *renderer) { SDL_Rect dstrect; dstrect.x = ball->x - ball->r; dstrect.y = ball->y - ball->r; dstrect.w = ball->r * 2; dstrect.h = ball->r * 2; db_dbg("Rendering ball (%dx%d) at (%d,%d)", dstrect.w, dstrect.h, dstrect.x, dstrect.y); db_tile_render(ball->tilesets, renderer, ball->gid, &dstrect); if (ball->next != NULL) { db_balls_render(ball->next, renderer); } }