diff options
Diffstat (limited to 'physics.c')
| -rw-r--r-- | physics.c | 204 | 
1 files changed, 204 insertions, 0 deletions
| diff --git a/physics.c b/physics.c new file mode 100644 index 0000000..b1a8db1 --- /dev/null +++ b/physics.c @@ -0,0 +1,204 @@ +#include "asset.h" +#include "config.h" +#include "map.h" +#include "maths.h" +#include "physics.h" +#include "rect.h" +#include "render.h" + +int test_bit( +	unsigned* data, +	int x, +	int y, +	int w +) { +	int idx = x + y * w; +	return data[idx >> 5] & (1 << (idx & 0x1f)); +} + +static int solve_mask( +	unsigned char t, +	int* x, +	int* y, +	int* vy, +	const Rect* col, +	const Rect* r, +	const Rect* tr, +	int* od +) { +	int n, d = 0; +	const Bitmap* b = get_bitmap(asset_id_mask_img); +	unsigned* mp; +	int xoff = 0; +	int mx = r->x + r->w / 2; +	int my = (r->y + r->h) - f1; +	mx -= tr->x; +	my -= tr->y; +	mx >>= fbits; +	my >>= fbits; +	if (mx < 0 || mx >= map_tile_size) +		return 0; +	if ((n = my - map_tile_size) >= 0 && n < 4) +		my = map_tile_size - 1; +	mp = (unsigned*)&b[1]; +	xoff = (t & 0xf) * map_tile_size; +	if (my >= map_tile_size) return 0; +	if (my < 0 || *vy < 0) goto up; +	if (test_bit(mp, mx + xoff, my, b->w)) { +		d = -1; +		my = map_tile_size - 1; +	} else { +up: +		my = (r->y - tr->y) >> fbits; +		if (my < 0) { +			if (my >= -4) my = 0; +			else          return 0; +		} +		if (my >= map_tile_size) return 0; +		if (test_bit(mp, mx + xoff, my, b->w)) d = 1; +		my = 0; +	} +	if (!d)                return 0; +	if (d < 0 && *vy <= 0) return 0; +	if (d > 0 && *vy >= 0) return 0; +	for ( +		; +			test_bit(mp, mx + xoff, my, b->w) && +			my >= 0 && +			my < map_tile_size; +		my += d +	); +	if (d < 0) { +		*y = (tr->y - (col->y + col->h)); +		*y += (my << fbits) + f1 * 2; +	} else { +		*y = (tr->y + tr->h) - col->y; +		*y -= ((map_tile_size - my) << fbits); +	} +	*vy = 0; +	*od = d; +	return 1; +} + +void update_body( +	const struct Map* map, +	int* x, +	int* y, +	int* vx, +	int* vy, +	int* grounded, +	int* headbutted, +	int* on_ramp, +	const Rect* r +) { +	Rect rect = { 0 }; +	int ramp = 0, d; +	rect.w = r->w; +	rect.h = r->h; +	if (grounded[0] < 3) { +		*vx = (*vx << fbits) / (f1 + player_friction); +	} +	grounded[0]++; +	headbutted[0] = 0; +#define ittiles(expr) { \ +	int sx, sy, ex, ey, tx, ty; \ +	Rect tr; \ +	rect.x = *x + r->x; \ +	rect.y = *y + r->y; \ +	sx = (rect.x >> fbits) / map_tile_size; \ +	sy = (rect.y >> fbits) / map_tile_size; \ +	ex = ((rect.x + rect.w) >> fbits) / map_tile_size; \ +	ey = ((rect.y + rect.h) >> fbits) / map_tile_size; \ +	tr.w = map_tile_size << fbits; \ +	tr.h = map_tile_size << fbits; \ +	for (ty = sy; ty <= ey && ty < map_h; ty++) { \ +		for (tx = sx; tx <= ex && tx < map_w; tx++) { \ +			int tc = map->collision[tx + ty * map_w]; \ +			tr.x = (tx * map_tile_size) << fbits; \ +			tr.y = (ty * map_tile_size) << fbits; \ +			if (rects_overlap(&tr, &rect)) \ +				expr \ +		} \ +	}} +	/* increase the gravity if i'm on a ramp, to +	 * smoothly slide down instead of skipping */ +	*vy += main_gravity + main_gravity_ramp * (*on_ramp && *grounded < 3); +	if (*vy) { +		*y += *vy; +		ittiles({ +			if (tc > 1) { +				int id = tc - 2; +				if (solve_mask( +					id, +					x, +					y, +					vy, +					r, +					&rect, +					&tr, +					&d +				)) { +					if (d < 0) +						*grounded = 0; +					else { +						*headbutted = 1; +					} +					ramp = 1; +				} +			} +		}); +		if (ramp) goto do_x; +		ittiles({ +			if (tc == 1) { +				if (*vy > 0) { +					*y = tr.y - (r->y + r->h); +					*vy = 0; +					*grounded = 0; +				} else if (*vy < 0) { +					*y = (tr.y + tr.h) - r->y; +					*vy = 0; +					*headbutted = 1; +				} +			} +		}); +	} +do_x: +	*on_ramp = ramp; +	if (*vx) { +		*x += *vx; +		if (ramp) { +			/* Handle the case where the body is at the top +			 * of a ramp hitting into a block. */ +			ittiles({ +				if (tc == 1) { +					int cy; +					if (d < 0) cy = rect.y; +					else cy = rect.y + rect.h; +					if (*vx < 0 && point_rect_overlap( +						&tr, +						rect.x, +						cy +					)) *x = (tr.x + tr.w) - r->x; +					if (*vx > 0 && point_rect_overlap( +						&tr, +						rect.x + rect.w, +						cy +					)) *x = tr.x - (r->x + r->w); +				} +			}); +			return; +		} +		ittiles({ +			if (tc == 1) { +				if (*vx > 0) { +					*x = tr.x - (r->x + r->w); +					*vx = 0; +				} else if (*vx < 0) { +					*x = (tr.x + tr.w) - r->x; +					*vx = 0; +				} +			} +		}); +	} +} + |