From 9add408984464bd6b3cc018bb14c3d69ad0a2898 Mon Sep 17 00:00:00 2001 From: quou Date: Tue, 8 Oct 2024 19:56:08 +1100 Subject: New flying enemy that drops arrows --- Makefile | 4 +++ config.h | 2 ++ enemy.c | 74 +++++++++++++++++++++++++++++++++++++++++++++ intermediate/arms.bmp | Bin 41538 -> 41538 bytes intermediate/arrow.anm | 2 ++ intermediate/fly_left.anm | 11 +++++++ intermediate/fly_right.anm | 11 +++++++ intermediate/npc.bmp | Bin 37002 -> 49290 bytes map.c | 34 +++++++++++++++++++++ obj.h | 24 +++++++++++++-- player.c | 13 ++++++++ projectile.c | 59 ++++++++++++++++++++++++++++++++++++ world.c | 20 ++++++++++++ world.h | 13 ++++++-- 14 files changed, 263 insertions(+), 4 deletions(-) create mode 100644 intermediate/arrow.anm create mode 100644 intermediate/fly_left.anm create mode 100644 intermediate/fly_right.anm create mode 100644 projectile.c diff --git a/Makefile b/Makefile index 61b0cf4..d91d34e 100644 --- a/Makefile +++ b/Makefile @@ -58,6 +58,7 @@ sources = \ physics.c \ plat.c \ player.c \ + projectile.c \ random.c \ rect.c \ render.c \ @@ -73,6 +74,7 @@ image_sources = \ $(int_dir)/rooms.bmp \ anim_sources = \ + $(int_dir)/arrow.anm \ $(int_dir)/demon_fly_left.anm \ $(int_dir)/demon_fly_right.anm \ $(int_dir)/demon_idle_left.anm \ @@ -83,6 +85,8 @@ anim_sources = \ $(int_dir)/demon_walk_right.anm \ $(int_dir)/fade_in.anm \ $(int_dir)/fade_out.anm \ + $(int_dir)/fly_left.anm \ + $(int_dir)/fly_right.anm \ $(int_dir)/guy_fall_left.anm \ $(int_dir)/guy_fall_right.anm \ $(int_dir)/guy_idle_left.anm \ diff --git a/config.h b/config.h index 906f097..5a0ccb9 100644 --- a/config.h +++ b/config.h @@ -53,5 +53,7 @@ #define enemy_touch_damage 1 #define enemy_knockback (4 << fbits) #define enemy_hurt_freeze 1 +#define enemy_arrow_cooldown 60 +#define enemy_arrow_damage 2 #endif diff --git a/enemy.c b/enemy.c index 5901445..467b45c 100644 --- a/enemy.c +++ b/enemy.c @@ -18,6 +18,8 @@ int get_enemy_hp(Enemy_Type t) { switch (t) { case enemy_demon: return 3; + case enemy_fly: + return 2; } return 0; } @@ -30,6 +32,12 @@ void get_enemy_rect(Enemy_Type t, Rect* d) { d->w = 12; d->h = 14; break; + case enemy_fly: + d->x = 2; + d->y = 6; + d->w = 11; + d->h = 7; + break; } } @@ -45,6 +53,7 @@ void init_enemy(Enemy* e, Enemy_Type t, int x, int y) { e->face = face_left; e->state = enemy_state_patrol; e->inv = 0; + e->timer = 0; } void update_enemy_anim(Enemy* e) { @@ -214,11 +223,75 @@ void update_demon_move(Enemy* e, World* w) { } } +void update_fly_move(Enemy* e, World* w) { + (void)e; + (void)w; + if (e->state == enemy_state_patrol) { + const Map* m = &w->map; + const Player* p = &w->player; + int d2g = 0, d2c = 0, y; + int tx = (e->x >> fbits) / map_tile_size; + int ty = (e->y >> fbits) / map_tile_size; + for (y = ty; y < map_h; y++, d2g++) + if (m->collision[tx + y * map_w]) + break; + for (y = ty; y >= 0; y--, d2c++) + if (m->collision[tx + y * map_w]) + break; + ty = + ((e->y >> fbits) + (map_tile_size >> 1)) / + map_tile_size; + if (e->face == face_left) { + tx = ( + (e->x >> fbits) - (map_tile_size >> 1)) / + map_tile_size; + } else { + tx = ( + (e->x >> fbits) + map_tile_size + + (map_tile_size >> 1)) / + map_tile_size; + } + if (tile_at(m, tx, ty)) + e->face = !e->face; + if (d2g < 3 && d2c > 2) + e->vy -= 3 * (f1 / (d2g + 1)); + e->vx = e->face == face_right? f1: -f1; + if (!e->grounded) + e->vx = (e->vx << fbits) / f1; + if ( + e->timer > enemy_arrow_cooldown && + p->y > e->y && + absolute((e->x + f1 * 8) - (p->x + f1 * 8)) < f1 * 8 + ) { + Rect r; + r.x = 0; + r.y = 0; + r.w = 3; + r.h = 12; + e->timer = 0; + inst_projectile( + w, + e->x + f1 * 8, + e->y, + &r, + asset_id_arrow_anm + ); + } + } + e->anim = + e->face == face_left? + asset_id_fly_left_anm: + asset_id_fly_right_anm; +} + void update_enemy_move(Enemy* e, World* w) { switch (e->t) { case enemy_demon: update_demon_move(e, w); break; + case enemy_fly: + update_fly_move(e, w); + break; } } @@ -227,6 +300,7 @@ int update_enemy(Enemy* e, World* w) { update_enemy_hurt(e, w); update_enemy_move(e, w); update_enemy_phys(e, &w->map); + e->timer++; return e->hp <= 0; } diff --git a/intermediate/arms.bmp b/intermediate/arms.bmp index 4a0664f..299bd16 100644 Binary files a/intermediate/arms.bmp and b/intermediate/arms.bmp differ diff --git a/intermediate/arrow.anm b/intermediate/arrow.anm new file mode 100644 index 0000000..52447af --- /dev/null +++ b/intermediate/arrow.anm @@ -0,0 +1,2 @@ +1 +{ 222, 34, 3, 12 } diff --git a/intermediate/fly_left.anm b/intermediate/fly_left.anm new file mode 100644 index 0000000..9fc3735 --- /dev/null +++ b/intermediate/fly_left.anm @@ -0,0 +1,11 @@ +1 +{ 0, 112, 16, 16 } +{ 16, 112, 16, 16 } +{ 32, 112, 16, 16 } +{ 48, 112, 16, 16 } +{ 64, 112, 16, 16 } +{ 80, 112, 16, 16 } +{ 64, 112, 16, 16 } +{ 48, 112, 16, 16 } +{ 32, 112, 16, 16 } +{ 16, 112, 16, 16 } diff --git a/intermediate/fly_right.anm b/intermediate/fly_right.anm new file mode 100644 index 0000000..ae4ef6c --- /dev/null +++ b/intermediate/fly_right.anm @@ -0,0 +1,11 @@ +1 +{ 0, 96, 16, 16 } +{ 16, 96, 16, 16 } +{ 32, 96, 16, 16 } +{ 48, 96, 16, 16 } +{ 64, 96, 16, 16 } +{ 80, 96, 16, 16 } +{ 64, 96, 16, 16 } +{ 48, 96, 16, 16 } +{ 32, 96, 16, 16 } +{ 16, 96, 16, 16 } diff --git a/intermediate/npc.bmp b/intermediate/npc.bmp index 9a961a7..ebcd40a 100644 Binary files a/intermediate/npc.bmp and b/intermediate/npc.bmp differ diff --git a/map.c b/map.c index 2acd988..5c0cf78 100644 --- a/map.c +++ b/map.c @@ -152,6 +152,7 @@ void generate_enemies(const Map* m, World* w) { (m->collision[x + y * map_w] == 1 && \ m->collision[x + (y - 1) * map_w] == 0) int x, y, ex = map_w - 1, ey = map_h - 1; + /* enemies on platforms */ for (y = 1; y < ey; y++) { for (x = 1; x < ex; x++) { int plat = -1; @@ -176,6 +177,39 @@ void generate_enemies(const Map* m, World* w) { for (; check && x < ex; x++); } } + /* flying enemies */ + for (y = 1; y < ey; y++) + for (x = 1; x < ex; x++) { + Rect rect = { 0 }; + int i, j; + rect.x = x; + rect.y = y; + for (j = y; j < ey; j++) { + if (m->collision[i + j * map_w]) + rect.h = 0; + for (i = x, rect.w = 0; i < ex && rect.h > 3; i++) { + if (!m->collision[i + j * map_w]) + rect.w++; + if (rect.w > 4) { + Enemy* e; + e = inst_enemy( + w, + enemy_fly, + ((x + (rect.w >> 1)) * map_tile_size) << fbits, + ((y + (rect.h >> 1)) * map_tile_size) << fbits + ); + e->face = get_r() & 1; + x = i; + y = j; + rect.w = 0; + rect.h = 0; + goto found; + } + } + rect.h++; + } + found:; + } } void generate_floor(Map* m, World* w) { diff --git a/obj.h b/obj.h index 98f2ebf..2227a64 100644 --- a/obj.h +++ b/obj.h @@ -79,7 +79,8 @@ int update_effect(Effect* e); void ren_effect(const Effect* e, struct Renderer* r); typedef enum { - enemy_demon + enemy_demon, + enemy_fly } Enemy_Type; typedef struct { @@ -87,7 +88,7 @@ typedef struct { int x, y, vx, vy; int hp, frame, anim; int grounded, headbutted, on_ramp; - int state, inv; + int state, inv, timer; Face face; Rect rect; } Enemy; @@ -116,4 +117,23 @@ int update_deathzone(Deathzone* d); void ren_hud(const struct World* w, struct Renderer* r); +typedef struct { + Rect r, s; + int anim, frame; + int x, y, vx, vy; +} Projectile; + +void init_projectile( + Projectile* p, + int x, + int y, + const Rect* r, + int anim +); +int update_projectile( + Projectile* p, + const struct World* w +); +void ren_projectile(const Projectile* p, struct Renderer* r); + #endif diff --git a/player.c b/player.c index 4a83a91..40ab92b 100644 --- a/player.c +++ b/player.c @@ -201,6 +201,19 @@ void update_player_hurt(Player* p, World* w) { goto took_damage; } } + for (i = 0; i < w->projectile_count; i++) { + const Projectile* pr = &w->projectiles[i]; + Rect r2; + r2.x = pr->x >> fbits; + r2.y = pr->y >> fbits; + r2.w = pr->r.w; + r2.h = pr->r.h; + if (rects_overlap(&r, &r2)) { + p->hp -= enemy_arrow_damage; + inst_effect(w, cx, cy, 0, 0, 32); + goto took_damage; + } + } return; took_damage: p->inv = player_inv_frames; diff --git a/projectile.c b/projectile.c new file mode 100644 index 0000000..ce68e8f --- /dev/null +++ b/projectile.c @@ -0,0 +1,59 @@ +#include "animation.h" +#include "asset.h" +#include "maths.h" +#include "obj.h" +#include "physics.h" +#include "render.h" +#include "world.h" + +void init_projectile( + Projectile* p, + int x, + int y, + const Rect* r, + int anim +) { + p->r = *r; + p->frame = 0; + p->anim = anim; + p->x = x; + p->y = y; + p->vx = 0; + p->vy = 0; + p->s.x = 0; + p->s.y = 0; + p->s.w = 0; + p->s.h = 0; +} + +int update_projectile( + Projectile* p, + const World* w +) { + Rect r = p->r; + int grounded = 1, headbutted = 1, on_ramp = 0; + const Animation* a = get_animation(p->anim); + r.x <<= fbits; + r.y <<= fbits; + r.w <<= fbits; + r.h <<= fbits; + update_body( + &w->map, + &p->x, + &p->y, + &p->vx, + &p->vy, + &grounded, + &headbutted, + &on_ramp, + &r + ); + update_anim(a, &p->frame, &p->s); + return !grounded; +} + +void ren_projectile(const Projectile* p, Renderer* r) { + const Bitmap* bm = get_bitmap(asset_id_arms_img); + ren_map(r, p->x >> fbits, p->y >> fbits, &p->s, bm); +} + diff --git a/world.c b/world.c index 52e2c5a..8d34b21 100644 --- a/world.c +++ b/world.c @@ -6,6 +6,7 @@ void init_world(World* w) { w->enemy_count = 0; w->deathzone_count = 0; w->effect_count = 0; + w->projectile_count = 0; w->frame = 0; w->freeze = 0; init_player(&w->player); @@ -70,6 +71,20 @@ Effect* inst_effect( return e; } +Projectile* inst_projectile( + World* w, + int x, + int y, + const Rect* r, + int anim +) { + Projectile* p; + assert(w->projectile_count < max_projectiles); + p = &w->projectiles[w->projectile_count++]; + init_projectile(p, x, y, r, anim); + return p; +} + void update_world(World* w, const App* a) { int i; if (w->laser.life) @@ -91,6 +106,9 @@ void update_world(World* w, const App* a) { for (i = w->enemy_count - 1; i >= 0; i--) if (update_enemy(&w->enemies[i], w)) w->enemies[i] = w->enemies[--w->enemy_count]; + for (i = w->projectile_count - 1; i >= 0; i--) + if (update_projectile(&w->projectiles[i], w)) + w->projectiles[i] = w->projectiles[--w->projectile_count]; w->frame++; } @@ -104,6 +122,8 @@ void ren_world(const World* w, struct Renderer* r) { ren_effect(&w->effects[i], r); for (i = 0; i < w->enemy_count; i++) ren_enemy(&w->enemies[i], r); + for (i = 0; i < w->projectile_count; i++) + ren_projectile(&w->projectiles[i], r); if (w->laser.life) ren_laser(&w->laser, r); ren_hud(w, r); diff --git a/world.h b/world.h index 81feb6f..a0f6506 100644 --- a/world.h +++ b/world.h @@ -4,10 +4,11 @@ #include "map.h" #include "obj.h" -#define max_particles 32 +#define max_particles 16 #define max_enemies 16 #define max_deathzones 16 #define max_effects 16 +#define max_projectiles 16 struct Renderer; @@ -16,8 +17,9 @@ typedef struct World { Enemy enemies[max_enemies]; Deathzone deathzones[max_deathzones]; Effect effects[max_effects]; + Projectile projectiles[max_projectiles]; int particle_count, enemy_count, effect_count; - int deathzone_count; + int deathzone_count, projectile_count; Player player; Laser laser; Map map; @@ -55,6 +57,13 @@ Effect* inst_effect( int vy, int c ); +Projectile* inst_projectile( + World* w, + int x, + int y, + const Rect* r, + int anim +); void update_world(World* w, const struct App* a); void ren_world(const World* w, struct Renderer* r); -- cgit v1.2.3-54-g00ecf