diff options
-rw-r--r-- | src/disk.c | 2 | ||||
-rw-r--r-- | src/funsinit.c | 3 | ||||
-rw-r--r-- | src/gun.h | 7 | ||||
-rw-r--r-- | src/hvy_guns.c | 449 | ||||
-rw-r--r-- | src/pacer.c | 330 | ||||
-rw-r--r-- | src/res/big.tmx | 29 | ||||
-rw-r--r-- | src/res/pacer.ase | bin | 0 -> 865 bytes | |||
-rw-r--r-- | src/save.c | 1 |
8 files changed, 807 insertions, 14 deletions
diff --git a/src/disk.c b/src/disk.c index 5231953..084533f 100644 --- a/src/disk.c +++ b/src/disk.c @@ -93,7 +93,7 @@ struct entity *save_new(struct entities *entities) { .timer = 0, .ext = NULL, }; - self->anim = save_anims[SAVE_A_SPIN]; + self->anim = save_anims[SAVE_A_IDLE]; self->texture = res_get_texture("save").data; entities->enemies++; return self; diff --git a/src/funsinit.c b/src/funsinit.c index 50469c6..a323b1a 100644 --- a/src/funsinit.c +++ b/src/funsinit.c @@ -8,6 +8,8 @@ void *walker_new(struct entities *entities); int walker_property(void *const restrict entity, char const *const restrict property, char const *const restrict value); void *flier_new(struct entities *entities); int flier_property(void *const restrict entity, char const *const restrict property, char const *const restrict value); +void *pacer_new(struct entities *entities); +int pacer_property(void *const restrict entity, char const *const restrict property, char const *const restrict value); void *warp_new(struct entities *entities); int warp_property(void *const restrict entity, char const *const restrict property, char const *const restrict value); @@ -16,5 +18,6 @@ void funs_init(void) { res_push_fun(save_new, save_property, "save"); res_push_fun(walker_new, walker_property, "walker"); res_push_fun(flier_new, flier_property, "flier"); + res_push_fun(pacer_new, pacer_property, "pacer"); res_push_fun(warp_new, warp_property, "warp"); } diff --git a/src/gun.h b/src/gun.h new file mode 100644 index 0000000..6dac3d7 --- /dev/null +++ b/src/gun.h @@ -0,0 +1,7 @@ +#pragma once + +struct gun { + int (*update)(struct gun *self, struct entity *parent, struct entity *target); + int timer, counter; + int x, y; // offset from the parents center +}; diff --git a/src/hvy_guns.c b/src/hvy_guns.c new file mode 100644 index 0000000..43fd1f1 --- /dev/null +++ b/src/hvy_guns.c @@ -0,0 +1,449 @@ +#include "loader.h" +#include "tilemap.h" +#include <math.h> +#include "particles.h" +#include "gun.h" + +#define POS_X (parent->x + self->x * parent->facing) +#define POS_Y (parent->y + self->y) + +#define HVY_BLASTER_RELOAD 120 + +#define HVY_SHOTGUN_RELOAD 150 + +#define HVY_REPEATER_RELOAD 60 +#define HVY_REPEATER_ROUNDS 3 + +#define HVY_CHAINGUN_RELOAD 30 +#define HVY_CHAINGUN_MIN_RELOAD 5 + +static int bullet_update(struct projectile *self) { + self->x += self->velocity.x; + if (collision_solid(tilemap_tile(tilemap, from_fixed(self->x), from_fixed(self->y)))) { + self->velocity.x = -self->velocity.x; + self->x += self->velocity.x; + } + + self->y += self->velocity.y; + if (collision_solid(tilemap_tile(tilemap, from_fixed(self->x), from_fixed(self->y)))) { + self->velocity.y = -self->velocity.y; + self->y += self->velocity.y; + } + + self->hp--; + int x = from_fixed(self->x); + int y = from_fixed(self->y); + self->hitbox = (struct hitbox) {.left = x, .right = x, .top = y, .bottom = y}; + if (hitbox_overlap(self->hitbox, entities.player[0].hitbox)) { + if (entities.player[0].hurt(entities.player + 0, (struct damage) {1, 60})) { + //return 1; + } + } + if (self->hp == 0) { + return 1; + self->state = 0; + } + return 0; +} + +static int projectile_update(struct projectile *self) { + self->x += self->velocity.x; + if (collision_solid(tilemap_tile(tilemap, from_fixed(self->x), from_fixed(self->y)))) { + goto explod; + } + + self->y += self->velocity.y; + if (collision_solid(tilemap_tile(tilemap, from_fixed(self->x), from_fixed(self->y)))) { + goto explod; + } + + self->hp--; + int x = from_fixed(self->x); + int y = from_fixed(self->y); + self->hitbox = (struct hitbox) {.left = x, .right = x, .top = y, .bottom = y}; + if (hitbox_overlap(self->hitbox, entities.player[0].hitbox)) { + entities.player[0].hurt(entities.player + 0, (struct damage) {1, 60}); + goto explod; + } + if (self->hp == 0) { + goto explod; + } + return 0; + explod: + for (float r = 0; r < M_PI * 2; r += M_PI / 6) { + struct particle *part = entities.particle + entities.particles; + part->x = self->x; + part->y = self->y; + part->velocity = (struct vec2) {sin(r) * 24, cos(r) * 24}; + part->rect = particle_red; + part->acceleration = (struct vec2) {0, 0}; + part->hp = 15; + entities.particles++; + } + return 1; +} + +static int laser_update(struct projectile *self) { + for (int i = 0; i < 15; i++) { + self->x += self->velocity.x; + if (collision_solid(tilemap_tile(tilemap, from_fixed(self->x), from_fixed(self->y)))) { + self->velocity.x = -self->velocity.x; + self->x += self->velocity.x; + } + + self->y += self->velocity.y; + if (collision_solid(tilemap_tile(tilemap, from_fixed(self->x), from_fixed(self->y)))) { + self->velocity.y = -self->velocity.y; + self->y += self->velocity.y; + } + + int x = from_fixed(self->x); + int y = from_fixed(self->y); + self->hitbox = (struct hitbox) {.left = x, .right = x, .top = y, .bottom = y}; + if (hitbox_overlap(self->hitbox, entities.player[0].hitbox)) { + if (entities.player[0].hurt(entities.player + 0, (struct damage) {1, 60})) { + return 1; + } + } + struct particle *part = entities.particle + entities.particles; + part->x = self->x; + part->y = self->y; + part->velocity = (struct vec2) {0, 0}; + part->rect = particle_red; + part->acceleration = (struct vec2) {0, 0}; + part->hp = i & 15; + entities.particles++; + } + self->hp -= 15; + if (self->hp <= 0) { + return 1; + self->state = 0; + } + return 0; +} + +static int bullet_draw(struct projectile *self, int camX, int camY) { + SDL_Rect const *rect; + if (self->hp & 0x2) { + rect = &particle_red; + } else { + rect = &particle_white; + } + SDL_RenderCopy(renderer, self->texture, rect, &(SDL_Rect) {from_fixed(self->x) - camX - 1, from_fixed(self->y) - camY - 1, 4, 4}); + SDL_RenderCopy(renderer, self->texture, rect, &(SDL_Rect) {from_fixed(self->x - self->velocity.x) - camX - 1, from_fixed(self->y - self->velocity.y) - camY - 1, 4, 4}); + //SDL_RenderFillRect(renderer, &(SDL_Rect) {from_fixed(self->x) - camX - 8, from_fixed(self->y) - camY - 12, 16, 16}); + return 0; +} + +static void bullet_free(struct projectile *self) {} + +int hvy_blaster(struct gun *self, struct entity *parent, struct entity *target) { + if (self->timer > 0) { + self->timer--; + return 0; + } + if (target != NULL) { + entities.projectile[entities.projectiles] = (struct projectile) { + .update = bullet_update, + .draw = bullet_draw, + .free = bullet_free, + .x = POS_X, .y = POS_Y, + .velocity = (struct vec2) {parent->velocity.x + to_fixed(parent->facing), 1}, + .hitbox = { + 0, 0, 0, 0, + }, + .state = 1, + .hp = 300, + .timer = 0, + .facing = parent->facing, + .faction = parent->faction, + .iframes = 0, + .texture = NULL, + .ext = NULL, + }; + //anim(entities->projectile + entities->projectiles, PACER_A_IDLE); + entities.projectile[entities.projectiles].texture = res_get_texture("particles").data; + entities.projectiles++; + self->timer = HVY_BLASTER_RELOAD; + } + return 1; +} + +void hvy_blaster_new(struct gun *self, int x, int y) { + self->update = hvy_blaster; + self->timer = HVY_BLASTER_RELOAD; + self->counter = 0; + self->x = to_fixed(x); + self->y = to_fixed(y); +} + +int hvy_shotgun(struct gun *self, struct entity *parent, struct entity *target) { + if (self->timer > 0) { + self->timer--; + return 0; + } + if (target != NULL) { + float angle = atan2(target->x - POS_X, target->y - POS_Y); + for (int i = -2; i <= 2; i++) { + entities.projectile[entities.projectiles] = (struct projectile) { + .update = bullet_update, + .draw = bullet_draw, + .free = bullet_free, + .x = POS_X, .y = POS_Y, + .velocity = (struct vec2) {parent->velocity.x + to_fixed(sin(angle + i * 0.1)), to_fixed(cos(angle + i * 0.1))}, + .hitbox = { + 0, 0, 0, 0, + }, + .state = 1, + .hp = 300, + .timer = 0, + .facing = parent->facing, + .faction = parent->faction, + .iframes = 0, + .texture = NULL, + .ext = NULL, + }; + //anim(entities->projectile + entities->projectiles, PACER_A_IDLE); + entities.projectile[entities.projectiles].texture = res_get_texture("particles").data; + entities.projectiles++; + } + self->timer = HVY_SHOTGUN_RELOAD; + } + return 1; +} + +void hvy_shotgun_new(struct gun *self, int x, int y) { + self->update = hvy_shotgun; + self->timer = HVY_SHOTGUN_RELOAD; + self->counter = 0; + self->x = to_fixed(x); + self->y = to_fixed(y); +} + +int hvy_repeater(struct gun *self, struct entity *parent, struct entity *target) { + if (self->timer > 0) { + self->timer--; + return 0; + } + if (target != NULL && self->counter == 0) { + self->counter = HVY_REPEATER_ROUNDS; + } + if (self->counter > 0) { + entities.projectile[entities.projectiles] = (struct projectile) { + .update = bullet_update, + .draw = bullet_draw, + .free = bullet_free, + .x = POS_X, .y = POS_Y, + .velocity = (struct vec2) {parent->velocity.x + to_fixed(parent->facing), (int []) {0, 1, 0, -1}[self->counter % 4]}, + .hitbox = { + 0, 0, 0, 0, + }, + .state = 1, + .hp = 300, + .timer = 0, + .facing = parent->facing, + .faction = parent->faction, + .iframes = 0, + .texture = NULL, + .ext = NULL, + }; + //anim(entities->projectile + entities->projectiles, PACER_A_IDLE); + entities.projectile[entities.projectiles].texture = res_get_texture("particles").data; + entities.projectiles++; + self->counter--; + if (self->counter > 0) { + self->timer = 6; + } else { + self->timer = HVY_REPEATER_RELOAD; + if (target != NULL) { + return 1; + } + } + return 0; + } + return 1; +} + +void hvy_repeater_new(struct gun *self, int x, int y) { + self->update = hvy_repeater; + self->timer = HVY_REPEATER_RELOAD; + self->counter = 0; + self->x = to_fixed(x); + self->y = to_fixed(y); +} + +int hvy_chaingun(struct gun *self, struct entity *parent, struct entity *target) { + if (self->timer > 0) { + self->timer--; + return 0; + } + if (target != NULL) { + entities.projectile[entities.projectiles] = (struct projectile) { + .update = bullet_update, + .draw = bullet_draw, + .free = bullet_free, + .x = POS_X, .y = POS_Y, + .velocity = (struct vec2) {parent->velocity.x + to_fixed(parent->facing), (int []) {0, 1, 0, -1}[self->counter % 4]}, + .hitbox = { + 0, 0, 0, 0, + }, + .state = 1, + .hp = 300, + .timer = 0, + .facing = parent->facing, + .faction = parent->faction, + .iframes = 0, + .texture = NULL, + .ext = NULL, + }; + //anim(entities->projectile + entities->projectiles, PACER_A_IDLE); + entities.projectile[entities.projectiles].texture = res_get_texture("particles").data; + entities.projectiles++; + if (self->counter > HVY_CHAINGUN_MIN_RELOAD) { + self->counter--; + } + self->timer = self->counter; + } else { + self->counter = HVY_CHAINGUN_RELOAD; + } + return 1; +} + +void hvy_chaingun_new(struct gun *self, int x, int y) { + self->update = hvy_chaingun; + self->timer = HVY_CHAINGUN_RELOAD; + self->counter = 0; + self->x = to_fixed(x); + self->y = to_fixed(y); +} + +int hvy_launcher(struct gun *self, struct entity *parent, struct entity *target) { + if (self->timer > 0) { + self->timer--; + return 0; + } + if (target != NULL) { + entities.projectile[entities.projectiles] = (struct projectile) { + .update = projectile_update, + .draw = bullet_draw, + .free = bullet_free, + .x = POS_X, .y = POS_Y, + .velocity = (struct vec2) {parent->velocity.x + to_fixed(parent->facing), 1}, + .hitbox = { + 0, 0, 0, 0, + }, + .state = 1, + .hp = 300, + .timer = 0, + .facing = parent->facing, + .faction = parent->faction, + .iframes = 0, + .texture = NULL, + .ext = NULL, + }; + //anim(entities->projectile + entities->projectiles, PACER_A_IDLE); + entities.projectile[entities.projectiles].texture = res_get_texture("particles").data; + entities.projectiles++; + self->timer = HVY_BLASTER_RELOAD; + } + return 1; +} + +void hvy_launcher_new(struct gun *self, int x, int y) { + self->update = hvy_launcher; + self->timer = HVY_BLASTER_RELOAD; + self->counter = 0; + self->x = to_fixed(x); + self->y = to_fixed(y); +} + +int hvy_laser(struct gun *self, struct entity *parent, struct entity *target) { + if (self->counter == 0) { + if (self->timer > 0) { + self->timer--; + return 0; + } + if (target != NULL) { + float angle = atan2(target->x - POS_X, target->y - POS_Y); + #if 0 + entities.projectile[entities.projectiles] = (struct projectile) { + .update = laser_update, + .draw = bullet_draw, + .free = bullet_free, + .x = POS_X, .y = POS_Y, + .velocity = (struct vec2) {parent->velocity.x + to_fixed(sin(angle)), to_fixed(cos(angle))}, + .hitbox = { + 0, 0, 0, 0, + }, + .state = 1, + .hp = 500, + .timer = 0, + .facing = parent->facing, + .faction = parent->faction, + .iframes = 0, + .texture = NULL, + .ext = NULL, + }; + //anim(entities->projectile + entities->projectiles, PACER_A_IDLE); + entities.projectile[entities.projectiles].texture = res_get_texture("particles").data; + entities.projectiles++; + self->timer = HVY_BLASTER_RELOAD; + #endif + self->counter = 25; + self->timer = angle * 128; + return 0; + } + } else { + if (target != NULL) { + self->counter--; + float angle = self->timer / 128.0; + if (self->counter != 0) { + float r = angle + (self->counter & 1? self->counter: -self->counter) * 0.05; + int const x = sin(r) * 16, y = cos(r) * 16; + struct particle *part = entities.particle + entities.particles; + part->x = POS_X + x * self->counter; + part->y = POS_Y + y * self->counter; + part->velocity = (struct vec2) {-x, -y}; + part->rect = particle_white; + part->acceleration = (struct vec2) {0, 0}; + part->hp = self->counter; + entities.particles++; + return 0; + } + entities.projectile[entities.projectiles] = (struct projectile) { + .update = laser_update, + .draw = bullet_draw, + .free = bullet_free, + .x = POS_X, .y = POS_Y, + .velocity = (struct vec2) {parent->velocity.x + to_fixed(sin(angle)), to_fixed(cos(angle))}, + .hitbox = { + 0, 0, 0, 0, + }, + .state = 1, + .hp = 500, + .timer = 0, + .facing = parent->facing, + .faction = parent->faction, + .iframes = 0, + .texture = NULL, + .ext = NULL, + }; + //anim(entities->projectile + entities->projectiles, PACER_A_IDLE); + entities.projectile[entities.projectiles].texture = res_get_texture("particles").data; + entities.projectiles++; + self->timer = HVY_BLASTER_RELOAD; + } else { + self->counter = 0; + self->timer = 0; + } + } + return 1; +} + +void hvy_laser_new(struct gun *self, int x, int y) { + self->update = hvy_laser; + self->timer = HVY_BLASTER_RELOAD; + self->counter = 0; + self->x = to_fixed(x); + self->y = to_fixed(y); +} diff --git a/src/pacer.c b/src/pacer.c new file mode 100644 index 0000000..9bbcfb6 --- /dev/null +++ b/src/pacer.c @@ -0,0 +1,330 @@ +#include "main.h" +#include "entity.h" +#include "loader.h" +#include "tilemap.h" +#include <stdbool.h> +#include <math.h> +#include "particles.h" +#include "gun.h" + +#define HP 15 +#define WIDTH 8 +#define HEIGHT 10 +#define ACCELERATION 1 +#define FRICTION 2 +#define MAX_SPEED 8 +#define GRAVITY 1 +#define WALKABLE_CHECK_DISTANCE 8 +#define DETECTION_RANGE to_fixed(80) +#define GIVE_UP_RANGE to_fixed(128) +#define FAR_RANGE to_fixed(56) +#define NEAR_RANGE to_fixed(40) +#define COLLISION_DAMAGE 1 +#define COLLISION_IFRAMES 60 + +struct pacer_ext { + unsigned detection_range, give_up_range; + signed facing; + struct gun primary, secondary; +}; + +enum { + PACER_NONE, + PACER_FALL, + PACER_IDLE, + PACER_PATROL, + PACER_ALERT_NEAR, + PACER_ALERT_APPROACH, + PACER_ALERT_HALT_FAR, + PACER_ALERT_FAR, + PACER_ALERT_BACK_AWAY, + PACER_ALERT_HALT_NEAR, +}; + +enum { + PACER_A_IDLE, + PACER_A_IDLE2, + PACER_A_REST, + PACER_A_WALK, + PACER_A_WALK2, + PACER_A_WALK3, + PACER_A_WALK4, +}; + +static struct anim pacer_anims[] = { + {PACER_A_IDLE2, {0, 0, 16, 16}, 300}, + {PACER_A_IDLE, {16, 0, 16, 16}, 2}, + {PACER_A_REST, {16, 0, 16, 16}, 300}, + {PACER_A_WALK2, {0, 0, 16, 16}, 6}, + {PACER_A_WALK3, {32, 0, 16, 16}, 6}, + {PACER_A_WALK4, {0, 0, 16, 16}, 6}, + {PACER_A_WALK, {48, 0, 16, 16}, 6}, +}; + +void hvy_blaster_new(struct gun *self, int x, int y); +void hvy_shotgun_new(struct gun *self, int x, int y); +void hvy_repeater_new(struct gun *self, int x, int y); +void hvy_chaingun_new(struct gun *self, int x, int y); +void hvy_launcher_new(struct gun *self, int x, int y); +void hvy_laser_new(struct gun *self, int x, int y); + +static collision_T collide(struct entity *self) { + return tilemap_area(tilemap, from_fixed(self->x) - WIDTH / 2, from_fixed(self->y) - HEIGHT, from_fixed(self->x) + WIDTH / 2, from_fixed(self->y)); +} + +static int move(struct entity *self, signed direction) { + int on_ground = false; + int const dx = direction * ACCELERATION; + self->velocity.x += dx; + // deaccel + if (dx == 0) { + if (self->velocity.x < -FRICTION) { + self->velocity.x += FRICTION; + } else if (self->velocity.x > FRICTION) { + self->velocity.x -= FRICTION; + } else { + self->velocity.x = 0; + } + } + // speed cap + if (self->velocity.x > MAX_SPEED) { + self->velocity.x = MAX_SPEED; + } else if (self->velocity.x < -MAX_SPEED) { + self->velocity.x = -MAX_SPEED; + } + // x collision + self->x += self->velocity.x; + collision_T const cx = collide(self); + if (collision_solid(cx)) { + if (self->velocity.x < 0) { + self->x += to_fixed(8) - ((self->x - to_fixed(WIDTH / 2)) % to_fixed(8)); // left + } else if (self->velocity.x == 0) { + //fputs("what?\n", stderr); + } else { + self->x -= ((self->x + to_fixed(WIDTH / 2)) % to_fixed(8)); // right + } + self->velocity.x = 0; + } + self->velocity.y += GRAVITY; + // y collision + self->y += self->velocity.y; + collision_T const cy = collide(self); + if (collision_solid(cy)) { + if (self->velocity.y < 0) { + self->y += to_fixed(8) - ((self->y - to_fixed(HEIGHT)) % to_fixed(8)); // up + self->velocity.y = 0; + } else if (self->velocity.y == 0) { + //fputs("what?\n", stderr); + } else { + self->y -= ((self->y) % to_fixed(8)); // down + self->velocity.y = to_fixed(1); // crazy but it works + on_ground = true; + } + } + + if (collision_hazard(cx | cy)) { + self->hurt(self, (struct damage) {1, 60}); + } + + self->hitbox.left = from_fixed(self->x) - WIDTH / 2; + self->hitbox.right = from_fixed(self->x) + WIDTH / 2; + self->hitbox.top = from_fixed(self->y) - HEIGHT; + self->hitbox.bottom = from_fixed(self->y); + return on_ground; +} + +static void anim(struct entity *self, unsigned anim) { + self->anim = pacer_anims[anim]; +} + +static inline int is_walkable(int const x, int const y) { + collision_T const front1 = tilemap_tile(tilemap, from_fixed(x), from_fixed(y) - 4); + collision_T const front2 = tilemap_tile(tilemap, from_fixed(x), from_fixed(y) - 10); + collision_T const floor = tilemap_tile(tilemap, from_fixed(x), from_fixed(y) + 4); + return !collision_solid(front1) && !collision_solid(front2) && collision_solid(floor) && !collision_hazard(floor); +} + +static void pacer_free(struct entity *self) { + self->state = 0; + free(self->ext), self->ext = NULL; +} + +static int pacer_update(struct entity *self) { + struct pacer_ext *const ext = self->ext; + if (self->hp <= 0) { + pacer_free(self); + entities.enemies--; + return 1; + } + switch (self->state) { + case PACER_IDLE: + move(self, 0); + ext->primary.update(&ext->primary, self, NULL); + ext->secondary.update(&ext->secondary, self, NULL); + if (abs(entities.player[0].x - self->x) + abs(entities.player[0].y - self->y) < ext->detection_range) { + self->facing = self->x > entities.player[0].x? -1: 1; + anim(self, PACER_A_WALK); + self->state = PACER_ALERT_APPROACH; + } + break; + + case PACER_ALERT_NEAR: + move(self, 0); + self->facing = self->x > entities.player[0].x? -1: 1; + if (ext->primary.update(&ext->primary, self, NULL)) { + anim(self, PACER_A_WALK); + self->state = PACER_ALERT_BACK_AWAY; + } + if (abs(entities.player[0].x - self->x) + abs(entities.player[0].y - self->y) > ext->give_up_range) { + anim(self, PACER_A_REST); + self->state = PACER_IDLE; + } + break; + + case PACER_ALERT_APPROACH: + move(self, self->facing); + if (hitbox_overlap(self->hitbox, entities.player[0].hitbox)) { + entities.player[0].hurt(entities.player + 0, (struct damage) {COLLISION_DAMAGE, COLLISION_IFRAMES}); + } + if (abs(entities.player[0].x - self->x) < NEAR_RANGE || !is_walkable(self->x + to_fixed(self->facing * WALKABLE_CHECK_DISTANCE), self->y)) { + anim(self, PACER_A_IDLE); + self->state = PACER_ALERT_HALT_NEAR; + } + break; + + case PACER_ALERT_HALT_NEAR: // velocity affects bullets + move(self, 0); + if (self->velocity.x == 0) { + if (ext->primary.update(&ext->primary, self, entities.player)) { + anim(self, PACER_A_IDLE); + self->state = PACER_ALERT_NEAR; + } + } + break; + + case PACER_ALERT_FAR: + move(self, 0); + self->facing = self->x > entities.player[0].x? -1: 1; + if (ext->secondary.update(&ext->secondary, self, NULL)) { + anim(self, PACER_A_WALK); + self->state = PACER_ALERT_APPROACH; + } + if (abs(entities.player[0].x - self->x) + abs(entities.player[0].y - self->y) > ext->give_up_range) { + anim(self, PACER_A_REST); + self->state = PACER_IDLE; + } + break; + + case PACER_ALERT_BACK_AWAY: + move(self, -self->facing); + if (hitbox_overlap(self->hitbox, entities.player[0].hitbox)) { + entities.player[0].hurt(entities.player + 0, (struct damage) {COLLISION_DAMAGE, COLLISION_IFRAMES}); + } + if (abs(entities.player[0].x - self->x) > FAR_RANGE || !is_walkable(self->x + to_fixed(-self->facing * WALKABLE_CHECK_DISTANCE), self->y)) { + anim(self, PACER_A_IDLE); + self->state = PACER_ALERT_HALT_FAR; + } + break; + + case PACER_ALERT_HALT_FAR: // velocity affects bullets + move(self, 0); + if (self->velocity.x == 0) { + if (ext->secondary.update(&ext->secondary, self, entities.player)) { + anim(self, PACER_A_IDLE); + self->state = PACER_ALERT_FAR; + } + } + break; + } + if (self->iframes > 0) { + self->iframes--; + } + self->anim.length--; + if (self->anim.length == 0) { + anim(self, self->anim.frame); + } + return 0; +} + +static int pacer_hurt(struct entity *self, struct damage damage) { + if (self->iframes == 0) { + self->hp -= damage.damage; + self->iframes = damage.iframes; + for (int x = -1; x <= 1; x += 2) { + for (int y = -1; y <= 1; y += 2) { + struct particle *part = entities.particle + entities.particles; + part->x = self->x; + part->y = self->y; + part->velocity = (struct vec2) {to_fixed(x) * 1.5, to_fixed(y) * 1.5}; + part->rect = particle_white; + part->acceleration = (struct vec2) {-x, -y}; + part->hp = 24; + entities.particles++; + } + } + } + return 0; +} + +static int pacer_draw(struct entity *self, int camX, int camY) { + if (!(self->iframes & 0x4)) { + SDL_Rect rect = self->anim.rect; + if (self->facing == -1) { + rect.y += 16; + } + SDL_RenderCopy(renderer, self->texture, &rect, &(SDL_Rect) {from_fixed(self->x) - camX - 8, from_fixed(self->y) - camY - 16, 16, 16}); + } + return 0; +} + +struct entity *pacer_new(struct entities *entities) { + struct entity *self = entities->enemy + entities->enemies; + *self = (struct entity) { + .update = pacer_update, + .hurt = pacer_hurt, + .draw = pacer_draw, + .free = pacer_free, + .x = 0, .y = 0, + .velocity = {.x = 0, .y = 0}, + .hitbox = { + 0, 0, 0, 0, + }, + .state = PACER_IDLE, + .hp = HP, + .timer = 0, + .facing = 1, + .faction = FACTION_ENEMY, + .iframes = 0, + .texture = NULL, + .ext = NULL, + }; + self->ext = malloc(sizeof (struct pacer_ext)); + *(struct pacer_ext *) self->ext = (struct pacer_ext) { + .detection_range = DETECTION_RANGE, + .give_up_range = GIVE_UP_RANGE, + .facing = 1, + }; + struct pacer_ext *ext = self->ext; + hvy_repeater_new(&ext->primary, 5, -6); + hvy_blaster_new(&ext->secondary, 5, -6); + anim(self, PACER_A_REST); + self->texture = res_get_texture("pacer").data; + entities->enemies++; + return self; +} + +int pacer_property(struct entity *const restrict self, char const *const restrict property, char const *const restrict value) { + struct pacer_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, "detection range") == 0) { + ext->detection_range = to_fixed(atoi(value)); + } else if (strcmp(property, "give up range") == 0) { + ext->give_up_range = to_fixed(atoi(value)); + } else { + return 1; + } + return 0; +} diff --git a/src/res/big.tmx b/src/res/big.tmx index d9d35ce..8c10ff6 100644 --- a/src/res/big.tmx +++ b/src/res/big.tmx @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="65" height="33" tilewidth="8" tileheight="8" infinite="0" backgroundcolor="#1d2b79" nextlayerid="5" nextobjectid="7"> +<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="65" height="33" tilewidth="8" tileheight="8" infinite="0" backgroundcolor="#1d2b79" nextlayerid="5" nextobjectid="8"> <tileset firstgid="1" source="autotiles.tsx"/> <tileset firstgid="33" source="tileset.tsx"/> <group id="3" name="Group Layer 1"> @@ -23,7 +23,7 @@ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -56,16 +56,16 @@ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,13,13,13,5,0,0,0,0,0,0,0,0,0,0,9,13,13,13,13,13,13,5,0,0,0,0,0,0,0,0,0, -0,9,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,5,9,13,13,13,13,13,13,13,13,13,13,13,13,13,13,15,8,4,12,14,13,5,9,13,13,13,13,13,13,13,15,8,4,4,4,4,12,14,13,5,0,0,0,0,0,0,0, -0,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,12,14,15,8,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,0,3,4,12,14,15,8,4,4,4,4,4,4,4,2,0,0,0,0,3,4,4,2,0,0,0,0,0,0,0, -0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,8,4,4,4,4,4,4,4,4,4,4,4,4,4,12,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,0,0,0,0,0,0,11,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,9,5,0,0,0,0,0,0,0,0,0,9,5,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,5,3,2,0,0,0,0,0,0,0,0,0,3,2,9,13,13,13,5,0,0,0,0,0,0,0,0,0,0,9,13,13,13,13,13,13,5,0,0,0,0,0,0,0,0,0, +0,9,13,13,13,13,13,13,13,13,13,13,13,13,5,9,13,13,15,14,13,13,13,13,13,13,13,13,13,13,13,13,13,15,8,4,12,14,13,5,9,13,13,13,13,13,13,13,15,8,4,4,4,4,12,14,13,5,0,9,13,13,13,5,0, +0,3,4,4,4,4,4,4,4,4,4,4,4,12,14,15,8,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,0,3,4,12,14,15,8,4,4,4,4,4,4,4,2,0,0,0,0,3,4,4,2,0,3,4,4,4,2,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, @@ -98,11 +98,14 @@ <object id="4" name="walky" type="walker" x="408" y="96"> <point/> </object> - <object id="5" name="walky" type="walker" x="200" y="112"> + <object id="5" name="pacer" type="pacer" x="200" y="112"> <point/> </object> <object id="6" name="disk" type="save" x="448" y="120"> <point/> </object> + <object id="7" name="pacer" type="pacer" x="488" y="112"> + <point/> + </object> </objectgroup> </map> diff --git a/src/res/pacer.ase b/src/res/pacer.ase new file mode 100644 index 0000000..7d8e666 --- /dev/null +++ b/src/res/pacer.ase Binary files differdiff --git a/src/save.c b/src/save.c index 0264dfa..9baf981 100644 --- a/src/save.c +++ b/src/save.c @@ -1,5 +1,6 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include "main.h" #include "util.h" #include "util.h" |