From be5c7263406aef867501c7965bcced6a7e2898a6 Mon Sep 17 00:00:00 2001 From: quou Date: Sun, 29 Sep 2024 16:39:31 +1000 Subject: animation, player movement, physics etc. --- .gitignore | 1 + 1bitjam.c | 27 +++--- Makefile | 36 ++++++- animation.c | 9 ++ animation.h | 12 +++ asset.c | 4 + asset.h | 1 + config.h | 32 ++++++- convanim.c | 79 ++++++++++++++++ intermediate/guy.bmp | Bin 394 -> 12426 bytes intermediate/guy_fall_left.anm | 2 + intermediate/guy_fall_right.anm | 2 + intermediate/guy_idle_left.anm | 2 + intermediate/guy_idle_right.anm | 2 + intermediate/guy_jump_left.anm | 2 + intermediate/guy_jump_right.anm | 2 + intermediate/guy_run_left.anm | 7 ++ intermediate/guy_run_right.anm | 7 ++ intermediate/map.bmp | Bin 0 -> 30858 bytes intermediate/mask.bmp | Bin 0 -> 16522 bytes map.c | 68 ++++++++++++++ map.h | 60 ++++++++++++ obj.h | 32 +++++++ physics.c | 204 ++++++++++++++++++++++++++++++++++++++++ physics.h | 19 ++++ plat.c | 12 +++ plat.h | 4 + player.c | 132 ++++++++++++++++++++++++++ rect.c | 16 ++++ rect.h | 2 + render.h | 2 +- 31 files changed, 751 insertions(+), 27 deletions(-) create mode 100644 animation.c create mode 100644 animation.h create mode 100644 convanim.c create mode 100644 intermediate/guy_fall_left.anm create mode 100644 intermediate/guy_fall_right.anm create mode 100644 intermediate/guy_idle_left.anm create mode 100644 intermediate/guy_idle_right.anm create mode 100644 intermediate/guy_jump_left.anm create mode 100644 intermediate/guy_jump_right.anm create mode 100644 intermediate/guy_run_left.anm create mode 100644 intermediate/guy_run_right.anm create mode 100644 intermediate/map.bmp create mode 100644 intermediate/mask.bmp create mode 100644 map.c create mode 100644 map.h create mode 100644 obj.h create mode 100644 physics.c create mode 100644 physics.h create mode 100644 player.c diff --git a/.gitignore b/.gitignore index 0a86950..40ef3ee 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ tags pack.h /data/ /convimg +/convanim /packassets /1bitjam /distrib diff --git a/1bitjam.c b/1bitjam.c index c2a84e1..619876e 100644 --- a/1bitjam.c +++ b/1bitjam.c @@ -1,7 +1,9 @@ #include "asset.h" #include "config.h" +#include "map.h" #include "maths.h" #include "memory.h" +#include "obj.h" #include "plat.h" #include "rect.h" #include "render.h" @@ -11,9 +13,8 @@ int entrypoint(int argc, const char** argv, Arena* m) { App* a; FPS f; Renderer r; - Rect rect = { 0, 0, 32, 32 }; - Rect rect2 = { 0, 0, 8, 8 }; - int x = 0, y = 0; + Player player; + Map map; (void)argc; (void)argv; init_maths(); @@ -25,25 +26,19 @@ int entrypoint(int argc, const char** argv, Arena* m) { a = new_app(&h, game_name); init_audio(); init_fps(&f, default_mpf); + generate_floor(&map, 0); + init_player(&player); while (a->o) { fps_begin(&f); while (f.now >= f.next && a->o) { app_begin(a); + + update_player(&player, a, &map); + ren_begin(&r, a->fb, viewport_w, viewport_h); ren_clear(&r); - if (a->btn_states[btn_left] & btn_state_pressed) - x--; - if (a->btn_states[btn_right] & btn_state_pressed) - x++; - if (a->btn_states[btn_up] & btn_state_pressed) - y--; - if (a->btn_states[btn_down] & btn_state_pressed) - y++; - if (a->btn_states[btn_shoot] & btn_state_just_pressed) - play_sound(30); - ren_text(&r, 30, 60, "Hello"); - ren_map(&r, 10, 5, &rect, get_bitmap(asset_id_hello_img)); - ren_map(&r, x, y, &rect2, get_bitmap(asset_id_guy_img)); + render_map(&map, &r); + ren_player(&player, &r); ren_end(&r); app_end(a); fps_update(&f); diff --git a/Makefile b/Makefile index 24a80cd..4c755b9 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ target = 1bitjam int_dir = intermediate data_dir = data convimg = convimg +convanim = convanim packassets = packassets pack = pack.h @@ -43,19 +44,35 @@ endif sources = \ 1bitjam.c \ + animation.c \ asset.c \ + map.c \ maths.c \ memory.c \ + physics.c \ plat.c \ + player.c \ rect.c \ render.c \ image_sources = \ $(int_dir)/guy.bmp \ - $(int_dir)/hello.bmp - -objects = $(sources:%.c=%.o) -images = $(image_sources:$(int_dir)/%.bmp=$(data_dir)/%.img) + $(int_dir)/map.bmp \ + $(int_dir)/mask.bmp + +anim_sources = \ + $(int_dir)/guy_fall_left.anm \ + $(int_dir)/guy_fall_right.anm \ + $(int_dir)/guy_idle_left.anm \ + $(int_dir)/guy_idle_right.anm \ + $(int_dir)/guy_jump_left.anm \ + $(int_dir)/guy_jump_right.anm \ + $(int_dir)/guy_run_left.anm \ + $(int_dir)/guy_run_right.anm \ + +objects = $(sources:%.c=%.o) +images = $(image_sources:$(int_dir)/%.bmp=$(data_dir)/%.img) +animations = $(anim_sources:$(int_dir)/%.anm=$(data_dir)/%.anm) all: $(target) $(pack) @@ -65,15 +82,22 @@ $(objects): %.o : %.c | $(pack) $(images): $(data_dir)/%.img : $(int_dir)/%.bmp | $(convimg) $(data_dir) ./$(convimg) $< $@ -$(pack): $(packassets) $(images) +$(animations): $(data_dir)/%.anm : $(int_dir)/%.anm | $(convanim) $(data_dir) + ./$(convanim) $< $@ + +$(pack): $(packassets) $(images) $(animations) ./$(packassets) \ $(pack) \ $(data_dir) \ + $(notdir $(animations)) \ $(notdir $(images)) $(convimg): convimg.c $(tool_compiler) convimg.c $(cflags) -o $@ +$(convanim): convanim.c + $(tool_compiler) convanim.c $(cflags) -o $@ + $(packassets): packassets.c $(tool_compiler) packassets.c $(cflags) -o $@ @@ -89,5 +113,7 @@ clean: rm $(pack) rm -r $(data_dir) rm $(target) + rm $(convimg) + rm $(convanim) -include $(sources:%.c=%.d) diff --git a/animation.c b/animation.c new file mode 100644 index 0000000..388e29d --- /dev/null +++ b/animation.c @@ -0,0 +1,9 @@ +#include "animation.h" +#include "rect.h" + +void update_anim(const Animation* a, int* f, Rect* r) { + f[0]++; + if (f[0] >= a->fc * a->s) + f[0] = 0; + *r = ((const Rect*)&a[1])[f[0] / a->s]; +} diff --git a/animation.h b/animation.h new file mode 100644 index 0000000..983fed8 --- /dev/null +++ b/animation.h @@ -0,0 +1,12 @@ +#ifndef animation_h +#define animation_h + +struct Rect; + +typedef struct Animation { + int fc, s; +} Animation; + +void update_anim(const Animation* a, int* f, struct Rect* r); + +#endif diff --git a/asset.c b/asset.c index 78085ec..31a6d8c 100644 --- a/asset.c +++ b/asset.c @@ -8,3 +8,7 @@ const unsigned char* get_asset(Asset_ID id) { const struct Bitmap* get_bitmap(Asset_ID id) { return (const struct Bitmap*)get_asset(id); } + +const struct Animation* get_animation(Asset_ID id) { + return (const struct Animation*)get_asset(id); +} diff --git a/asset.h b/asset.h index 74cf66b..09b631e 100644 --- a/asset.h +++ b/asset.h @@ -7,5 +7,6 @@ const unsigned char* get_asset(Asset_ID id); const struct Bitmap* get_bitmap(Asset_ID id); +const struct Animation* get_animation(Asset_ID id); #endif diff --git a/config.h b/config.h index 418e7f4..2fc4561 100644 --- a/config.h +++ b/config.h @@ -1,20 +1,42 @@ #ifndef config_h #define config_h +#include "maths.h" + #define game_name "1bit Game Jam" -#define memory_size (1024 * 8) -#define app_memory_size (1024 * 4) +#define memory_size (1024 * 32) +#define app_memory_size (1024 * 16) #define max_pc_window_w 3000 #define max_pc_window_h 3000 -#define viewport_w 128 -#define viewport_h 128 +#define viewport_w 320 +#define viewport_h 240 #define default_scale 3 -#define default_mpf 50 +#define default_mpf 20 + +#define map_w 20 +#define map_h 15 +#define map_tile_size 16 #define audio_buffer_size 64 #define audio_sample_rate 8000 +#define main_gravity (f1 / 4) +#define main_gravity_ramp (f1) + +#define max_velocity (f1 * 4) + +#define player_move_force (f1 / 2) +#define player_air_move_force (f1 / 10) +#define player_max_vel 853 +#define player_friction (f1 / 4 + f1 / 20) +#define player_stop_thresh 128 +#define player_jump_power (2 << fbits) +#define player_jump_power_air (f1) +#define player_shoot_cooldown 20 +#define player_attack_frames 10 +#define player_lunge_force (f1) + #endif diff --git a/convanim.c b/convanim.c new file mode 100644 index 0000000..80a1b4a --- /dev/null +++ b/convanim.c @@ -0,0 +1,79 @@ +#include "rect.h" + +#include +#include +#include + +#define max_rects 32 + +const char* next_line(const char* line) { + const char* c; + for (c = line; *c != '\n'; c++) { + if (!*c) return 0; + } + return c + 1; +} + +int line_len(const char* line) { + const char* c; + int r = 0; + for (c = line; *c && *c != '\n'; c++, r++); + return r; +} + +int main(int argc, char** argv) { + FILE* infile; + const char* line; + char* src; + int size; + int speed; + Rect rects[max_rects]; + Rect r; + int fc = 0; + if (argc < 3) { + fprintf(stderr, "Usage: %s infile outfile.\n", argv[0]); + return 1; + } + infile = fopen(argv[1], "rb"); + if (!infile) { + fprintf(stderr, "Failed to open %s.\n", argv[1]); + return 2; + } + fseek(infile, 0, SEEK_END); + size = ftell(infile); + rewind(infile); + src = malloc(size + 1); + fread(src, 1, size, infile); + src[size] = 0; + fclose(infile); + line = src; + speed = (int)strtol(line, 0, 10); + line = next_line(line); + for (; line; line = next_line(line)) { + int ll = line_len(line); + char* line2 = malloc(ll + 1); + line2[ll] = 0; /* lmao */ + memcpy(line2, line, ll); + if (sscanf(line, + "{%d,%d,%d,%d}", + &r.x, + &r.y, + &r.w, + &r.h + ) != 4) break; + rects[fc++] = r; + } + free(src); + { + FILE* outfile = fopen(argv[2], "wb"); + if (!outfile) { + fprintf(stderr, "Failed to open %s.\n", argv[2]); + return 2; + } + fwrite(&fc, 1, 4, outfile); + fwrite(&speed, 1, 4, outfile); + fwrite(rects, fc, sizeof r, outfile); + fclose(outfile); + } + return 0; +} diff --git a/intermediate/guy.bmp b/intermediate/guy.bmp index 9a881e0..c7b155d 100644 Binary files a/intermediate/guy.bmp and b/intermediate/guy.bmp differ diff --git a/intermediate/guy_fall_left.anm b/intermediate/guy_fall_left.anm new file mode 100644 index 0000000..61e8acd --- /dev/null +++ b/intermediate/guy_fall_left.anm @@ -0,0 +1,2 @@ +1 +{ 32, 16, 16, 16 } diff --git a/intermediate/guy_fall_right.anm b/intermediate/guy_fall_right.anm new file mode 100644 index 0000000..1919fdd --- /dev/null +++ b/intermediate/guy_fall_right.anm @@ -0,0 +1,2 @@ +1 +{ 32, 0, 16, 16 } diff --git a/intermediate/guy_idle_left.anm b/intermediate/guy_idle_left.anm new file mode 100644 index 0000000..fe05177 --- /dev/null +++ b/intermediate/guy_idle_left.anm @@ -0,0 +1,2 @@ +1 +{ 0, 16, 16, 16 } diff --git a/intermediate/guy_idle_right.anm b/intermediate/guy_idle_right.anm new file mode 100644 index 0000000..86616a0 --- /dev/null +++ b/intermediate/guy_idle_right.anm @@ -0,0 +1,2 @@ +1 +{ 0, 0, 16, 16 } diff --git a/intermediate/guy_jump_left.anm b/intermediate/guy_jump_left.anm new file mode 100644 index 0000000..acec84e --- /dev/null +++ b/intermediate/guy_jump_left.anm @@ -0,0 +1,2 @@ +1 +{ 16, 16, 16, 16 } diff --git a/intermediate/guy_jump_right.anm b/intermediate/guy_jump_right.anm new file mode 100644 index 0000000..633f46e --- /dev/null +++ b/intermediate/guy_jump_right.anm @@ -0,0 +1,2 @@ +1 +{ 16, 0, 16, 16 } diff --git a/intermediate/guy_run_left.anm b/intermediate/guy_run_left.anm new file mode 100644 index 0000000..4d13a8e --- /dev/null +++ b/intermediate/guy_run_left.anm @@ -0,0 +1,7 @@ +4 +{ 0, 16, 16, 16 } +{ 16, 16, 16, 16 } +{ 16, 16, 16, 16 } +{ 0, 16, 16, 16 } +{ 32, 16, 16, 16 } +{ 32, 16, 16, 16 } diff --git a/intermediate/guy_run_right.anm b/intermediate/guy_run_right.anm new file mode 100644 index 0000000..b6098c5 --- /dev/null +++ b/intermediate/guy_run_right.anm @@ -0,0 +1,7 @@ +4 +{ 0, 0, 16, 16 } +{ 16, 0, 16, 16 } +{ 16, 0, 16, 16 } +{ 0, 0, 16, 16 } +{ 32, 0, 16, 16 } +{ 32, 0, 16, 16 } diff --git a/intermediate/map.bmp b/intermediate/map.bmp new file mode 100644 index 0000000..13ee910 Binary files /dev/null and b/intermediate/map.bmp differ diff --git a/intermediate/mask.bmp b/intermediate/mask.bmp new file mode 100644 index 0000000..0363e3d Binary files /dev/null and b/intermediate/mask.bmp differ diff --git a/map.c b/map.c new file mode 100644 index 0000000..855f636 --- /dev/null +++ b/map.c @@ -0,0 +1,68 @@ +#include "asset.h" +#include "map.h" +#include "rect.h" +#include "render.h" + +#define null_tile 0xffff + +Rect tile_rects[] = { +#define x(n, x, y) \ + { x, y, map_tile_size, map_tile_size }, + tiles_xmacro +#undef x +}; + +void generate_floor(Map* m, int seed) { + int e = map_w * map_h, i; + for (i = 0; i < e; i++) { + m->tiles[i] = null_tile; + m->collision[i] = 0; + } + e = map_w * (map_h - 1); + for (i = 0; i < map_w; i++) { + int idx = i + e; + m->tiles[idx] = tile_stone; + m->collision[idx] = 1; + } +#define set(x, y) \ + m->tiles[x + y * map_w] = tile_brick; \ + m->collision[x + y * map_w] = 1; + set(0, 13); + set(0, 12); + set(0, 11); + set(1, 11); + set(2, 11); + set(3, 11); + set(4, 11); + set(19, 13); + set(19, 12); + set(19, 11); + set(19, 10); + set(19, 9); + set(19, 8); +#undef set + m->tiles[18 + 13 * map_w] = tile_stone_ramp9; + m->collision[18 + 13 * map_w] = 6; + m->tiles[1 + 12 * map_w] = tile_stone_ramp13; + m->collision[1 + 12 * map_w] = 10; + m->tiles[2 + 12 * map_w] = tile_stone_ramp14; + m->collision[2 + 12 * map_w] = 11; +} + +void render_map(const Map* m, Renderer* r) { + const Bitmap* tm = get_bitmap(asset_id_map_img); + int t = 0, x, y; + for (y = 0; y < map_h; y++) { + for (x = 0; x < map_w; x++, t++) { + int tile = m->tiles[t]; + if (tile != null_tile) + ren_map( + r, + x * map_tile_size, + y * map_tile_size, + &tile_rects[tile], + tm + ); + } + } +} diff --git a/map.h b/map.h new file mode 100644 index 0000000..806de4d --- /dev/null +++ b/map.h @@ -0,0 +1,60 @@ +#ifndef map_h_included +#define map_h_included + +#include "config.h" + +struct Renderer; + +#define tiles_xmacro \ + x(tile_stone, 0, 0) \ + x(tile_block1, 16, 0) \ + x(tile_block2, 32, 0) \ + x(tile_brick, 48, 0) \ + x(tile_ladder1, 64, 0) \ + x(tile_ladder2, 80, 0) \ + x(tile_stone_ramp1, 96, 0) \ + x(tile_stone_ramp2, 112, 0) \ + x(tile_stone_ramp3, 128, 0) \ + x(tile_stone_ramp4, 144, 0) \ + x(tile_stone_ramp5, 0, 16) \ + x(tile_stone_ramp6, 16, 16) \ + x(tile_stone_ramp7, 32, 16) \ + x(tile_stone_ramp8, 48, 16) \ + x(tile_stone_ramp9, 64, 16) \ + x(tile_stone_ramp10, 80, 16) \ + x(tile_stone_ramp11, 96, 16) \ + x(tile_stone_ramp12, 112, 16) \ + x(tile_stone_ramp13, 128, 16) \ + x(tile_stone_ramp14, 144, 16) \ + x(tile_brick_ramp5, 0, 32) \ + x(tile_brick_ramp6, 16, 32) \ + x(tile_brick_ramp7, 32, 32) \ + x(tile_brick_ramp8, 48, 32) \ + x(tile_brick_ramp9, 64, 32) \ + x(tile_brick_ramp10, 80, 32) \ + x(tile_brick_ramp11, 96, 32) \ + x(tile_brick_ramp12, 112, 32) \ + x(tile_brick_ramp13, 128, 32) \ + +typedef enum { +#define x(n, x, y) \ + n, + tiles_xmacro +#undef x + tile_count +} Map_Tile; + +typedef struct Map { + unsigned short tiles[map_w * map_h]; + unsigned short collision[map_w * map_h]; + /* collision: + * 0 = nothing + * 1 = tile + * >2 = mask + */ +} Map; + +void generate_floor(Map* m, int seed); +void render_map(const Map* m, struct Renderer* r); + +#endif diff --git a/obj.h b/obj.h new file mode 100644 index 0000000..9a331b7 --- /dev/null +++ b/obj.h @@ -0,0 +1,32 @@ +#ifndef obj_h +#define obj_h + +#include "rect.h" + +struct App; +struct Renderer; +struct Map; + +typedef enum { + face_left, + face_right +} Face; + +typedef struct { + int x, y, vx, vy; + int frame; + int anim; + int grounded, headbutted, on_ramp, jumping; + Face face; + Rect rect; +} Player; + +void init_player(Player* p); +void update_player( + Player* p, + struct App* app, + const struct Map* map +); +void ren_player(Player* p, struct Renderer* r); + +#endif 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; + } + } + }); + } +} + diff --git a/physics.h b/physics.h new file mode 100644 index 0000000..7044996 --- /dev/null +++ b/physics.h @@ -0,0 +1,19 @@ +#ifndef physics_h +#define physics_h + +struct Map; +struct Rect; + +void update_body( + const struct Map* map, + int* x, + int* y, + int* vx, + int* vy, + int* grounded, + int* headbutted, + int* on_ramp, + const struct Rect* r +); + +#endif diff --git a/plat.c b/plat.c index 7c5c9f8..cc351ff 100644 --- a/plat.c +++ b/plat.c @@ -3,6 +3,18 @@ #include "memory.h" #include "plat.h" +int btn_pressed(const App* a, Btn btn) { + return a->btn_states[btn] & btn_state_pressed; +} + +int btn_just_pressed(const App* a, Btn btn) { + return a->btn_states[btn] & btn_state_just_pressed; +} + +int btn_just_released(const App* a, Btn btn) { + return a->btn_states[btn] & btn_state_just_released; +} + #ifdef plat_posix #define _POSIX_SOURCE #define _GNU_SOURCE diff --git a/plat.h b/plat.h index 3c91ef6..32f17f6 100644 --- a/plat.h +++ b/plat.h @@ -81,4 +81,8 @@ void init_audio(void); void deinit_audio(void); void play_sound(int len); +int btn_pressed(const App* a, Btn btn); +int btn_just_pressed(const App* a, Btn btn); +int btn_just_released(const App* a, Btn btn); + #endif diff --git a/player.c b/player.c new file mode 100644 index 0000000..a99f40b --- /dev/null +++ b/player.c @@ -0,0 +1,132 @@ +#include "animation.h" +#include "asset.h" +#include "map.h" +#include "maths.h" +#include "obj.h" +#include "physics.h" +#include "plat.h" +#include "render.h" + +void init_player(Player* p) { + p->anim = asset_id_guy_run_right_anm; + p->x = 0; + p->y = 0; + p->vx = 0; + p->vy = 0; + p->frame = 0; + p->grounded = 0; + p->headbutted = 0; + p->on_ramp = 0; + p->jumping = 0; + p->face = face_right; +} + +void update_player_anim(Player* p) { + const Animation* anim = get_animation(p->anim); + update_anim(anim, &p->frame, &p->rect); +} + +void update_player_phys(Player* p, const Map* map) { + const Rect r = { + 5 << fbits, + 4 << fbits, + 5 << fbits, + 12 << fbits + }; + update_body( + map, + &p->x, + &p->y, + &p->vx, + &p->vy, + &p->grounded, + &p->headbutted, + &p->on_ramp, + &r + ); +} + +void update_player_move(Player* p, const App* a) { + int grounded = p->grounded < 3; + int mf = player_move_force; + int nanim, moving, t; + int jumping = p->jumping; + Face* face = &p->face; + if (!grounded) { + mf = player_air_move_force; + } + if (btn_pressed(a, btn_left)) { + p->vx -= mf; + *face = face_left; + } + if (btn_pressed(a, btn_right)) { + p->vx += mf; + *face = face_right; + } + p->vx = + p->vx < -player_max_vel ? -player_max_vel: + p->vx > player_max_vel ? player_max_vel: + p->vx; + moving = absolute(p->vx); + moving = moving > player_stop_thresh; + if (p->grounded == 0 || p->headbutted) { + p->jumping = 0; + } + jumping = p->jumping; + if (jumping) { + if (p->grounded < 10 && btn_pressed(a, btn_jump)) { + t = absolute(p->vy + 1); + t = (f1 << fbits) / t; + p->vy -= (player_jump_power_air * t) >> fbits; + } + if (p->vy < 0 && btn_just_released(a, btn_jump)) { + p->vy += f1; + p->jumping = 0; + } + } + if (grounded && btn_just_pressed(a, btn_jump)) { + p->vy = -player_jump_power; + p->jumping = 1; + play_sound(500); + } + jumping = p->jumping; + nanim = p->anim; + switch (p->face) { + case face_left: + nanim = + grounded ? + moving ? + asset_id_guy_run_left_anm: + asset_id_guy_idle_left_anm: + p->vy < 0 ? + asset_id_guy_jump_left_anm: + asset_id_guy_fall_left_anm; + break; + case face_right: + nanim = + grounded ? + moving ? + asset_id_guy_run_right_anm: + asset_id_guy_idle_right_anm: + p->vy < 0 ? + asset_id_guy_jump_right_anm: + asset_id_guy_fall_right_anm; + break; + default: break; + } + if (nanim != p->anim) { + p->frame = 0; + p->anim = nanim; + } +} + +void update_player(Player* p, App* app, const Map* map) { + update_player_move(p, app); + update_player_phys(p, map); + update_player_anim(p); +} + +void ren_player(Player* p, struct Renderer* r) { + const Bitmap* b = get_bitmap(asset_id_guy_img); + ren_map(r, p->x >> fbits, p->y >> fbits, &p->rect, b); +} diff --git a/rect.c b/rect.c index 69758c1..a35052d 100644 --- a/rect.c +++ b/rect.c @@ -1,5 +1,21 @@ #include "rect.h" +int rects_overlap(const Rect* a, const Rect* b) { + return + a->x + a->w > b->x && + a->y + a->h > b->y && + a->x < b->x + b->w && + a->y < b->y + b->h; +} + +int point_rect_overlap(const Rect* r, int px, int py) { + return + px >= r->x && + py >= r->y && + px <= r->x + r->w && + py <= r->y + r->h; +} + Rect* rect_clip(Rect* r, const Rect* c) { int n; if ((n = c->x - r->x) > 0) { diff --git a/rect.h b/rect.h index 5c89aec..40f5e4e 100644 --- a/rect.h +++ b/rect.h @@ -5,6 +5,8 @@ typedef struct Rect { int x, y, w, h; } Rect; +int rects_overlap(const Rect* a, const Rect* b); +int point_rect_overlap(const Rect* r, int px, int py); Rect* rect_clip(Rect* r, const Rect* c); Rect* rect_clipr(Rect* r, const int* c); Rect* rect_clips(Rect* r, Rect* s, const Rect* c); diff --git a/render.h b/render.h index 01391c5..2cb54b8 100644 --- a/render.h +++ b/render.h @@ -7,7 +7,7 @@ typedef struct Bitmap { short w, h; } Bitmap; -typedef struct { +typedef struct Renderer { unsigned* t; int w, h; int clip[4]; -- cgit v1.2.3-54-g00ecf