diff options
| author | zlago | 2024-10-09 18:52:26 +0200 | 
|---|---|---|
| committer | zlago | 2024-10-09 19:53:27 +0200 | 
| commit | eb5b0d4d8d40dc4f564ad2fd6f69eaf2e108322a (patch) | |
| tree | fba6327741170d06ed2aebb6de9fa1136f8124fd | |
| parent | 45a781a5af1107c7511fba7d4b4b66588cbf008f (diff) | |
finish walking enemy
| -rw-r--r-- | src/walker.c | 199 | 
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;  	} | 
