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