Add hurt, die animation when taking damage

This commit is contained in:
2024-02-07 17:14:17 +01:00
parent d6466b8f55
commit 6c1d0dfdb2
16 changed files with 251 additions and 24 deletions

View File

@@ -1,6 +1,7 @@
#include "systems.h"
#include "../game_state.h"
#include "../utils.h"
#include <raymath.h>
#include <stdlib.h>
@@ -68,11 +69,6 @@ bool updateParticle(const Texture2D tex, Particle *particle, f32 dt) {
particle->elapsed += dt;
return alpha >= 1.0f;
}
// https://stackoverflow.com/questions/13408990/how-to-generate-random-float-number-in-c
static inline f32 randFloatRange(f32 min, f32 max) {
float scale = rand() / (float) RAND_MAX; /* [0, 1.0] */
return min + scale * ( max - min ); /* [min, max] */
}
static inline Vector2 randVector2Range(Vector2 from, Vector2 to) {
return (Vector2) {
randFloatRange(from.x, to.x),
@@ -107,10 +103,18 @@ Particle spawnParticle(const ParticleEmitter *emitter) {
};
}
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)) {
@@ -123,10 +127,7 @@ void updateAnimationState(ecs_iter_t *it) {
}
if (type != anim[i].animType) {
anim[i].animType = type;
anim[i].sequence = entityGetAnimationSequence(anim[i].entityType, type);
anim[i].curFrame = 0;
anim[i].elapsed = 0;
animationSetState(&anim[i], type, false);
}
}
}
@@ -145,6 +146,7 @@ void updateAnimation(ecs_iter_t *it) {
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;

View File

@@ -5,6 +5,7 @@
#include "../input.h"
#include "../pathfinding.h"
#include "../entity_factory.h"
#include "../utils.h"
#include <math.h>
#include <raymath.h>
@@ -144,16 +145,22 @@ void entityUpdateKinematic(ecs_iter_t *it) {
position[i] = Vector2Add(position[i], Vector2Scale(velocity[i], dt));
}
}
void entityUpdatePhysics(ecs_iter_t *it) {
void entityUpdate(ecs_iter_t *it) {
Game *game = ecs_singleton_get_mut(ECS, Game);
Position *position = ecs_field(it, Position, 1);
HitBox *hitbox = ecs_field(it, HitBox, 2);
Velocity *velocity = ecs_field(it, Velocity, 3);
SpatialGridID *spatialID = ecs_field(it, SpatialGridID, 4);
Unit *unit = ecs_field(it, Unit, 4);
Owner *owner = ecs_field(it, Owner, 5);
SpatialGridID *spatialID = ecs_field(it, SpatialGridID, 6);
f32 dt = it->delta_time;
for (i32 i = 0; i < it->count; i++) {
// Attack thingies
unit[i].attackElapsed += dt;
bool canAttack = unit[i].attackElapsed > unit[i].attackCooldown;
// Only update "stationary" entities
bool stationary = Vector2Length(velocity[i]) <= 0.2f;
Rectangle bounds = entityTransformHitBox(position[i], hitbox[i]);
@@ -173,9 +180,35 @@ void entityUpdatePhysics(ecs_iter_t *it) {
if (!CheckCollisionRecs(bounds, otherBounds)) {
continue;
}
// Attack update
if (canAttack && ecs_has(ECS, other, Health) && ecs_has(ECS, other, Owner)) {
Health *otherHealth = ecs_get_mut(ECS, other, Health);
Player otherPlayer = ecs_get(ECS, other, Owner)->player;
if (otherPlayer != owner[i].player) {
Rectangle collisionRec = GetCollisionRec(bounds, otherBounds);
f32 percentageCovered = (collisionRec.width * collisionRec.height) / (bounds.width * bounds.height);
f32 dealDmg = randFloatRange(unit[i].minDamage, unit[i].maxDamage);
f32 multiplier = 1.0f + percentageCovered;
multiplier = Clamp(multiplier, 0.8f, 1.6f);
dealDmg *= multiplier;
damageEvent(other, (DamageEvent) {
.amount = dealDmg
});
canAttack = false;
unit[i].attackElapsed = 0.0f;
}
}
// Physics update
slowDown += 0.1f;
Position dif = Vector2Subtract(otherPos, position[i]);
//dif = Vector2Normalize(dif);
dir = Vector2Add(dir, dif);
}
@@ -270,7 +303,7 @@ void updateBuildingRecruitment(ecs_iter_t *it) {
}
}
void renderHealth(ecs_iter_t *it) {
void renderHealthBar(ecs_iter_t *it) {
Position *pos = ecs_field(it, Position, 1);
HitBox *hitbox = ecs_field(it, HitBox, 2);
Health *health = ecs_field(it, Health, 3);

View File

@@ -3,6 +3,36 @@
#include "../game_state.h"
#include "../sounds.h"
void damageEvent(ecs_entity_t entity, DamageEvent event) {
BZ_ASSERT(ecs_has(ECS, entity, Health));
Health *health = ecs_get_mut(ECS, entity, Health);
health->hp -= event.amount;
bool hasAnimation = ecs_has(ECS, entity, Animation);
if (hasAnimation && health->hp > 0) {
// Still alive, just play hurt anim
Animation *animation = ecs_get_mut(ECS, entity, Animation);
animationSetState(animation, ANIM_HURT, true);
} else if (hasAnimation) {
// Delay delete
Animation *animation = ecs_get_mut(ECS, entity, Animation);
animationSetState(animation, ANIM_DIE, true);
ecs_set(ECS, entity, DelayDelete, {
.time = entityGetAnimationLength(animation->entityType, ANIM_DIE)
});
// Remove, so it becomes inactive
ecs_remove_id(ECS, entity, Selectable);
ecs_remove(ECS, entity, Health);
ecs_remove(ECS, entity, Unit);
ecs_remove(ECS, entity, Building);
} else {
// No animation, delete right away
ecs_delete(ECS, entity);
}
health->lastChanged = GetTime();
}
i32 harvestEvent(ecs_entity_t entity, HarvestEvent event) {
BZ_ASSERT(ecs_has_id(ECS, entity, ecs_id(Harvestable)));
BZ_ASSERT(ecs_has(ECS, entity, Resource));

View File

@@ -291,9 +291,9 @@ void drawMainMenuUI(Game *game, f32 dt) {
if (uiMainMenuButton("Play", true)) {
setScreen(game, SCREEN_GAME);
unloadMap(game);
//loadMap(game, "assets/maps/tree_test.tmj");
loadMap(game, "assets/maps/tree_test.tmj");
//loadMap(game, "assets/maps/entity_test.tmj");
loadMap(game, "assets/maps/map_01.tmj");
//loadMap(game, "assets/maps/map_01.tmj");
}
if (uiMainMenuButton("Settings", true)) {
setScreen(game, SCREEN_SETTINGS);

View File

@@ -73,6 +73,19 @@ ECS_MOVE(Building, dst, src, {
*dst = *src;
})
void delayDeleteUpdate(ecs_iter_t *it) {
DelayDelete *delay = ecs_field(it, DelayDelete, 1);
f32 dt = GetFrameTime();
for (i32 i = 0; i < it->count; i++) {
delay[i].elapsed += dt;
if (delay[i].elapsed >= delay[i].time)
ecs_delete(ECS, it->entities[i]);
}
}
void setupSystems() {
ecs_set_hooks(ECS, SpatialGridID, {
.dtor = ecs_dtor(SpatialGridID),
@@ -99,7 +112,7 @@ void setupSystems() {
ECS_SYSTEM(ECS, entityUpdateSpatialID, EcsOnUpdate, Position, HitBox, Velocity, SpatialGridID);
ECS_SYSTEM(ECS, entityUpdateKinematic, EcsOnUpdate, Position, Velocity, Steering, Unit);
ECS_SYSTEM(ECS, entityUpdatePhysics, EcsOnUpdate, Position, HitBox, Velocity, SpatialGridID);
ECS_SYSTEM(ECS, entityUpdate, EcsOnUpdate, Position, HitBox, Velocity, Unit, Owner, SpatialGridID);
ECS_SYSTEM(ECS, entityMoveToTarget, EcsOnUpdate, Position, Velocity, TargetPosition, Steering);
ECS_SYSTEM(ECS, entityFollowPath, EcsOnUpdate, Path);
@@ -113,12 +126,14 @@ void setupSystems() {
ECS_SYSTEM(ECS, updateAnimation, EcsOnUpdate, Animation, TextureRegion);
ECS_SYSTEM(ECS, updateEasingSystem, EcsOnUpdate, Easing, Position, HitBox, Rotation);
ECS_SYSTEM(ECS, renderHealth, EcsOnUpdate, Position, HitBox, Health);
ECS_SYSTEM(ECS, renderHealthBar, EcsOnUpdate, Position, HitBox, Health);
ECS_SYSTEM(ECS, renderDebugPath, EcsOnUpdate, Path);
ECS_SYSTEM(ECS, renderColliders, EcsOnUpdate, Position, HitBox);
ECS_SYSTEM(ECS, renderOrientationDirection, EcsOnUpdate, Position, Orientation);
ECS_SYSTEM(ECS, delayDeleteUpdate, EcsOnUpdate, DelayDelete);
renderDebugPathSystem = renderDebugPath;
renderOrientDirSystem = renderOrientationDirection;
renderCollidersSystem = renderColliders;

View File

@@ -46,6 +46,7 @@ bool updateParticle(const Texture2D tex, Particle *particle, f32 dt);
Particle spawnParticle(const ParticleEmitter *emitter);
void animationSetState(Animation *anim, AnimType type, bool playInFull);
/*
* 1: Animation
* 2: TextureRegion
@@ -117,13 +118,16 @@ void entityUpdateSpatialID(ecs_iter_t *it);
void entityUpdateKinematic(ecs_iter_t *it);
/*
* Big system for updating physics and attacking
* 0: Game (singleton) for collisions
* 1: Position
* 2: HitBox
* 2: Velocity
* 3: SpatialGridID
* 3: Velocity
* 4: Unit
* 5: Owner
* 6: SpatialGridID
*/
void entityUpdatePhysics(ecs_iter_t *it);
void entityUpdate(ecs_iter_t *it);
/*
* 1: Position
@@ -152,7 +156,7 @@ void updateBuildingRecruitment(ecs_iter_t *it);
* 2: HitBox
* 3: Health
*/
void renderHealth(ecs_iter_t *it);
void renderHealthBar(ecs_iter_t *it);
/*
* 1: Position
@@ -177,6 +181,8 @@ void renderDebugPath(ecs_iter_t *it);
* Event Systems
**********************************/
void damageEvent(ecs_entity_t entity, DamageEvent event);
i32 harvestEvent(ecs_entity_t entity, HarvestEvent event);
void depositEvent(ecs_entity_t entity, DepositEvent event);