summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/walker.c199
1 files changed, 157 insertions, 42 deletions
diff --git a/src/walker.c b/src/walker.c
index 9a74816..0d32d57 100644
--- a/src/walker.c
+++ b/src/walker.c
@@ -1,23 +1,36 @@
 #include "main.h"
 #include "entity.h"
 #include "loader.h"
-#include "input.h"
 #include "tilemap.h"
 #include <stdbool.h>
+#include <math.h>
 
 #define SIZE 4
-#define ACCELERATION 2
-#define FRICTION 3
+#define ACCELERATION 1
+#define FRICTION 2
 #define MAX_SPEED 16
-#define GRAVITY 2
-#define JUMP 40
+#define GRAVITY 1
+#define WALKABLE_CHECK_DISTANCE 10
+#define IDLE_TIME 90
+#define ATTACK_DELAY 120
+#define ATTACK_DELAY2 60
+#define ATTACK_REPEAT 60
+
+struct walker_ext {
+	unsigned idle_time, attack_delay, attack_delay2, attack_repeat;
+	signed facing;
+};
 
 enum {
 	WALKER_NONE,
+	WALKER_FALL,
 	WALKER_IDLE,
 	WALKER_PATROL,
-	WALKER_ALERT,
-	WALKER_ATTACK,
+	WALKER_ALERT_IDLE,
+	WALKER_ALERT_CHASE,
+	WALKER_ALERT_RUN,
+	WALKER_ALERT_HALT,
+	WALKER_ALERT_ATTACK,
 };
 
 enum {
@@ -30,11 +43,12 @@ enum {
 	WALKER_A_JUMP,
 	WALKER_A_FALL,
 	WALKER_A_FALL2,
+	WALKER_A_ATTACK,
 };
 
 struct anim walker_anims[] = {
 	{WALKER_A_IDLE2, {0, 0, 16, 16}, 300},
-	{WALKER_A_IDLE, {0, 32, 16, 16}, 2},
+	{WALKER_A_IDLE, {64, 0, 16, 16}, 2},
 	{WALKER_A_WALK2, {0, 0, 16, 16}, 6},
 	{WALKER_A_WALK3, {16, 0, 16, 16}, 6},
 	{WALKER_A_WALK4, {32, 0, 16, 16}, 6},
@@ -42,6 +56,7 @@ struct anim walker_anims[] = {
 	{WALKER_A_JUMP, {16, 0, 16, 16}, 300},
 	{WALKER_A_FALL2, {32, 0, 16, 16}, 15},
 	{WALKER_A_FALL2, {48, 0, 16, 16}, 15},
+	{WALKER_A_ATTACK, {80, 0, 16, 16}, 15},
 };
 
 static int bullet_update(struct projectile *self) {
@@ -71,6 +86,10 @@ static int bullet_update(struct projectile *self) {
 		part->hp = 5;
 		entities.particles++;
 	#endif
+	if (hitbox_overlap(self->hitbox, entities.player[0].hitbox)) {
+		entities.player[0].hurt(entities.player + 0, 1);
+		return 1;
+	}
 	if (self->hp == 0) {
 		return 1;
 		self->state = 0;
@@ -162,7 +181,7 @@ static void anim(struct entity *self, unsigned anim) {
 	self->anim = walker_anims[anim];
 }
 
-static void attack(struct entity *self) {
+static void attack(struct entity const *const self) {
 	entities.projectile[entities.projectiles] = (struct projectile) {
 		.update = bullet_update,
 		.draw = bullet_draw,
@@ -186,64 +205,145 @@ static void attack(struct entity *self) {
 	entities.projectiles++;
 }
 
+static inline int is_walkable(int const x, int const y) {
+	collision_T const front = tilemap_tile(tilemap, from_fixed(x), from_fixed(y) - 4);
+	collision_T const floor = tilemap_tile(tilemap, from_fixed(x), from_fixed(y) + 4);
+	return !collision_solid(front) && collision_solid(floor) && !collision_hazard(floor);
+}
+
+static void walker_free(struct entity *self) {
+	self->state = 0;
+	free(self->ext), self->ext = NULL;
+}
+
 static int walker_update(struct entity *self) {
+	struct walker_ext *const ext = self->ext;
 	if (self->hp <= 0) {
-		self->state = 0;
+		walker_free(self);
 		entities.enemies--;
 		return 1;
 	}
 	switch (self->state) {
+		case WALKER_FALL:
+			if (move(self, 0) != false) {
+				anim(self, WALKER_A_WALK);
+				self->state = WALKER_PATROL;
+			}
+			break;
+		
 		case WALKER_IDLE:
 			if (move(self, 0) == false) {
-				abort();
 				self->velocity.y = 0;
 				anim(self, WALKER_A_FALL);
-				//self->state = WALKER_FALL;
+				self->state = WALKER_FALL;
 			}
 			self->timer--;
 			if (self->timer == 0) {
-				self->facing = -self->facing;
+				ext->facing = -ext->facing;
 				anim(self, WALKER_A_WALK);
 				self->state = WALKER_PATROL;
 			}
+			if (abs(entities.player[0].x - self->x) + abs(entities.player[0].y - self->y) < to_fixed(64)) {
+				self->state = WALKER_ALERT_IDLE;
+				self->timer = ext->attack_delay;
+			}
 			break;
 
 		case WALKER_PATROL:
-			if (move(self, self->facing) == false) {
-				abort();
+			if (move(self, ext->facing) == false) {
 				self->velocity.y = 0;
 				anim(self, WALKER_A_FALL);
-				//self->state = WALKER_FALL;
+				self->state = WALKER_FALL;
 			}
-			if (collision_solid(tilemap_tile(tilemap, from_fixed(self->x) + self->facing * 8, from_fixed(self->y) - 4)) ||
-			!collision_solid(tilemap_tile(tilemap, from_fixed(self->x) + self->facing * 8, from_fixed(self->y) + 4))) {
+			if (!is_walkable(self->x + to_fixed(ext->facing * WALKABLE_CHECK_DISTANCE), self->y)) {
 				anim(self, WALKER_A_IDLE);
 				self->state = WALKER_IDLE;
-				self->timer = 30;
+				self->timer = ext->idle_time;
+			}
+			if (abs(entities.player[0].x - self->x) + abs(entities.player[0].y - self->y) < to_fixed(64)) {
+				anim(self, WALKER_A_IDLE);
+				self->state = WALKER_ALERT_IDLE;
+				self->timer = ext->attack_delay;
 			}
 			break;
 		
-		case WALKER_ALERT:
-			attack(self);
-			if (!input_a(input_held)) {
-				self->velocity.y /= 2;
-				anim(self, WALKER_A_FALL);
-				//self->state = WALKER_FALL;
+		case WALKER_ALERT_IDLE:
+			move(self, 0);
+			ext->facing = self->x > entities.player[0].x? -1: 1;
+			self->facing = ext->facing;
+			if (abs(entities.player[0].x - self->x) > to_fixed(48)) {
+				if (is_walkable(self->x + to_fixed(ext->facing * WALKABLE_CHECK_DISTANCE), self->y)) {
+					anim(self, WALKER_A_WALK);
+					self->state = WALKER_ALERT_CHASE;
+				}
+			} else if (abs(entities.player[0].x - self->x) < to_fixed(32)) {
+				if (is_walkable(self->x + to_fixed(-ext->facing * WALKABLE_CHECK_DISTANCE), self->y)) {
+					ext->facing = -ext->facing;
+					anim(self, WALKER_A_WALK);
+					self->state = WALKER_ALERT_RUN;
+				} else if (is_walkable(self->x + to_fixed(ext->facing * WALKABLE_CHECK_DISTANCE), self->y)) {
+					anim(self, WALKER_A_WALK);
+					self->state = WALKER_ALERT_RUN;
+				}
 			}
-			if (self->velocity.y > 0) {
-				anim(self, WALKER_A_FALL);
-				//self->state = WALKER_FALL;
+			if (abs(entities.player[0].x - self->x) + abs(entities.player[0].y - self->y) > to_fixed(96)) {
+				self->state = WALKER_IDLE;
 			}
-			if (move(self, 0) == true) {
+			self->timer--;
+			if (self->timer == 0) {
+				anim(self, WALKER_A_ATTACK);
+				self->state = WALKER_ALERT_ATTACK;
+				self->timer = ext->attack_delay2;
+			}
+			break;
+
+		case WALKER_ALERT_CHASE:
+			move(self, ext->facing);
+			if (abs(entities.player[0].x - self->x) < to_fixed(40) || !is_walkable(self->x + to_fixed(ext->facing * WALKABLE_CHECK_DISTANCE), self->y)) {
 				anim(self, WALKER_A_IDLE);
-				self->state = WALKER_IDLE;
+				self->state = WALKER_ALERT_IDLE;
+				self->timer = ext->attack_delay;
+			}
+			break;
+
+		case WALKER_ALERT_RUN:
+			move(self, ext->facing);
+			if (abs(entities.player[0].x - self->x) > to_fixed(40)) {
+				anim(self, WALKER_A_IDLE);
+				self->state = WALKER_ALERT_IDLE;
+				self->timer = ext->attack_delay;
+			}
+			if (!is_walkable(self->x + to_fixed(ext->facing * WALKABLE_CHECK_DISTANCE), self->y)) {
+				anim(self, WALKER_A_IDLE);
+				self->state = WALKER_ALERT_HALT;
+			}
+			break;
+
+		case WALKER_ALERT_HALT: // hack workaround for a weird issue where enemies would rapidly switch between moving and idling
+			move(self, 0);
+			if (self->velocity.x == 0) {
+				ext->facing = -ext->facing;
+				anim(self, WALKER_A_WALK);
+				self->state = WALKER_ALERT_RUN;
 			}
 			break;
 		
-		case WALKER_ATTACK:
-			if (move(self, 0) == true) {
+		case WALKER_ALERT_ATTACK:
+			ext->facing = self->x > entities.player[0].x? -1: 1;
+			self->facing = ext->facing;
+			if (self->timer > 0) {
+				self->timer--;
+			}
+			if (self->timer == 0 && self->y + to_fixed(4) > entities.player[0].y && self->y <= entities.player[0].y) {
+				attack(self);
+				anim(self, WALKER_A_IDLE);
+				self->state = WALKER_ALERT_IDLE;
+				self->timer = ext->attack_repeat;
+			}
+			if (abs(entities.player[0].x - self->x) + abs(entities.player[0].y - self->y) > to_fixed(96)) {
 				anim(self, WALKER_A_IDLE);
 				self->state = WALKER_IDLE;
+				self->timer = ext->idle_time;
 			}
 			break;
 	}
@@ -288,22 +388,19 @@ static int walker_draw(struct entity *self, int camX, int camY) {
 	return 0;
 }
 
-static void walker_free(struct entity *self) {
-	self->state = 0;
-}
-
 struct entity *walker_new(struct entities *entities) {
-	entities->enemy[entities->enemies] = (struct entity) {
+	struct entity *self = entities->enemy + entities->enemies;
+	*self = (struct entity) {
 		.update = walker_update,
 		.hurt = walker_hurt,
 		.draw = walker_draw,
 		.free = walker_free,
 		.x = 0, .y = 0,
-		.velocity = {.x = 0, .y = to_fixed(1)},
+		.velocity = {.x = 0, .y = 0},
 		.hitbox = {
 			0, 0, 0, 0,
 		},
-		.state = WALKER_PATROL,
+		.state = WALKER_FALL,
 		.hp = 3,
 		.timer = 0,
 		.facing = 1,
@@ -312,16 +409,34 @@ struct entity *walker_new(struct entities *entities) {
 		.texture = NULL,
 		.ext = NULL,
 	};
-	anim(entities->enemy + entities->enemies, WALKER_A_IDLE);
-	entities->enemy[entities->enemies].texture = res_get_texture("fywi").data;
-	return entities->enemy + entities->enemies++;
+	self->ext = malloc(sizeof (struct walker_ext));
+	*(struct walker_ext *) self->ext = (struct walker_ext) {
+		.idle_time = IDLE_TIME,
+		.attack_delay = ATTACK_DELAY,
+		.attack_delay2 = ATTACK_DELAY2,
+		.attack_repeat = ATTACK_REPEAT,
+		.facing = 1,
+	};
+	anim(self, WALKER_A_FALL);
+	self->texture = res_get_texture("enemy").data;
+	entities->enemies++;
+	return self;
 }
 
 int walker_property(struct entity *const restrict self, char const *const restrict property, char const *const restrict value) {
+	struct walker_ext *const ext = self->ext;
 	if (strcmp(property, "x") == 0) {
 		self->x = to_fixed(atoi(value));
 	} else if (strcmp(property, "y") == 0) {
 		self->y = to_fixed(atoi(value));
+	} else if (strcmp(property, "idle time") == 0) {
+		ext->idle_time = atoi(value);
+	} else if (strcmp(property, "attack delay") == 0) {
+		ext->attack_delay = atoi(value);
+	} else if (strcmp(property, "attack delay 2") == 0) {
+		ext->attack_delay2 = atoi(value);
+	} else if (strcmp(property, "attack repeat") == 0) {
+		ext->attack_repeat = atoi(value);
 	} else {
 		return 1;
 	}