From eb5b0d4d8d40dc4f564ad2fd6f69eaf2e108322a Mon Sep 17 00:00:00 2001 From: zlago Date: Wed, 9 Oct 2024 18:52:26 +0200 Subject: finish walking enemy --- src/walker.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 157 insertions(+), 42 deletions(-) (limited to 'src/walker.c') 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 +#include #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; } -- cgit 1.4.1-2-gfad0