aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--1bitjam.c1
-rw-r--r--Makefile13
-rw-r--r--config.h5
-rw-r--r--deathzone.c23
-rw-r--r--enemy.c207
-rw-r--r--intermediate/demon_fly_left.anm2
-rw-r--r--intermediate/demon_fly_right.anm2
-rw-r--r--intermediate/demon_idle_left.anm5
-rw-r--r--intermediate/demon_idle_right.anm5
-rw-r--r--intermediate/demon_jump_left.anm5
-rw-r--r--intermediate/demon_jump_right.anm5
-rw-r--r--intermediate/demon_walk_left.anm5
-rw-r--r--intermediate/demon_walk_right.anm5
-rw-r--r--intermediate/npc.bmpbin0 -> 37002 bytes
-rw-r--r--map.c3
-rw-r--r--obj.h35
-rw-r--r--player.c10
-rw-r--r--world.c39
-rw-r--r--world.h24
19 files changed, 390 insertions, 4 deletions
diff --git a/1bitjam.c b/1bitjam.c
index 8c63aa5..4cbd93e 100644
--- a/1bitjam.c
+++ b/1bitjam.c
@@ -27,6 +27,7 @@ int entrypoint(int argc, const char** argv, Arena* m) {
world = arena_alloc(m, sizeof world);
init_world(world);
generate_floor(&world->map, 0);
+ inst_enemy(world, enemy_demon, 100 << fbits, 100 << fbits);
while (a->o) {
fps_begin(&f);
while (f.now >= f.next && a->o) {
diff --git a/Makefile b/Makefile
index 7be7f78..c0a7b45 100644
--- a/Makefile
+++ b/Makefile
@@ -46,6 +46,8 @@ sources = \
1bitjam.c \
animation.c \
asset.c \
+ deathzone.c \
+ enemy.c \
map.c \
maths.c \
memory.c \
@@ -59,11 +61,20 @@ sources = \
image_sources = \
$(int_dir)/arms.bmp \
+ $(int_dir)/npc.bmp \
$(int_dir)/guy.bmp \
$(int_dir)/map.bmp \
$(int_dir)/mask.bmp \
anim_sources = \
+ $(int_dir)/demon_fly_left.anm \
+ $(int_dir)/demon_fly_right.anm \
+ $(int_dir)/demon_idle_left.anm \
+ $(int_dir)/demon_idle_right.anm \
+ $(int_dir)/demon_jump_left.anm \
+ $(int_dir)/demon_jump_right.anm \
+ $(int_dir)/demon_walk_left.anm \
+ $(int_dir)/demon_walk_right.anm \
$(int_dir)/guy_fall_left.anm \
$(int_dir)/guy_fall_right.anm \
$(int_dir)/guy_idle_left.anm \
@@ -74,8 +85,8 @@ anim_sources = \
$(int_dir)/guy_run_right.anm \
$(int_dir)/guy_slash_left.anm \
$(int_dir)/guy_slash_right.anm \
- $(int_dir)/slash_right.anm \
$(int_dir)/slash_left.anm \
+ $(int_dir)/slash_right.anm \
objects = $(sources:%.c=%.o)
images = $(image_sources:$(int_dir)/%.bmp=$(data_dir)/%.img)
diff --git a/config.h b/config.h
index 2fc4561..f2b0699 100644
--- a/config.h
+++ b/config.h
@@ -38,5 +38,10 @@
#define player_shoot_cooldown 20
#define player_attack_frames 10
#define player_lunge_force (f1)
+#define player_slash_damage 1
+
+#define enemy_demon_move_force 32
+#define enemy_demon_attack_dist f1
+#define enemy_demon_jump_force (5 << fbits)
#endif
diff --git a/deathzone.c b/deathzone.c
new file mode 100644
index 0000000..cbc0215
--- /dev/null
+++ b/deathzone.c
@@ -0,0 +1,23 @@
+#include "obj.h"
+
+void init_deathzone(
+ Deathzone* d,
+ const Rect* r,
+ int vx,
+ int vy,
+ int hp,
+ int life,
+ int friendly
+) {
+ d->r = *r;
+ d->vx = vx;
+ d->vy = vy;
+ d->hp = hp;
+ d->life = life;
+ d->friendly = friendly;
+}
+
+int update_deathzone(Deathzone* d) {
+ d->life--;
+ return d->life <= 0;
+}
diff --git a/enemy.c b/enemy.c
new file mode 100644
index 0000000..bc0bbba
--- /dev/null
+++ b/enemy.c
@@ -0,0 +1,207 @@
+#include "animation.h"
+#include "asset.h"
+#include "config.h"
+#include "maths.h"
+#include "obj.h"
+#include "physics.h"
+#include "render.h"
+#include "world.h"
+
+enum {
+ enemy_state_patrol,
+ enemy_state_approach,
+ enemy_state_approach2,
+ enemy_state_attack
+};
+
+int get_enemy_hp(Enemy_Type t) {
+ switch (t) {
+ case enemy_demon:
+ return 3;
+ }
+ return 0;
+}
+
+void get_enemy_rect(Enemy_Type t, Rect* d) {
+ switch (t) {
+ case enemy_demon:
+ d->x = 2;
+ d->y = 2;
+ d->w = 12;
+ d->h = 14;
+ break;
+ }
+}
+
+void init_enemy(Enemy* e, Enemy_Type t, int x, int y) {
+ e->t = t;
+ e->x = x;
+ e->y = y;
+ e->vx = 0;
+ e->vy = 0;
+ e->hp = get_enemy_hp(t);
+ e->frame = 0;
+ e->anim = asset_id_demon_idle_left_anm;
+ e->face = face_left;
+ e->state = enemy_state_patrol;
+}
+
+void update_enemy_anim(Enemy* e) {
+ int done;
+ const Animation* anim = get_animation(e->anim);
+ done = update_anim(anim, &e->frame, &e->rect);
+ if (
+ done &&
+ e->t == enemy_demon &&
+ e->state == enemy_state_approach
+ ) {
+ e->state = enemy_state_approach2;
+ }
+}
+
+void update_enemy_phys(Enemy* e, const Map* map) {
+ Rect r;
+ get_enemy_rect(e->t, &r);
+ r.x <<= fbits;
+ r.y <<= fbits;
+ r.w <<= fbits;
+ r.h <<= fbits;
+ update_body(
+ map,
+ &e->x,
+ &e->y,
+ &e->vx,
+ &e->vy,
+ &e->grounded,
+ &e->headbutted,
+ &e->on_ramp,
+ &r
+ );
+}
+
+void update_enemy_hurt(Enemy* e, const World* w) {
+ Rect a;
+ int i;
+ get_enemy_rect(e->t, &a);
+ a.x += e->x >> fbits;
+ a.y += e->y >> fbits;
+ for (i = 0; i < w->deathzone_count; i++) {
+ const Deathzone* dz = &w->deathzones[i];
+ if (rects_overlap(&a, &dz->r)) {
+ e->hp -= dz->hp;
+ e->vx -= dz->vx;
+ e->vy -= dz->vy;
+ }
+ }
+}
+
+int tile_at(const Map* m, int x, int y) {
+ return
+ x < 0 ||
+ y < 0 ||
+ x >= map_w ||
+ y >= map_h ||
+ m->collision[x + y * map_w] == 1;
+}
+
+void update_demon_move(Enemy* e, const World* w) {
+ int nanim = e->anim;
+ const Player* p = &w->player;
+ if (e->state == enemy_state_patrol) {
+ int dx = e->x - p->x;
+ int dy = e->y - p->y;
+ int dx2 = dx >> fbits;
+ int dy2 = dy >> fbits;
+ int dist = fxsqrt(dx * dx2 + dy * dy2) >> fbits;
+ if (e->grounded < 3) {
+ int tx, ty =
+ ((e->y >> fbits) - (map_tile_size >> 1)) /
+ map_tile_size;
+ int ty2 = ty + 2;
+ 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(&w->map, tx, ty) ||
+ (ty2 < map_h && !tile_at(&w->map, tx, ty2))
+ )
+ e->face = !e->face;
+ if (e->face == face_left) {
+ e->vx -= enemy_demon_move_force;
+ } else {
+ e->vx += enemy_demon_move_force;
+ }
+ }
+ if (dist < enemy_demon_attack_dist) {
+ e->state = enemy_state_approach;
+ }
+ } else if (e->state == enemy_state_approach) {
+ if (p->x < e->x)
+ e->face = face_left;
+ else
+ e->face = face_right;
+ } else if (e->state == enemy_state_approach2) {
+ if (e->grounded < 3) {
+ e->vx = (p->x - e->vx) / 100;
+ if (e->face == face_left)
+ e->vx = -e->vx;
+ e->vy = -enemy_demon_jump_force;
+ e->grounded = 100;
+ e->state = enemy_state_attack;
+ }
+ } else if (e->state == enemy_state_attack) {
+ if (e->grounded < 3) {
+ e->state = enemy_state_patrol;
+ }
+ }
+ switch (e->face) {
+ case face_left:
+ nanim =
+ e->state == enemy_state_patrol?
+ asset_id_demon_walk_left_anm:
+ e->state == enemy_state_approach?
+ asset_id_demon_jump_left_anm:
+ asset_id_demon_idle_left_anm;
+ break;
+ case face_right:
+ nanim =
+ e->state == enemy_state_patrol?
+ asset_id_demon_walk_right_anm:
+ e->state == enemy_state_approach?
+ asset_id_demon_jump_right_anm:
+ asset_id_demon_idle_right_anm;
+ break;
+ }
+ if (nanim != e->anim) {
+ e->frame = 0;
+ e->anim = nanim;
+ }
+}
+
+void update_enemy_move(Enemy* e, const World* w) {
+ switch (e->t) {
+ case enemy_demon:
+ update_demon_move(e, w);
+ break;
+ }
+}
+
+int update_enemy(Enemy* e, World* w) {
+ update_enemy_anim(e);
+ update_enemy_hurt(e, w);
+ update_enemy_move(e, w);
+ update_enemy_phys(e, &w->map);
+ return e->hp <= 0;
+}
+
+void ren_enemy(const Enemy* e, struct Renderer* r) {
+ const Bitmap* b = get_bitmap(asset_id_npc_img);
+ ren_map(r, e->x >> fbits, e->y >> fbits, &e->rect, b);
+}
diff --git a/intermediate/demon_fly_left.anm b/intermediate/demon_fly_left.anm
new file mode 100644
index 0000000..686a35f
--- /dev/null
+++ b/intermediate/demon_fly_left.anm
@@ -0,0 +1,2 @@
+1
+{ 64, 64, 16, 16 }
diff --git a/intermediate/demon_fly_right.anm b/intermediate/demon_fly_right.anm
new file mode 100644
index 0000000..8a62ff3
--- /dev/null
+++ b/intermediate/demon_fly_right.anm
@@ -0,0 +1,2 @@
+1
+{ 64, 80, 16, 16 }
diff --git a/intermediate/demon_idle_left.anm b/intermediate/demon_idle_left.anm
new file mode 100644
index 0000000..0a4ac8d
--- /dev/null
+++ b/intermediate/demon_idle_left.anm
@@ -0,0 +1,5 @@
+15
+{ 0, 0, 16, 16 }
+{ 16, 0, 16, 16 }
+{ 32, 0, 16, 16 }
+{ 48, 0, 16, 16 }
diff --git a/intermediate/demon_idle_right.anm b/intermediate/demon_idle_right.anm
new file mode 100644
index 0000000..a8edeb0
--- /dev/null
+++ b/intermediate/demon_idle_right.anm
@@ -0,0 +1,5 @@
+15
+{ 0, 16, 16, 16 }
+{ 16, 16, 16, 16 }
+{ 32, 16, 16, 16 }
+{ 48, 16, 16, 16 }
diff --git a/intermediate/demon_jump_left.anm b/intermediate/demon_jump_left.anm
new file mode 100644
index 0000000..da710dd
--- /dev/null
+++ b/intermediate/demon_jump_left.anm
@@ -0,0 +1,5 @@
+7
+{ 0, 64, 16, 16 }
+{ 16, 64, 16, 16 }
+{ 32, 64, 16, 16 }
+{ 48, 64, 16, 16 }
diff --git a/intermediate/demon_jump_right.anm b/intermediate/demon_jump_right.anm
new file mode 100644
index 0000000..bb01bbd
--- /dev/null
+++ b/intermediate/demon_jump_right.anm
@@ -0,0 +1,5 @@
+7
+{ 0, 80, 16, 16 }
+{ 16, 80, 16, 16 }
+{ 32, 80, 16, 16 }
+{ 48, 80, 16, 16 }
diff --git a/intermediate/demon_walk_left.anm b/intermediate/demon_walk_left.anm
new file mode 100644
index 0000000..8a86a59
--- /dev/null
+++ b/intermediate/demon_walk_left.anm
@@ -0,0 +1,5 @@
+15
+{ 0, 32, 16, 16 }
+{ 16, 32, 16, 16 }
+{ 32, 32, 16, 16 }
+{ 16, 32, 16, 16 }
diff --git a/intermediate/demon_walk_right.anm b/intermediate/demon_walk_right.anm
new file mode 100644
index 0000000..5312b7a
--- /dev/null
+++ b/intermediate/demon_walk_right.anm
@@ -0,0 +1,5 @@
+15
+{ 0, 48, 16, 16 }
+{ 16, 48, 16, 16 }
+{ 32, 48, 16, 16 }
+{ 16, 48, 16, 16 }
diff --git a/intermediate/npc.bmp b/intermediate/npc.bmp
new file mode 100644
index 0000000..9a961a7
--- /dev/null
+++ b/intermediate/npc.bmp
Binary files differ
diff --git a/map.c b/map.c
index 855f636..c2d9e0d 100644
--- a/map.c
+++ b/map.c
@@ -34,6 +34,9 @@ void generate_floor(Map* m, int seed) {
set(2, 11);
set(3, 11);
set(4, 11);
+ set(6, 11);
+ set(7, 11);
+ set(8, 11);
set(19, 13);
set(19, 12);
set(19, 11);
diff --git a/obj.h b/obj.h
index 57172f5..8824a2d 100644
--- a/obj.h
+++ b/obj.h
@@ -43,4 +43,39 @@ void init_particle(Particle* p, int x, int y, int anim, int bmp);
int update_particle(Particle* p);
void ren_particle(const Particle* p, struct Renderer* r);
+typedef enum {
+ enemy_demon
+} Enemy_Type;
+
+typedef struct {
+ Enemy_Type t;
+ int x, y, vx, vy;
+ int hp, frame, anim;
+ int grounded, headbutted, on_ramp;
+ int state;
+ Face face;
+ Rect rect;
+} Enemy;
+
+void init_enemy(Enemy* e, Enemy_Type t, int x, int y);
+int update_enemy(Enemy* e, struct World* w);
+void ren_enemy(const Enemy* e, struct Renderer* r);
+
+typedef struct {
+ Rect r;
+ int vx, vy;
+ int hp, life, friendly;
+} Deathzone;
+
+void init_deathzone(
+ Deathzone* d,
+ const Rect* r,
+ int vx,
+ int vy,
+ int hp,
+ int life,
+ int friendly
+);
+int update_deathzone(Deathzone* d);
+
#endif
diff --git a/player.c b/player.c
index 67a4817..e965c66 100644
--- a/player.c
+++ b/player.c
@@ -94,18 +94,26 @@ void update_player_move(Player* p, const App* a, World* w) {
play_sound(500);
}
if (p->cooldown <= 0 && btn_just_pressed(a, btn_shoot)) {
- int sx, sy;
+ int sx, sy, vx;
int anim;
+ Rect dz;
if (p->face == face_left) {
anim = asset_id_slash_left_anm;
sx = (p->x >> fbits) - 45;
sy = (p->y >> fbits);
+ vx = f1 * 3;
} else {
anim = asset_id_slash_right_anm;
sx = (p->x >> fbits) + 16;
sy = (p->y >> fbits);
+ vx = -f1 * 3;
}
+ dz.x = sx;
+ dz.y = sy;
+ dz.w = 45;
+ dz.h = 17;
inst_particle(w, sx, sy, anim, asset_id_arms_img);
+ inst_deathzone(w, &dz, vx, 0, player_slash_damage, 1, 1);
p->cooldown = 10;
p->slashing = get_animation(asset_id_guy_slash_left_anm)->fc;
}
diff --git a/world.c b/world.c
index e7777fd..aba9bfb 100644
--- a/world.c
+++ b/world.c
@@ -3,6 +3,8 @@
void init_world(World* w) {
w->particle_count = 0;
+ w->enemy_count = 0;
+ w->deathzone_count = 0;
init_player(&w->player);
}
@@ -20,12 +22,47 @@ Particle* inst_particle(
return p;
}
+Enemy* inst_enemy(
+ World* w,
+ Enemy_Type t,
+ int x,
+ int y
+) {
+ Enemy* e;
+ assert(w->enemy_count < max_enemies);
+ e = &w->enemies[w->enemy_count++];
+ init_enemy(e, t, x, y);
+ return e;
+}
+
+Deathzone* inst_deathzone(
+ World* w,
+ const Rect* r,
+ int vx,
+ int vy,
+ int hp,
+ int life,
+ int friendly
+) {
+ Deathzone* d;
+ assert(w->deathzone_count < max_deathzones);
+ d = &w->deathzones[w->deathzone_count++];
+ init_deathzone(d, r, vx, vy, hp, life, friendly);
+ return d;
+}
+
void update_world(World* w, const App* a) {
int i;
update_player(&w->player, w, a, &w->map);
for (i = w->particle_count - 1; i >= 0; i--)
if (update_particle(&w->particles[i]))
w->particles[i] = w->particles[--w->particle_count];
+ 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->deathzone_count - 1; i >= 0; i--)
+ if (update_deathzone(&w->deathzones[i]))
+ w->deathzones[i] = w->deathzones[--w->deathzone_count];
}
void ren_world(const World* w, struct Renderer* r) {
@@ -34,4 +71,6 @@ void ren_world(const World* w, struct Renderer* r) {
render_map(&w->map, r);
for (i = 0; i < w->particle_count; i++)
ren_particle(&w->particles[i], r);
+ for (i = 0; i < w->enemy_count; i++)
+ ren_enemy(&w->enemies[i], r);
}
diff --git a/world.h b/world.h
index bd3df31..14e2447 100644
--- a/world.h
+++ b/world.h
@@ -5,12 +5,17 @@
#include "obj.h"
#define max_particles 32
+#define max_enemies 16
+#define max_deathzones 16
struct Renderer;
typedef struct World {
Particle particles[max_particles];
- int particle_count;
+ Enemy enemies[max_enemies];
+ Deathzone deathzones[max_deathzones];
+ int particle_count, enemy_count;
+ int deathzone_count;
Player player;
Map map;
} World;
@@ -23,7 +28,22 @@ Particle* inst_particle(
int anim,
int bmp
);
-void update_world(World* w, const App* a);
+Enemy* inst_enemy(
+ World* w,
+ Enemy_Type t,
+ int x,
+ int y
+);
+Deathzone* inst_deathzone(
+ World* w,
+ const Rect* r,
+ int vx,
+ int vy,
+ int hp,
+ int life,
+ int friendly
+);
+void update_world(World* w, const struct App* a);
void ren_world(const World* w, struct Renderer* r);
#endif