aboutsummaryrefslogtreecommitdiff
path: root/physics.c
diff options
context:
space:
mode:
Diffstat (limited to 'physics.c')
-rw-r--r--physics.c204
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;
+ }
+ }
+ });
+ }
+}
+