BeanCountersClassic/src/collider.c

226 lines
5.0 KiB
C

/*
* collider.c
* This file is part of Bean Counters Classic
*
* Copyright (C) 2018 - Félix Arreola Rodríguez
*
* Bean Counters Classic 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 2 of the License, or
* (at your option) any later version.
*
* Bean Counters Classic 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 Bean Counters Classic; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#include <SDL.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "collider.h"
#include "sdl2_rect.h"
struct _Collider {
Uint32 offset_x, offset_y;
Uint32 size_w, size_h;
Uint32 pitch;
Uint32 *pixels;
};
Uint32 collider_extract_block (Collider *c, int y, int x, int size) {
int bit_pos;
int align;
Uint32 res;
bit_pos = c->pitch * y + (x / 32);
align = 32 - (x % 32);
if (align == 32) {
res = c->pixels[bit_pos];
} else {
res = c->pixels[bit_pos] << (32 - align);
res = res | (c->pixels[bit_pos + 1] >> align);
}
if (size < 32) {
/* Quitar los bits sobrantes */
res = res ^ (res & ((1 << (32 - size)) - 1));
}
return res;
}
Collider * collider_new_from_file (const char *filename) {
int fd;
Collider *new;
Uint32 temp;
Uint32 four_int[4];
int res, g, h;
int map_size;
fd = open (filename, O_RDONLY);
if (fd < 0) {
return NULL;
}
new = (Collider *) malloc (sizeof (Collider));
if (new == NULL) goto bad_load;
/* Empezar a leer los bytes iniciales para saber cuánto reservar en pixeles */
res = read (fd, &temp, sizeof (temp));
if (res < 0) goto bad_load_and_free;
/* Versión incorrecta */
if (temp != 1) goto bad_load_and_free;
/* Consumir el byte extra de alineación de bytes */
res = read (fd, &temp, sizeof (temp));
if (res < 0) goto bad_load_and_free;
res = read (fd, four_int, 4 * sizeof (Uint32));
if (res < 4 * sizeof (Uint32)) goto bad_load_and_free;
new->offset_x = four_int[0];
new->offset_y = four_int[1];
new->size_w = four_int[2];
new->size_h = four_int[3];
if (new->size_w % 32 != 0) {
new->pitch = (new->size_w / 32) + 2;
} else {
new->pitch = (new->size_w / 32) + 1;
}
map_size = new->pitch * new->size_h;
new->pixels = (Uint32 *) malloc (sizeof (Uint32) * map_size);
if (new->pixels == NULL) goto bad_load_and_free;
memset (new->pixels, 0, sizeof (Uint32) * map_size);
for (h = 0; h < new->size_h; h++) {
for (g = 0; g < new->pitch; g++) {
res = read (fd, &temp, sizeof (Uint32));
if (res < 0) goto bad_load_and_free_pixels;
new->pixels[(h * new->pitch) + g] = temp;
}
}
close (fd);
return new;
bad_load_and_free_pixels:
free (new->pixels);
bad_load_and_free:
free (new);
bad_load:
close (fd);
return NULL;
}
Collider * collider_new_block (int w, int h) {
Collider *new;
int map_size;
new = (Collider *) malloc (sizeof (Collider));
if (new == NULL) return NULL;
new->size_w = w;
new->size_h = h;
new->offset_x = new->offset_y = 0;
if (new->size_w % 32 != 0) {
new->pitch = (new->size_w / 32) + 2;
} else {
new->pitch = (new->size_w / 32) + 1;
}
map_size = new->pitch * new->size_h;
/* Reservar los bytes necesarios */
new->pixels = (Uint32 *) malloc (sizeof (Uint32) * map_size);
if (new->pixels == NULL) {
free (new);
return NULL;
}
memset (new->pixels, -1, sizeof (Uint32) * map_size);
return new;
}
int collider_hittest (Collider *a, int x1, int y1, Collider *b, int x2, int y2) {
SDL_Rect rect_left, rect_right, result;
int first = SDL_FALSE;
int g, h;
int s, x;
int offset_a_x, offset_a_y;
int offset_b_x, offset_b_y;
rect_left.x = x1 + a->offset_x; // Sumar los offsets del collider
rect_left.y = y1 + a->offset_y;
rect_left.w = a->size_w;
rect_left.h = a->size_h;
rect_right.x = x2 + b->offset_x;
rect_right.y = y2 + b->offset_y;
rect_right.w = b->size_w;
rect_right.h = b->size_h;
first = SDL_IntersectRect (&rect_left, &rect_right, &result);
if (first == SDL_FALSE) {
/* Ni siquiera cercas */
//printf ("Ni siquiera cercas de colision\n");
return 0;
}
offset_a_y = result.y - rect_left.y;
offset_a_x = result.x - rect_left.x;
offset_b_y = result.y - rect_right.y;
offset_b_x = result.x - rect_right.x;
Uint32 block_a, block_b;
for (h = result.h; h >= 0; h++) {
s = result.w;
x = 0;
while (s > 0) {
block_a = collider_extract_block (a, h + offset_a_y, x + offset_a_x, s);
block_b = collider_extract_block (b, h + offset_b_y, x + offset_b_x, s);
block_a = block_a & block_b;
if (block_a != 0) {
return 1;
}
x = x + 32;
s = s - 32;
}
}
return 0;
}