Files
PixelDefense/game/systems/s_animation.c

200 lines
6.8 KiB
C

#include "systems.h"
#include "../game_state.h"
#include "../utils.h"
#include <raymath.h>
#include <stdlib.h>
void updateParticleEmitter(ecs_iter_t *it) {
ParticleEmitter *emitter = ecs_field(it, ParticleEmitter, 1);
f32 dt = GetFrameTime();
for (i32 i = 0; i < it->count; i++) {
ecs_entity_t entity = it->entities[i];
if (ecs_has(ECS, entity, EmitterAttachment)) {
EmitterAttachment attachment = *ecs_get(ECS, entity, EmitterAttachment);
if (!ecs_is_alive(ECS, attachment.baseEntity)) {
ecs_delete(ECS, entity);
continue;
}
Vector2 pos = *ecs_get(ECS, entity, Position);
pos = Vector2Add(pos, attachment.offset);
emitter->pos = pos;
}
emitter[i].emitterElapsed += dt;
if (emitter[i].emitterElapsed >= emitter[i].emitterLifetime)
ecs_delete(ECS, entity);
}
}
static inline int lerpInt(int start, int end, int amount)
{
float result = start + amount*(end - start);
return result;
}
bool updateParticle(const Texture2D tex, Particle *particle, f32 dt) {
f32 alpha = particle->elapsed / particle->lifetime;
Vector2 vel = Vector2Lerp(particle->startVel, particle->endVel, alpha);
f32 size = Lerp(particle->startSize, particle->endSize, alpha);
f32 rot = Lerp(particle->startRotSpeed, particle->endRotSpeed, alpha);
Color startC = particle->startColor;
Color endC = particle->endColor;
Color color = {
.r = lerpInt(startC.r, endC.r, alpha),
.g = lerpInt(startC.g, endC.g, alpha),
.b = lerpInt(startC.b, endC.b, alpha),
.a = lerpInt(startC.a, endC.a, alpha)
};
BeginBlendMode(particle->blend);
f32 hSize = size * 0.5f;
Vector2 center = particle->pos;
Rectangle src = getTextureRect(particle->tileID);
src.x += 0.1f;
src.y += 0.1f;
src.width -= 0.2f;
src.height -= 0.2f;
DrawTexturePro(tex, src, (Rectangle) {
center.x,
center.y,
size, size
}, (Vector2) {hSize, hSize}, rot, color);
EndBlendMode();
particle->pos = Vector2Add(particle->pos, Vector2Scale(vel, dt));
particle->elapsed += dt;
return alpha >= 1.0f;
}
static inline Vector2 randVector2Range(Vector2 from, Vector2 to) {
return (Vector2) {
randFloatRange(from.x, to.x),
randFloatRange(from.y, to.y)
};
}
static inline Color randColorRange(Color from, Color to) {
return (Color) {
.r = GetRandomValue(from.r, to.r),
.g = GetRandomValue(from.g, to.g),
.b = GetRandomValue(from.b, to.b),
.a = GetRandomValue(from.a, to.a),
};
}
Particle spawnParticle(const ParticleEmitter *emitter) {
const struct ParticleEmitterData *data = &emitter->data;
return (Particle) {
.pos = Vector2Add(emitter->pos, randVector2Range(data->minOffset, data->maxOffset)),
.rotation = 0.0f,
.startVel = randVector2Range(data->minStartVel, data->maxStartVel),
.endVel = randVector2Range(data->minEndVel, data->maxEndVel),
.startSize = randFloatRange(data->minStartSize, data->maxStartSize),
.endSize = randFloatRange(data->minEndSize, data->maxEndSize),
.startRotSpeed = randFloatRange(data->minStartRotSpeed, data->maxStartRotSpeed),
.endRotSpeed = randFloatRange(data->minEndRotSpeed, data->maxEndRotSpeed),
.startColor = data->startColor,
.endColor = data->endColor,
.blend = data->blend,
.tileID = data->tileID,
.elapsed = 0.0f,
.lifetime = randFloatRange(data->minLifetime, data->maxLifetime),
};
}
void animationSetState(Animation *anim, AnimType type, bool playInFull) {
anim->animType = type;
anim->sequence = entityGetAnimationSequence(anim->entityType, type);
anim->curFrame = 0;
anim->elapsed = 0;
anim->playInFull = playInFull;
}
void updateAnimationState(ecs_iter_t *it) {
Animation *anim = ecs_field(it, Animation, 1);
TextureRegion *text = ecs_field(it, TextureRegion, 2);
for (i32 i = 0; i < it->count; i++) {
if (anim->playInFull) continue;
ecs_entity_t entity = it->entities[i];
AnimType type = ANIM_IDLE;
if (ecs_has(ECS, entity, Velocity)) {
Velocity vel = *ecs_get(ECS, entity, Velocity);
f32 len = Vector2Length(vel);
if (len > 1.0f) {
type = ANIM_WALK;
text[i].flipX = vel.x < 0;
}
}
if (type != anim[i].animType) {
animationSetState(&anim[i], type, false);
}
}
}
void updateAnimation(ecs_iter_t *it) {
Animation *anim = ecs_field(it, Animation, 1);
TextureRegion *texture = ecs_field(it, TextureRegion, 2);
float dt = GetFrameTime();
for (i32 i = 0; i < it->count; i++) {
ecs_entity_t entity = it->entities[i];
AnimationFrame frame = anim[i].frame;
AnimationSequence seq = anim[i].sequence;
anim[i].elapsed += dt;
if (anim[i].elapsed < frame.duration) continue;
i32 nextFrame = (anim[i].curFrame + 1) % seq.frameCount;
if (nextFrame == 0) anim[i].playInFull = false;
anim[i].curFrame = nextFrame;
anim[i].frame = entityGetAnimationFrame(anim[i].entityType, anim[i].animType, nextFrame);
anim[i].elapsed = 0.0f;
texture[i].rec = getTextureRect(anim[i].frame.frame);
if (ecs_has(ECS, entity, Owner)) {
Owner owner = *ecs_get(ECS, entity, Owner);
BzTileID base = anim[i].frame.frame;
Vector2 offset = getTileOffset(base, owner.player);
texture[i].rec.x += offset.x;
texture[i].rec.y += offset.y;
}
}
}
void updateEasingSystem(ecs_iter_t *it) {
Easing *easing = ecs_field(it, Easing, 1);
f32 dt = GetFrameTime();
for (i32 i = 0; i < it->count; i++) {
ecs_entity_t entity = it->entities[i];
if (easing[i].elapsed > easing[i].duration) {
ecs_remove(ECS, entity, Easing);
continue;
}
if (!ecs_has_id(ECS, entity, easing[i].compID))
continue;
easing[i].elapsed += dt;
f32 alpha = easing[i].elapsed / easing[i].duration;
alpha = BZ_MIN(1.0f, alpha);
f32 prevX = easing[i].x;
easing[i].x = bzEase(easing[i].easingFunc, alpha);
const Easing *e = &easing[i];
f32 x = e->x;
// Inner
x = e->easeStart + e->easeTarget * x + e->easeOffset;
// Outer
x = e->start + e->target * x + e->offset;
easing[i].x = x;
u8 *compData = ecs_get_mut_id(ECS, entity, easing[i].compID);
compData += easing[i].memberOffset;
f32 *out = (f32 *) compData;
*out -= prevX;
*out += x;
}
}