From d005a7756f6d15f67a7033a53bae23d00a58af69 Mon Sep 17 00:00:00 2001 From: zlago Date: Sun, 6 Oct 2024 19:54:24 +0200 Subject: particles and projectiles --- GNUmakefile | 2 +- src/entity.h | 53 +++++++++++++++++++++------ src/main.c | 66 ++++++++++++++++++++++++++++------ src/main.h | 6 +++- src/player.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- src/warp.c | 4 +-- 6 files changed, 211 insertions(+), 34 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index f16eaa2..8b08ac0 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -2,7 +2,7 @@ export MKDIR ?= mkdir -p -libs := SDL2 z +libs := SDL2 m z CFLAGS ?= -Wall -Wpedantic -g -Og cflags := ${CFLAGS} diff --git a/src/entity.h b/src/entity.h index fe76feb..0166bdc 100644 --- a/src/entity.h +++ b/src/entity.h @@ -5,23 +5,29 @@ #define from_fixed(a) (a / 16) #define to_fixed(a) (a * 16) +struct vec2 { + signed x, y; +}; + +struct hitbox { + unsigned left, right, top, bottom; +}; + +struct anim { + unsigned frame; + SDL_Rect rect; + unsigned length; +}; + struct entity { int (*update)(struct entity *self); int (*hurt)(struct entity *self, int damage); int (*draw)(struct entity *self, int camx, int camy); void (*free)(struct entity *self); int x, y; // unsigned results in a bunch of weird edge cases - struct velocity { - signed x, y; - } velocity; - struct hitbox { - unsigned left, right, top, bottom; - } hitbox; - struct anim { - unsigned frame; - SDL_Rect rect; - unsigned length; - } anim; + struct vec2 velocity; + struct hitbox hitbox; + struct anim anim; unsigned state; int hp; int timer; @@ -40,3 +46,28 @@ struct warp { unsigned timer; void *ext; }; + +struct projectile { + int (*update)(struct projectile *self); + int (*draw)(struct projectile *self, int camx, int camy); + void (*free)(struct projectile *self); + int x, y; // unsigned results in a bunch of weird edge cases + struct vec2 velocity; + struct hitbox hitbox; + struct anim anim; + unsigned state; + int hp; + int timer; + int iframes; + signed facing; + void *texture; + void *ext; +}; + +struct particle { + int x, y; + struct vec2 velocity; + struct vec2 acceleration; + SDL_Rect rect; + int hp; +}; diff --git a/src/main.c b/src/main.c index c48deef..d5f6504 100644 --- a/src/main.c +++ b/src/main.c @@ -3,6 +3,7 @@ #include #include +#include #include "input.h" #include "loader.h" @@ -22,11 +23,13 @@ SDL_Renderer *renderer = NULL; enum game_state game_state = STATE_PLAYING; char *game_next_level; +static void *particle_tex = NULL; + #define WINDOW_WIDTH 160 #define WINDOW_HEIGHT 90 #define INIT_SUBSYSTEMS SDL_INIT_VIDEO -unsigned input_now = 0; +unsigned input_pressed = 0, input_held = 0; SDL_Scancode keybinds[] = { SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, @@ -64,6 +67,8 @@ void entities_free(struct entities *entities) { int entities_load(struct entities *entities, char *data, size_t size, size_t input_bytes) { entities->enemies = 0; entities->warps = 0; + entities->projectiles = 0; + entities->particles = 0; data += input_bytes; size -= input_bytes; while (size) { @@ -138,6 +143,31 @@ int game_update(void) { entities.warp[i].update(entities.warp + i); } + for (int i = 0, e = 0; i < 64 && e < entities.projectiles; i++) { + if (entities.projectile[i].state) { + e++; + if (entities.projectile[i].update(entities.projectile + i)) { + entities.projectiles--; + if (entities.projectile + i != entities.projectile + entities.projectiles) { + memcpy(entities.projectile + i, entities.projectile + entities.projectiles, sizeof (struct projectile)); + } + continue; + } + } + } + + for (int i = 0; i < entities.particles; i++) { + if (entities.particle[i].hp-- == 0) { + entities.particles--; + if (entities.particle + i != entities.particle + entities.particles) { + memcpy(entities.particle + i, entities.particle + entities.particles, sizeof (struct particle)); + } + continue; + } + entities.particle[i].x += (entities.particle[i].velocity.x += entities.particle[i].acceleration.x); + entities.particle[i].y += (entities.particle[i].velocity.y += entities.particle[i].acceleration.y); + } + return 0; } @@ -151,8 +181,17 @@ void game_render(SDL_Texture *framebuffer, int x, int y) { entities.enemy[i].draw(entities.enemy + i, x, y); } } + for (int i = 0, e = 0; i < 64 && e < entities.projectiles; i++) { + if (entities.projectile[i].state) { + e++; + entities.projectile[i].draw(entities.projectile + i, x, y); + } + } + for (int i = 0; i < entities.particles; i++) { + struct particle *self = entities.particle + i; + SDL_RenderCopy(renderer, particle_tex, &self->rect, &(SDL_Rect) {from_fixed(self->x) - x, from_fixed(self->y) - y, self->rect.w, self->rect.h}); + } tilemap_foreground(tilemap, x, y, WINDOW_WIDTH, WINDOW_HEIGHT); - //SDL_RenderCopy(renderer, res_get_texture("meow").data, &(SDL_Rect) {0, 0, 128, 90}, &(SDL_Rect) {0, 0, 128, 90}); } void game_render_flush(SDL_Texture *framebuffer) { @@ -174,8 +213,8 @@ int main(int const argc, char *const *const argv) { {"border", 2, NULL, 'b'}, {NULL, 0, NULL, 0 }, }; - int opt, li; - unsigned scale = 0, flags = 0, error = 0; + int opt, li, scale = 0; + unsigned flags = 0, error = 0; while ((opt = getopt_long(argc, argv, "h", opts, &li)) != -1) { switch (opt) { case 'h': @@ -199,9 +238,9 @@ int main(int const argc, char *const *const argv) { case 'f': if (optarg == NULL || strcmp(optarg, "true") == 0) { - flags |= SDL_WINDOW_FULLSCREEN; + flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; } else if (strcmp(optarg, "false") == 0) { - flags &= ~SDL_WINDOW_FULLSCREEN; + flags &= ~SDL_WINDOW_FULLSCREEN_DESKTOP; } else { fprintf(stderr, "%s=%s -- expected 'true' or 'false'\n", opts[li].name, optarg); error = 1; @@ -243,7 +282,7 @@ int main(int const argc, char *const *const argv) { return EXIT_FAILURE; } SDL_StopTextInput(); - if (scale == 0) { + if (scale <= 0) { SDL_DisplayMode dm; if (SDL_GetDesktopDisplayMode(0, &dm) != 0) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "couldnt get desktop size", SDL_GetError(), NULL); @@ -299,6 +338,8 @@ int main(int const argc, char *const *const argv) { optind++; } } + + particle_tex = res_get_texture("particles").data; struct blob blob = res_get_map("untitled"); next_tilemap = tilemap_load(blob.data, blob.size); @@ -339,6 +380,7 @@ int main(int const argc, char *const *const argv) { player_property(next_entities.player, "y", "64"); memcpy(&entities, &next_entities, sizeof (entities)); while (1) { + input_pressed = input_held; SDL_Event evt; while (SDL_PollEvent(&evt)) { switch (evt.type) { @@ -355,16 +397,16 @@ int main(int const argc, char *const *const argv) { goto end; } - //static_assert(INPUT_LENGTH <= sizeof(input_now) * CHAR_BIT); // if this trips up, scope creep happened + //static_assert(INPUT_LENGTH <= sizeof(input_held) * CHAR_BIT); // if this trips up, scope creep happened for (unsigned key = 0, bit = 1; key < INPUT_LENGTH; key++, bit <<= 1) { if (evt.key.keysym.scancode == keybinds[key]) { if (evt.key.state == SDL_PRESSED) - input_now |= bit; + input_held |= bit; else - input_now &= ~bit; + input_held &= ~bit; } } - //fprintf(stderr, "input: %0*b\n", INPUT_LENGTH, input_now); + //fprintf(stderr, "input: %0*b\n", INPUT_LENGTH, input_held); break; case SDL_MOUSEMOTION: // the cursor moves //fprintf(stderr, "mouse at %4i %4i\n", evt.motion.x, evt.motion.y); @@ -434,6 +476,8 @@ int main(int const argc, char *const *const argv) { fprintf(stderr, "unknown event type %i\n", evt.type); } } + input_pressed = ~input_pressed & input_held; + //fprintf(stderr, "input: %0*b\n", INPUT_LENGTH, input_pressed); switch (game_state) { case STATE_PLAYING: diff --git a/src/main.h b/src/main.h index d743ce7..ef214bb 100644 --- a/src/main.h +++ b/src/main.h @@ -5,14 +5,18 @@ extern SDL_Window *window; extern SDL_Renderer *renderer; -extern unsigned input_now; +extern unsigned input_pressed, input_held; extern struct entities { struct entity player[1]; struct entity enemy[64]; unsigned enemies; + struct projectile projectile[64]; + unsigned projectiles; struct warp warp[16]; unsigned warps; + struct particle particle[64]; + unsigned particles; } entities, next_entities; extern enum game_state { diff --git a/src/player.c b/src/player.c index 0f0025e..a6fa86d 100644 --- a/src/player.c +++ b/src/player.c @@ -4,6 +4,7 @@ #include "input.h" #include "tilemap.h" #include +#include #define SIZE 4 #define ACCELERATION 1 @@ -44,13 +45,50 @@ struct anim player_anims[] = { {PLAYER_A_FALL2, {48, 0, 16, 16}, 15}, }; +static int slash_update(struct projectile *self) { + self->x += self->velocity.x; + self->y += self->velocity.y; + self->hp--; + #if 0 + struct particle *part = entities.particle + entities.particles; + part->x = self->x + to_fixed(self->facing >= 0? sin(self->hp): -sin(self->hp)) * 6; + part->y = self->y - to_fixed(cos(self->hp)) * 4; + part->velocity = (struct vec2) {0, 0}; + part->rect = (SDL_Rect) {0, 0, 4, 4}; + part->acceleration = (struct vec2) {0, 0}; + part->hp = 5; + entities.particles++; + #endif + if (self->hp == 0) { + return 1; + self->state = 0; + } + return 0; +} + +static int slash_draw(struct projectile *self, int camX, int camY) { + //SDL_Rect rect = self->anim.rect; + //SDL_RenderCopy(renderer, self->texture, &rect, &(SDL_Rect) {from_fixed(self->x) - camX - 8, from_fixed(self->y) - camY - 12, 16, 16}); + //SDL_RenderFillRect(renderer, &(SDL_Rect) {from_fixed(self->x) - camX - 8, from_fixed(self->y) - camY - 12, 16, 16}); + int const x = from_fixed(self->x) - camX, y = from_fixed(self->y) - camY; + SDL_Vertex vertices[3] = { + {.position = {x + from_fixed(self->velocity.x) + ((self->facing >= 0)? sin(self->hp - 1): -sin(self->hp - 1)) * 8, y - cos(self->hp - 1) * 6}, .color = {255, 127, 0, 255}}, + {.position = {x + ((self->facing >= 0)? sin(self->hp + 0): -sin(self->hp + 0)) * 8, y - cos(self->hp + 0) * 6}, .color = {255, 127, 0, 255}}, + {.position = {x - from_fixed(self->velocity.x) + ((self->facing >= 0)? sin(self->hp + 1): -sin(self->hp + 1)) * 8, y - cos(self->hp + 1) * 6}, .color = {255, 127, 0, 255}}, + }; + SDL_RenderGeometry(renderer, NULL, vertices, 3, NULL, 0); + return 0; +} + +static void slash_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 int move(struct entity *self) { int on_ground = false; - int dx = (input_right(input_now) - input_left(input_now)) * ACCELERATION; + int dx = (input_right(input_held) - input_left(input_held)) * ACCELERATION; self->velocity.x += dx; // deaccel if (dx == 0) { @@ -114,6 +152,32 @@ static int move(struct entity *self) { return on_ground; } +static void attack(struct entity *self) { + if (!input_s(input_pressed)) { + return; + } + entities.projectile[entities.projectiles] = (struct projectile) { + .update = slash_update, + .draw = slash_draw, + .free = slash_free, + .x = self->x + to_fixed(self->facing) * 4, .y = self->y - to_fixed(3), + .velocity = (struct vec2) {self->velocity.x, 0}, + .hitbox = { + 0, 0, 0, 0, + }, + .state = PLAYER_IDLE, + .hp = 4, + .timer = 0, + .facing = self->facing, + .iframes = 0, + .texture = NULL, + .ext = NULL, + }; + //anim(entities->projectile + entities->projectiles, ENEMY_A_IDLE); + //entities->projectile[entities->projectiles].texture = res_get_texture("fywi").data; + entities.projectiles++; +} + static void anim(struct entity *self, unsigned anim) { self->anim = player_anims[anim]; } @@ -130,36 +194,47 @@ static int player_update(struct entity *self) { anim(self, PLAYER_A_FALL); self->state = PLAYER_FALL; } - if (input_right(input_now) - input_left(input_now)) { + if (input_right(input_held) - input_left(input_held)) { anim(self, PLAYER_A_WALK); self->state = PLAYER_WALK; } - if (input_a(input_now)) { + if (input_a(input_held)) { self->velocity.y = -JUMP; anim(self, PLAYER_A_JUMP); self->state = PLAYER_JUMP; } + attack(self); break; - case PLAYER_WALK: + case PLAYER_WALK:; + struct particle *part = entities.particle + entities.particles; + part->x = self->x - to_fixed(self->facing) * 3 - to_fixed(1); + part->y = self->y - 2; + part->velocity = (struct vec2) {-self->velocity.x, -8}; + part->rect = (SDL_Rect) {0, 0, 4, 4}; + part->acceleration = (struct vec2) {0, 1}; + part->hp = 5; + entities.particles++; + if (move(self) == false) { self->velocity.y = 0; anim(self, PLAYER_A_FALL); self->state = PLAYER_FALL; } - if (!(input_right(input_now) - input_left(input_now))) { + if (!(input_right(input_held) - input_left(input_held))) { anim(self, PLAYER_A_IDLE); self->state = PLAYER_IDLE; } - if (input_a(input_now)) { + if (input_a(input_held)) { self->velocity.y = -JUMP; anim(self, PLAYER_A_JUMP); self->state = PLAYER_JUMP; } + attack(self); break; case PLAYER_JUMP: - if (!input_a(input_now)) { + if (!input_a(input_held)) { self->velocity.y /= 2; anim(self, PLAYER_A_FALL); self->state = PLAYER_FALL; @@ -174,10 +249,21 @@ static int player_update(struct entity *self) { } break; - case PLAYER_FALL: + case PLAYER_FALL:; + int yy = self->velocity.y; if (move(self) == true) { anim(self, PLAYER_A_IDLE); self->state = PLAYER_IDLE; + for (int x = -2; x <= 2; x++) { + struct particle *part = entities.particle + entities.particles; + part->x = self->x; + part->y = self->y; + part->velocity = (struct vec2) {x * 5, -yy / 4 - 8}; + part->rect = (SDL_Rect) {0, 0, 4, 4}; + part->acceleration = (struct vec2) {0, 1}; + part->hp = yy / 2; + entities.particles++; + } } break; } @@ -195,6 +281,18 @@ static int player_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) {0, 0, 4, 4}; + part->acceleration = (struct vec2) {0, 1}; + part->hp = 30; + entities.particles++; + } + } } return 0; } diff --git a/src/warp.c b/src/warp.c index f36b716..3556ef3 100644 --- a/src/warp.c +++ b/src/warp.c @@ -15,8 +15,8 @@ static int warp_update(struct warp *self) { } game_next_level = self->ext; memcpy(next_entities.player, entities.player, sizeof (entities.player)); - next_entities.player[0].x = self->tox * 16; - next_entities.player[0].y = self->toy * 16; + next_entities.player[0].x = to_fixed(self->tox); + next_entities.player[0].y = to_fixed(self->toy); game_state = STATE_FADE_IN; self->ext = NULL; return 0; -- cgit 1.4.1-2-gfad0