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; + } + } + }); + } +} + |