2024-03-01 17:09:24 +08:00
|
|
|
|
2024-03-05 11:40:42 +08:00
|
|
|
#include "player.h"
|
|
|
|
#include "entity.h"
|
|
|
|
#include "app.h"
|
|
|
|
#include "input.h"
|
2024-03-26 12:17:21 +08:00
|
|
|
#include "particle.h"
|
2024-03-01 17:09:24 +08:00
|
|
|
|
2024-03-26 12:17:21 +08:00
|
|
|
#include "render_util.h"
|
2024-03-05 15:20:32 +08:00
|
|
|
#include "types.h"
|
2024-03-01 17:09:24 +08:00
|
|
|
#include "util/assert.h"
|
2024-03-26 13:54:32 +08:00
|
|
|
#include "util/rand.h"
|
2024-03-01 17:09:24 +08:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
|
2024-04-16 10:24:44 +08:00
|
|
|
#ifndef M_PI
|
|
|
|
#define M_PI 3.14159265358979323846
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2024-03-01 17:09:24 +08:00
|
|
|
static inline double dabs(double x) {
|
|
|
|
return x < 0.0 ? -x : x;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
System_Player *player_NewSystem(App *super) {
|
|
|
|
System_Player *sys = malloc(sizeof(System_Player));
|
|
|
|
sys->super = super;
|
2024-03-04 17:04:05 +08:00
|
|
|
sys->player = NULL;
|
2024-04-14 14:34:01 +08:00
|
|
|
sys->cutoff = 5000.0;
|
2024-03-01 17:09:24 +08:00
|
|
|
return sys;
|
|
|
|
}
|
|
|
|
void player_DeleteSystem(System_Player *sys) {
|
|
|
|
free(sys);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _player_OnHit(Entity *me, Entity *other, Vec2 triedDelta, void *data) {
|
|
|
|
ASSERT(me->player && "_player_OnHit called with ME as non-player");
|
|
|
|
// Moving down
|
|
|
|
if (triedDelta.y > 0)
|
|
|
|
me->player->onGround = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void player_AddEntity(System_Player *sys, Entity *e) {
|
|
|
|
if (e->player) {
|
|
|
|
ASSERT(!sys->player && "Two player entities are added! Did you forget to DeleteEntity for player?");
|
|
|
|
sys->player = e->player;
|
|
|
|
|
|
|
|
ASSERT(e->hitbox && "Player does not have Hitbox component");
|
|
|
|
e->hitbox->onHit = &_player_OnHit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void player_DeleteEntity(System_Player *sys, uintptr_t id) {
|
|
|
|
if (sys->player && sys->player->super->id == id)
|
|
|
|
sys->player = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-03-05 15:20:32 +08:00
|
|
|
static double walkSpeed = 300.0, jumpSpeed = 800.0, dashSpeed = 1500.0;
|
2024-03-30 15:54:00 +08:00
|
|
|
static int airjumpCount = 1, airdashCount = 1, airjumpParticleCount = 10;
|
2024-03-05 15:20:32 +08:00
|
|
|
static Duration dashLength = {.microseconds = 150000}, dashCooldown = {.microseconds = 400000};
|
2024-03-01 17:09:24 +08:00
|
|
|
|
2024-03-26 12:17:21 +08:00
|
|
|
|
2024-03-01 17:09:24 +08:00
|
|
|
void player_Advance(System_Player *sys, Duration deltaTime) {
|
|
|
|
if (!sys->player)
|
|
|
|
return;
|
|
|
|
// The bulk of player logic right here
|
2024-03-26 12:17:21 +08:00
|
|
|
System_Input *input = sys->super->input;
|
|
|
|
Component_Player *p = sys->player;
|
2024-03-01 17:09:24 +08:00
|
|
|
|
2024-03-26 12:17:21 +08:00
|
|
|
if (p->faceDirection != 1 && p->faceDirection != -1)
|
|
|
|
p->faceDirection = 1; // Face right by default
|
2024-03-05 15:20:32 +08:00
|
|
|
|
2024-03-26 13:54:32 +08:00
|
|
|
// Particles
|
|
|
|
static Duration emitCooldown = {.microseconds = 150000};
|
2024-03-26 14:40:36 +08:00
|
|
|
static TimePoint lastEmit = {.microseconds = 0};
|
2024-03-26 13:54:32 +08:00
|
|
|
if (time_Since(lastEmit).microseconds > emitCooldown.microseconds) {
|
|
|
|
lastEmit = time_Now();
|
|
|
|
Vec2 to_pos = vec2_Add(p->super->position->position, vec2(0, -p->super->hitbox->box.size.y));
|
|
|
|
particle_Emit(
|
|
|
|
sys->super->particle,
|
|
|
|
vec2_Add(vec2_Random(-20, 20, -10, 10), to_pos),
|
|
|
|
vec2(0, -100), 2, 6, 6,
|
2024-03-30 21:25:39 +08:00
|
|
|
duration_FromSeconds(0), &render_ModeInverse);
|
2024-03-26 13:54:32 +08:00
|
|
|
}
|
|
|
|
// Particles when dashing
|
|
|
|
if (time_Since(p->lastDash).microseconds < dashLength.microseconds && dabs(p->super->position->velocity.x) > EPS) {
|
2024-03-26 14:40:36 +08:00
|
|
|
static TimePoint lastDashEmit = {.microseconds = 0};
|
2024-03-30 15:54:00 +08:00
|
|
|
static Duration cooldown = {.microseconds = 4000};
|
2024-03-26 13:54:32 +08:00
|
|
|
if (time_Since(lastDashEmit).microseconds > cooldown.microseconds) {
|
|
|
|
lastDashEmit = time_Now();
|
|
|
|
Vec2 to_pos = vec2_Add(p->super->position->position, vec2(0, -p->super->hitbox->box.size.y / 2.0));
|
|
|
|
particle_Emit(
|
|
|
|
sys->super->particle,
|
|
|
|
vec2_Add(vec2_Random(-5, 5, -30, 30), to_pos),
|
|
|
|
vec2(rand_DoubleRange(650, 700) * -p->faceDirection, rand_DoubleRange(-100, 100)),
|
2024-03-30 15:54:00 +08:00
|
|
|
7, rand_DoubleRange(14, 20), rand_DoubleRange(32, 40),
|
|
|
|
duration_FromSeconds(0), &render_ModeRotate);
|
2024-03-26 13:54:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-05 15:20:32 +08:00
|
|
|
|
2024-03-01 17:09:24 +08:00
|
|
|
double targetVecX = 0.0;
|
|
|
|
// Move left/right
|
2024-03-05 15:20:32 +08:00
|
|
|
if (input_IsPressed(input->keys[input_Key_Left])) {
|
2024-03-26 12:17:21 +08:00
|
|
|
p->faceDirection = -1;
|
2024-03-01 17:09:24 +08:00
|
|
|
targetVecX += -walkSpeed;
|
2024-03-05 15:20:32 +08:00
|
|
|
}
|
|
|
|
if (input_IsPressed(input->keys[input_Key_Right])) {
|
2024-03-26 12:17:21 +08:00
|
|
|
p->faceDirection = 1;
|
2024-03-01 17:09:24 +08:00
|
|
|
targetVecX += walkSpeed;
|
2024-03-05 15:20:32 +08:00
|
|
|
}
|
2024-03-26 12:17:21 +08:00
|
|
|
p->super->position->velocity.x = targetVecX;
|
2024-03-05 14:26:39 +08:00
|
|
|
|
2024-03-01 17:09:24 +08:00
|
|
|
|
2024-03-05 15:20:32 +08:00
|
|
|
// Jump
|
2024-03-26 12:17:21 +08:00
|
|
|
if (p->onGround)
|
|
|
|
p->jumpCount = 0;
|
|
|
|
if (input->keys[input_Key_Jump] == JustPressed ||
|
|
|
|
(input->keys[input_Key_Jump] == Pressed && dabs(p->super->position->velocity.y) < 5)) {
|
|
|
|
if (p->onGround || p->jumpCount < airjumpCount) {
|
|
|
|
p->storedSpeedY = -jumpSpeed;
|
|
|
|
if (!p->onGround) // Took the second clause, airjumped
|
|
|
|
p->jumpCount++;
|
2024-03-30 15:54:00 +08:00
|
|
|
// Particles fly up if on ground, down if airjumped
|
|
|
|
for (int i = 0; i < airjumpParticleCount; i++)
|
|
|
|
particle_Emit(
|
|
|
|
sys->super->particle,
|
|
|
|
vec2_Add(vec2_Random(-20, 20, -5, 5), p->super->position->position),
|
|
|
|
(p->onGround) ? vec2(rand_DoubleRange(-50, 50), -100) : vec2(0, 200), 2, rand_DoubleRange(8, 12), 20,
|
|
|
|
duration_FromSeconds(0), &render_ModeRotate);
|
2024-03-05 14:26:39 +08:00
|
|
|
}
|
2024-03-01 17:09:24 +08:00
|
|
|
}
|
|
|
|
|
2024-03-05 15:20:32 +08:00
|
|
|
// Dash
|
2024-03-26 12:17:21 +08:00
|
|
|
if (p->onGround)
|
|
|
|
p->dashCount = 0;
|
2024-03-05 15:20:32 +08:00
|
|
|
if (input_IsPressed(input->keys[input_Key_Dash]) &&
|
2024-03-26 12:17:21 +08:00
|
|
|
(p->onGround || p->dashCount < airdashCount) &&
|
|
|
|
time_Since(p->lastDash).microseconds > dashCooldown.microseconds) {
|
|
|
|
p->lastDash = time_Now();
|
|
|
|
if (!p->onGround)
|
|
|
|
p->dashCount++;
|
2024-03-05 15:20:32 +08:00
|
|
|
}
|
|
|
|
// Am I dashing right now?
|
2024-03-26 12:17:21 +08:00
|
|
|
if (time_Since(p->lastDash).microseconds < dashLength.microseconds) {
|
|
|
|
p->super->position->velocity.x += p->faceDirection * dashSpeed;
|
|
|
|
p->super->position->velocity.y = 0;
|
2024-03-05 15:20:32 +08:00
|
|
|
} else { // Release the stored Y speed
|
2024-03-30 15:54:00 +08:00
|
|
|
if (dabs(p->storedSpeedY) > EPS) {
|
|
|
|
if (p->super->position->velocity.y < 0 && p->super->position->velocity.y > p->storedSpeedY)
|
|
|
|
p->super->position->velocity.y += p->storedSpeedY;
|
|
|
|
else
|
|
|
|
p->super->position->velocity.y = p->storedSpeedY;
|
|
|
|
p->storedSpeedY = 0;
|
|
|
|
}
|
2024-03-05 15:20:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-03-30 22:41:49 +08:00
|
|
|
// Respawn if fallen out of the world
|
2024-04-14 14:34:01 +08:00
|
|
|
if (p->super->position->position.y > sys->cutoff)
|
2024-03-30 22:41:49 +08:00
|
|
|
player_HazardHarm(sys);
|
|
|
|
|
|
|
|
|
2024-03-01 17:09:24 +08:00
|
|
|
// Check OnGround again
|
|
|
|
if (dabs(sys->player->super->position->velocity.y) > EPS)
|
|
|
|
sys->player->onGround = false;
|
|
|
|
}
|
2024-03-30 22:41:49 +08:00
|
|
|
|
|
|
|
|
2024-04-15 13:56:16 +08:00
|
|
|
static int harmed_particle_count = 20;
|
|
|
|
|
2024-03-30 22:41:49 +08:00
|
|
|
void player_HazardHarm(System_Player *sys) {
|
|
|
|
if (!sys->player)
|
|
|
|
return;
|
2024-04-15 13:56:16 +08:00
|
|
|
|
|
|
|
// Emit some particles
|
|
|
|
for (int i = 0; i < harmed_particle_count; i++) {
|
|
|
|
double speed_linear = rand_DoubleRange(150, 400);
|
|
|
|
double angle = rand_Double01() * 2.0 * M_PI;
|
|
|
|
particle_Emit(
|
|
|
|
sys->super->particle,
|
|
|
|
sys->player->super->position->position,
|
|
|
|
vec2(speed_linear * cos(angle), speed_linear * sin(angle)),
|
|
|
|
rand_DoubleRange(2, 3),
|
2024-04-15 16:21:24 +08:00
|
|
|
rand_DoubleRange(5, 28),
|
|
|
|
rand_DoubleRange(20, 32),
|
2024-04-15 13:56:16 +08:00
|
|
|
duration_FromSeconds(0),
|
|
|
|
&render_ModeDefault);
|
|
|
|
}
|
|
|
|
|
2024-03-30 22:41:49 +08:00
|
|
|
sys->player->storedSpeedY = 0;
|
|
|
|
sys->player->super->position->velocity = vec2(0, 0);
|
|
|
|
sys->player->super->position->position = sys->player->hazardRespawn;
|
|
|
|
sys->player->super->position->position.y -= EPS; // Stay loose!
|
|
|
|
}
|