summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/flier.c469
-rw-r--r--src/funsinit.c4
-rw-r--r--src/res/flier.asebin0 -> 964 bytes
-rw-r--r--utl/json2map/main.c49
4 files changed, 521 insertions, 1 deletions
diff --git a/src/flier.c b/src/flier.c
new file mode 100644
index 0000000..bfce46a
--- /dev/null
+++ b/src/flier.c
@@ -0,0 +1,469 @@
+#include "main.h"
+#include "entity.h"
+#include "loader.h"
+#include "tilemap.h"
+#include <stdbool.h>
+#include <math.h>
+
+#define SIZE 4
+#define ACCELERATION 1
+#define FRICTION 2
+#define MAX_SPEED 16
+#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 flier_ext {
+ unsigned idle_time, attack_delay, attack_delay2, attack_repeat;
+ signed facing_x, facing_y;
+ struct vec2 *path;
+ size_t current_node, path_length;
+ bool reached_x, reached_y;
+};
+
+enum {
+ FLIER_NONE,
+ FLIER_IDLE,
+ FLIER_PATROL,
+ FLIER_ALERT_IDLE,
+ FLIER_ALERT_CHASE,
+ FLIER_ALERT_RUN,
+ FLIER_ALERT_ATTACK,
+};
+
+enum {
+ FLIER_A_IDLE,
+ FLIER_A_IDLE2,
+ FLIER_A_IDLE3,
+ FLIER_A_IDLE4,
+ FLIER_A_WALK,
+ FLIER_A_WALK2,
+ FLIER_A_WALK3,
+ FLIER_A_WALK4,
+ FLIER_A_ATTACK,
+ FLIER_A_ATTACK2,
+};
+
+struct anim flier_anims[] = {
+ {FLIER_A_IDLE2, {0, 0, 16, 16}, 7},
+ {FLIER_A_IDLE3, {16, 0, 16, 16}, 7},
+ {FLIER_A_IDLE4, {32, 0, 16, 16}, 7},
+ {FLIER_A_IDLE, {16, 0, 16, 16}, 7},
+ {FLIER_A_WALK2, {0, 32, 16, 16}, 5},
+ {FLIER_A_WALK3, {16, 32, 16, 16}, 5},
+ {FLIER_A_WALK4, {32, 32, 16, 16}, 5},
+ {FLIER_A_WALK, {16, 32, 16, 16}, 5},
+ {FLIER_A_ATTACK2, {48, 0, 16, 16}, 3},
+ {FLIER_A_ATTACK, {48, 32, 16, 16}, 3},
+};
+
+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)) {
+ entities.player[0].hurt(entities.player + 0, 1);
+ return 1;
+ }
+ if (self->hp == 0) {
+ return 1;
+ self->state = 0;
+ }
+ return 0;
+}
+
+static int bullet_draw(struct projectile *self, int camX, int camY) {
+ SDL_Rect rect = {4, 0, 4, 4};
+ 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});
+ return 0;
+}
+
+static void bullet_free(struct projectile *self) {}
+
+static collision_T collide(struct entity *self) {
+ return tilemap_area(tilemap, from_fixed(self->x) - SIZE / 2, from_fixed(self->y) - SIZE, from_fixed(self->x) + SIZE / 2, from_fixed(self->y));
+}
+
+static void move(struct entity *self, signed direction_x, signed direction_y, bool friction) {
+ int const dx = direction_x * ACCELERATION;
+ self->velocity.x += dx;
+ // deaccel
+ if (dx == 0 && friction) {
+ 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(SIZE / 2)) % to_fixed(8)); // left
+ } else if (self->velocity.x == 0) {
+ //fputs("what?\n", stderr);
+ } else {
+ self->x -= ((self->x + to_fixed(SIZE / 2)) % to_fixed(8)); // right
+ }
+ self->velocity.x = -self->velocity.x;
+ }
+ #if 0
+ if (self->velocity.x < 0) {
+ self->facing = -1;
+ } else if (self->velocity.x > 0) {
+ self->facing = +1;
+ }
+ #endif
+ self->velocity.y += direction_y * ACCELERATION;
+ if (direction_y == 0 && friction) {
+ if (self->velocity.y < -FRICTION) {
+ self->velocity.y += FRICTION;
+ } else if (self->velocity.y > FRICTION) {
+ self->velocity.y -= FRICTION;
+ } else {
+ self->velocity.y = 0;
+ }
+ }
+ // speed cap
+ if (self->velocity.y > MAX_SPEED) {
+ self->velocity.y = MAX_SPEED;
+ } else if (self->velocity.y < -MAX_SPEED) {
+ self->velocity.y = -MAX_SPEED;
+ }
+ // 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(SIZE)) % to_fixed(8)); // up
+ } else if (self->velocity.y == 0) {
+ //fputs("what?\n", stderr);
+ } else {
+ self->y -= ((self->y) % to_fixed(8)); // down
+ }
+ self->velocity.y = -self->velocity.y;
+ }
+
+ if (collision_hazard(cx | cy)) {
+ self->hurt(self, 1);
+ }
+
+ self->hitbox.left = from_fixed(self->x) - SIZE / 2;
+ self->hitbox.right = from_fixed(self->x) + SIZE / 2;
+ self->hitbox.top = from_fixed(self->y) - SIZE;
+ self->hitbox.bottom = from_fixed(self->y);
+}
+
+static void move2(struct entity *self) {
+ int dx = 0, dy = 0;
+ struct flier_ext *const ext = self->ext;
+ struct vec2 const *node = ext->path + ext->current_node;
+ int const tx = node->x - self->x, ty = node->y - self->y;
+ if (ext->reached_x == false) {
+ dx = tx >= 0? 1: -1;
+ }
+ if (ext->reached_y == false) {
+ dy = ty >= 0? 1: -1;
+ }
+ move(self, dx, dy, true);
+ if (ext->reached_x == false) {
+ if (dx != (node->x - self->x >= 0? 1: -1)) {
+ ext->reached_x = true;
+ }
+ }
+ if (ext->reached_y == false) {
+ if (dy != (node->y - self->y >= 0? 1: -1)) {
+ ext->reached_y = true;
+ }
+ }
+}
+
+static void anim(struct entity *self, unsigned anim) {
+ self->anim = flier_anims[anim];
+}
+
+static void attack(struct entity const *const self) {
+ float const angle = atan2(entities.player[0].x - self->x, entities.player[0].y - self->y);
+ entities.projectile[entities.projectiles] = (struct projectile) {
+ .update = bullet_update,
+ .draw = bullet_draw,
+ .free = bullet_free,
+ .x = self->x, .y = self->y - to_fixed(3),
+ .velocity = (struct vec2) {to_fixed(sin(angle)), to_fixed(cos(angle))},
+ .hitbox = {
+ 0, 0, 0, 0,
+ },
+ .state = FLIER_IDLE,
+ .hp = 300,
+ .timer = 0,
+ .facing = self->facing,
+ .faction = FACTION_ENEMY,
+ .iframes = 0,
+ .texture = NULL,
+ .ext = NULL,
+ };
+ //anim(entities->projectile + entities->projectiles, FLIER_A_IDLE);
+ entities.projectile[entities.projectiles].texture = res_get_texture("particles").data;
+ entities.projectiles++;
+}
+
+static void flier_free(struct entity *self) {
+ self->state = 0;
+ free(((struct flier_ext *) self->ext)->path);
+ free(self->ext), self->ext = NULL;
+}
+
+static int flier_update(struct entity *self) {
+ struct flier_ext *const ext = self->ext;
+ if (self->hp <= 0) {
+ flier_free(self);
+ entities.enemies--;
+ return 1;
+ }
+ switch (self->state) {
+ case FLIER_IDLE:
+ move(self, 0, 0, true);
+ self->timer--;
+ if (self->timer == 0) {
+ anim(self, FLIER_A_WALK);
+ self->state = FLIER_PATROL;
+ }
+ if (abs(entities.player[0].x - self->x) + abs(entities.player[0].y - self->y) < to_fixed(64)) {
+ self->state = FLIER_ALERT_IDLE;
+ self->timer = ext->attack_delay;
+ }
+ break;
+
+ case FLIER_PATROL:
+ move2(self);
+ if (ext->reached_x && ext->reached_y) {
+ ext->current_node++;
+ if (ext->current_node == ext->path_length) {
+ ext->current_node = 0;
+ }
+ ext->reached_x = false;
+ ext->reached_y = false;
+ anim(self, FLIER_A_IDLE);
+ self->state = FLIER_IDLE;
+ 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, FLIER_A_IDLE);
+ self->state = FLIER_ALERT_IDLE;
+ self->timer = ext->attack_delay;
+ }
+ break;
+
+ case FLIER_ALERT_IDLE:
+ move(self, 0, 0, false);
+ ext->facing_x = self->x > entities.player[0].x? -1: 1;
+ ext->facing_y = self->y > entities.player[0].y? -1: 1;
+ self->facing = ext->facing_x;
+ if (abs(entities.player[0].x - self->x) + abs(entities.player[0].y - self->y) > to_fixed(64)) {
+ anim(self, FLIER_A_WALK);
+ self->state = FLIER_ALERT_CHASE;
+ }
+ if (abs(entities.player[0].x - self->x) + abs(entities.player[0].y - self->y) > to_fixed(96)) {
+ self->state = FLIER_IDLE;
+ }
+ self->timer--;
+ if (self->timer == 0) {
+ anim(self, FLIER_A_ATTACK);
+ self->state = FLIER_ALERT_ATTACK;
+ self->timer = ext->attack_delay2;
+ }
+ break;
+
+ case FLIER_ALERT_CHASE:
+ ext->facing_x = self->x > entities.player[0].x? -1: 1;
+ ext->facing_y = self->y > entities.player[0].y? -1: 1;
+ move(self, ext->facing_x, ext->facing_y, true);
+ if (abs(entities.player[0].x - self->x) + abs(entities.player[0].y - self->y) < to_fixed(64) /*|| !is_walkable(self->x + to_fixed(ext->facing * WALKABLE_CHECK_DISTANCE), self->y)*/) {
+ anim(self, FLIER_A_IDLE);
+ self->state = FLIER_ALERT_IDLE;
+ self->timer = ext->attack_delay;
+ }
+ break;
+
+ case FLIER_ALERT_ATTACK:
+ move(self, 0, 0, false);
+ ext->facing_x = self->x > entities.player[0].x? -1: 1;
+ self->facing = ext->facing_x;
+ if (self->timer > 0) {
+ self->timer--;
+ }
+ if (self->timer == 0) {
+ attack(self);
+ anim(self, FLIER_A_IDLE);
+ self->state = FLIER_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, FLIER_A_IDLE);
+ self->state = FLIER_IDLE;
+ self->timer = ext->idle_time;
+ }
+ break;
+ }
+ if (self->iframes > 0) {
+ self->iframes--;
+ }
+ self->anim.length--;
+ if (self->anim.length == 0) {
+ anim(self, self->anim.frame);
+ }
+ return 0;
+}
+
+static int flier_init(struct entity *self) {
+ if (((struct flier_ext *) self->ext)->path == NULL) {
+ flier_free(self);
+ entities.enemies--;
+ return 1;
+ }
+ self->update = flier_update;
+ return flier_update(self);
+}
+
+static int flier_hurt(struct entity *self, int damage) {
+ if (self->iframes == 0) {
+ self->hp -= damage;
+ self->iframes = 60;
+ 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), to_fixed(y) - 8};
+ part->rect = (SDL_Rect) {4, 0, 4, 4};
+ part->acceleration = (struct vec2) {0, 1};
+ part->hp = 30;
+ entities.particles++;
+ }
+ }
+ }
+ return 0;
+}
+
+static int flier_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 - 12, 16, 16});
+ }
+ return 0;
+}
+
+struct entity *flier_new(struct entities *entities) {
+ struct entity *self = entities->enemy + entities->enemies;
+ *self = (struct entity) {
+ .update = flier_init,
+ .hurt = flier_hurt,
+ .draw = flier_draw,
+ .free = flier_free,
+ .x = 0, .y = 0,
+ .velocity = {.x = 0, .y = 0},
+ .hitbox = {
+ 0, 0, 0, 0,
+ },
+ .state = FLIER_PATROL,
+ .hp = 3,
+ .timer = 0,
+ .facing = 1,
+ .faction = FACTION_ENEMY,
+ .iframes = 0,
+ .texture = NULL,
+ .ext = NULL,
+ };
+ self->ext = malloc(sizeof (struct flier_ext));
+ *(struct flier_ext *) self->ext = (struct flier_ext) {
+ .idle_time = IDLE_TIME,
+ .attack_delay = ATTACK_DELAY,
+ .attack_delay2 = ATTACK_DELAY2,
+ .attack_repeat = ATTACK_REPEAT,
+ .facing_x = 1,
+ .facing_y = 1,
+ };
+ anim(self, FLIER_A_WALK);
+ self->texture = res_get_texture("flier").data;
+ entities->enemies++;
+ return self;
+}
+
+int flier_property(struct entity *const restrict self, char const *const restrict property, char const *const restrict value) {
+ struct flier_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 if (strcmp(property, "path") == 0) {
+ size_t spaces = 1, commas = 0;
+ for (size_t i = 0; value[i]; i++) {
+ if (value[i] == ' ') {
+ spaces++;
+ } else if (value[i] == ',') {
+ commas++;
+ }
+ }
+ if (spaces != commas) {
+ return 1;
+ }
+ ext->path = malloc(spaces * sizeof (*ext->path));
+ ext->path_length = spaces;
+ char *v = strdup(value), *vv = v; // stupid but it silences a warning
+ for (size_t i = 0; i < spaces; i++) {
+ ext->path[i].x = to_fixed(strtol(v, &v, 0)) + self->x;
+ if (*v != ',') {
+ return 1;
+ }
+ v++;
+ ext->path[i].y = to_fixed(strtol(v, &v, 0)) + self->y;
+ if (*v != ' ' && *v != 0) {
+ return 1;
+ }
+ v++;
+ }
+ free(vv);
+ self->x = ext->path[0].x;
+ self->y = ext->path[0].y;
+ } else {
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/funsinit.c b/src/funsinit.c
index 264a653..8e5581a 100644
--- a/src/funsinit.c
+++ b/src/funsinit.c
@@ -4,10 +4,14 @@
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 *warp_new(struct entities *entities);
int warp_property(void *const restrict entity, char const *const restrict property, char const *const restrict value);
void funs_init(void) {
res_push_fun(walker_new, walker_property, "walker");
+ res_push_fun(flier_new, flier_property, "flier");
res_push_fun(warp_new, warp_property, "warp");
}
diff --git a/src/res/flier.ase b/src/res/flier.ase
new file mode 100644
index 0000000..741409a
--- /dev/null
+++ b/src/res/flier.ase
Binary files differ
diff --git a/utl/json2map/main.c b/utl/json2map/main.c
index 9ffa9b9..beb8909 100644
--- a/utl/json2map/main.c
+++ b/utl/json2map/main.c
@@ -382,7 +382,7 @@ int main(int argc, char **argv) {
//printf("%s:", property->valuestring);
char const *const *default_props = (char const *const[]) {"x", "y", "width", "height", NULL};
- if (cJSON_IsTrue(cJSON_GetObjectItemCaseSensitive(object, "point"))) {
+ if (cJSON_IsTrue(cJSON_GetObjectItemCaseSensitive(object, "point")) || cJSON_GetObjectItemCaseSensitive(object, "polygon") || cJSON_GetObjectItemCaseSensitive(object, "polyline")) {
default_props = (char const *const[]) {"x", "y", NULL};
}
for (int i = 0; default_props[i] != NULL; i++) {
@@ -407,6 +407,53 @@ int main(int argc, char **argv) {
return 1;
}
}
+ cJSON const *path = cJSON_GetObjectItemCaseSensitive(object, "polygon");
+ if (path) {
+ fputs("polygon ", stdout);
+ cJSON const *node;
+ struct blob nodes = blob_new();
+ blob_append(&nodes, "path", strlen("path") + 1);
+ cJSON_ArrayForEach(node, path) {
+ char buf[64];
+ sprintf(buf, "%i,%i ", cJSON_GetObjectItemCaseSensitive(node, "x")->valueint,
+ cJSON_GetObjectItemCaseSensitive(node, "y")->valueint);
+ blob_append(&nodes, buf, strlen(buf));
+ }
+ blob_append(&entity, nodes.data, nodes.size - 1);
+ blob_append(&entity, &(char) {0}, 1);
+ fwrite(nodes.data, 1, nodes.size - 1, stdout);
+ blob_free(&nodes);
+ putchar('\n');
+ }
+ path = cJSON_GetObjectItemCaseSensitive(object, "polyline");
+ if (path) {
+ //fputs("polyline ", stdout);
+ char **node_strings = malloc(cJSON_GetArraySize(path) * sizeof (char *));
+ int node_strings_index = 0;
+ cJSON const *node;
+ struct blob nodes = blob_new();
+ blob_append(&nodes, "path", strlen("path") + 1);
+ cJSON_ArrayForEach(node, path) {
+ char buf[64];
+ sprintf(buf, "%i,%i ", cJSON_GetObjectItemCaseSensitive(node, "x")->valueint,
+ cJSON_GetObjectItemCaseSensitive(node, "y")->valueint);
+ blob_append(&nodes, buf, strlen(buf));
+ node_strings[node_strings_index++] = strdup(buf);
+ }
+ free(node_strings[--node_strings_index]);
+ while (node_strings_index > 1) {
+ node_strings_index--;
+ blob_append(&nodes, node_strings[node_strings_index], strlen(node_strings[node_strings_index]));
+ free(node_strings[node_strings_index]);
+ }
+ free(node_strings[--node_strings_index]);
+ free(node_strings);
+ blob_append(&entity, nodes.data, nodes.size - 1);
+ blob_append(&entity, &(char) {0}, 1);
+ //fwrite(nodes.data, 1, nodes.size - 1, stdout);
+ blob_free(&nodes);
+ //putchar('\n');
+ }
cJSON_ArrayForEach(property, cJSON_GetObjectItemCaseSensitive(object, "properties")) {
char const *const name = cJSON_GetObjectItemCaseSensitive(property, "name")->valuestring;
char const *const type = cJSON_GetObjectItemCaseSensitive(property, "type")->valuestring;