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
@@ -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 Binary files differnew file mode 100644 index 0000000..7d8e666 --- /dev/null +++ b/src/res/pacer.ase @@ -1,5 +1,6 @@  #include <stdio.h>  #include <stdlib.h> +#include <string.h>  #include "main.h"  #include "util.h"  #include "util.h"  | 
