Unify entity update to only use state switches
Remove the use of beginAttack and endAttack in the entity update loop and delegate all changes to entity through state switching. This consolidates the flow of data in the loop more consistently by going through one master function. This also minimises the number of entry-points to functions that will update an entity states- for ease of comprehension.
This commit is contained in:
parent
a43102ee26
commit
8fc895e584
@ -679,102 +679,6 @@ INTERNAL inline void resetEntityState(World *world, Entity *entity)
|
|||||||
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
|
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERNAL void entityStateSwitch(World *world, Entity *entity,
|
|
||||||
enum EntityState newState)
|
|
||||||
{
|
|
||||||
#ifdef DENGINE_DEBUG
|
|
||||||
ASSERT(world && entity)
|
|
||||||
ASSERT(entity->type == entitytype_mob || entity->type == entitytype_hero)
|
|
||||||
#endif
|
|
||||||
if (entity->state == newState) return;
|
|
||||||
|
|
||||||
switch(entity->state)
|
|
||||||
{
|
|
||||||
case entitystate_idle:
|
|
||||||
switch (newState)
|
|
||||||
{
|
|
||||||
case entitystate_battle:
|
|
||||||
updateWorldBattleEntities(world, entity, ENTITY_IN_BATTLE);
|
|
||||||
entity->stats->entityIdToAttack =
|
|
||||||
findBestEntityToAttack(world, *entity);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// TODO(doyle): Corner case- if move out of range and entity has
|
|
||||||
// switched to idle mode, we reach the attacker entity and they continue
|
|
||||||
// attacking it since there's no check before attack if entity is idle
|
|
||||||
// or not (i.e. has moved out of frame last frame).
|
|
||||||
case entitystate_dead:
|
|
||||||
setActiveEntityAnim(entity, animlist_hero_idle);
|
|
||||||
entity->stats->busyDuration = 0;
|
|
||||||
entity->stats->actionTimer = entity->stats->actionRate;
|
|
||||||
entity->stats->queuedAttack = entityattack_invalid;
|
|
||||||
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case entitystate_attack:
|
|
||||||
default:
|
|
||||||
#ifdef DENGINE_DEBUG
|
|
||||||
ASSERT(INVALID_CODE_PATH);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case entitystate_battle:
|
|
||||||
switch (newState)
|
|
||||||
{
|
|
||||||
case entitystate_attack:
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case entitystate_idle:
|
|
||||||
case entitystate_dead:
|
|
||||||
resetEntityState(world, entity);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
#ifdef DENGINE_DEBUG
|
|
||||||
ASSERT(INVALID_CODE_PATH);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case entitystate_attack:
|
|
||||||
switch (newState)
|
|
||||||
{
|
|
||||||
case entitystate_battle:
|
|
||||||
setActiveEntityAnim(entity, animlist_hero_battlePose);
|
|
||||||
entity->stats->actionTimer = entity->stats->actionRate;
|
|
||||||
entity->stats->busyDuration = 0;
|
|
||||||
break;
|
|
||||||
// NOTE(doyle): Entity has been forced out of an attack (out of range)
|
|
||||||
case entitystate_idle:
|
|
||||||
case entitystate_dead:
|
|
||||||
resetEntityState(world, entity);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
#ifdef DENGINE_DEBUG
|
|
||||||
ASSERT(INVALID_CODE_PATH);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case entitystate_dead:
|
|
||||||
switch (newState)
|
|
||||||
{
|
|
||||||
case entitystate_idle:
|
|
||||||
case entitystate_battle:
|
|
||||||
case entitystate_attack:
|
|
||||||
default:
|
|
||||||
#ifdef DENGINE_DEBUG
|
|
||||||
ASSERT(INVALID_CODE_PATH);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
#ifdef DENGINE_DEBUG
|
|
||||||
ASSERT(INVALID_CODE_PATH);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
entity->state = newState;
|
|
||||||
}
|
|
||||||
|
|
||||||
INTERNAL void beginAttack(World *world, Entity *attacker)
|
INTERNAL void beginAttack(World *world, Entity *attacker)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -782,8 +686,6 @@ INTERNAL void beginAttack(World *world, Entity *attacker)
|
|||||||
ASSERT(attacker->stats->entityIdToAttack != ENTITY_NULL_ID);
|
ASSERT(attacker->stats->entityIdToAttack != ENTITY_NULL_ID);
|
||||||
ASSERT(attacker->state == entitystate_battle);
|
ASSERT(attacker->state == entitystate_battle);
|
||||||
#endif
|
#endif
|
||||||
entityStateSwitch(world, attacker, entitystate_attack);
|
|
||||||
|
|
||||||
switch (attacker->stats->queuedAttack)
|
switch (attacker->stats->queuedAttack)
|
||||||
{
|
{
|
||||||
case entityattack_tackle:
|
case entityattack_tackle:
|
||||||
@ -806,8 +708,6 @@ INTERNAL void beginAttack(World *world, Entity *attacker)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(doyle): Calculate the battle damage, transition back into battle pose ..
|
|
||||||
// etc
|
|
||||||
INTERNAL void endAttack(World *world, Entity *attacker)
|
INTERNAL void endAttack(World *world, Entity *attacker)
|
||||||
{
|
{
|
||||||
#ifdef DENGINE_DEBUG
|
#ifdef DENGINE_DEBUG
|
||||||
@ -868,36 +768,117 @@ INTERNAL void endAttack(World *world, Entity *attacker)
|
|||||||
}
|
}
|
||||||
} while (!defender && noMoreValidTargets == TRUE);
|
} while (!defender && noMoreValidTargets == TRUE);
|
||||||
|
|
||||||
enum EntityState newAttackerState = entitystate_invalid;
|
|
||||||
if (!noMoreValidTargets)
|
if (!noMoreValidTargets)
|
||||||
{
|
{
|
||||||
|
i32 damage = -10;
|
||||||
// TODO(doyle): Use attacker stats in battle equations
|
// TODO(doyle): Use attacker stats in battle equations
|
||||||
if (attacker->type == entitytype_hero)
|
if (attacker->type == entitytype_hero)
|
||||||
defender->stats->health -= 1;
|
{
|
||||||
|
defender->stats->health += damage;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// defender->stats->health--;
|
// defender->stats->health--;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (defender->stats->health <= 0)
|
INTERNAL void entityStateSwitch(World *world, Entity *entity,
|
||||||
{
|
enum EntityState newState)
|
||||||
|
{
|
||||||
#ifdef DENGINE_DEBUG
|
#ifdef DENGINE_DEBUG
|
||||||
DEBUG_LOG("Entity has died");
|
ASSERT(world && entity)
|
||||||
|
ASSERT(entity->type == entitytype_mob || entity->type == entitytype_hero)
|
||||||
#endif
|
#endif
|
||||||
entityStateSwitch(world, defender, entitystate_dead);
|
if (entity->state == newState) return;
|
||||||
attacker->stats->entityIdToAttack =
|
|
||||||
findBestEntityToAttack(world, *attacker);
|
|
||||||
}
|
|
||||||
|
|
||||||
newAttackerState = entitystate_battle;
|
switch(entity->state)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
newAttackerState = entitystate_idle;
|
case entitystate_idle:
|
||||||
|
switch (newState)
|
||||||
|
{
|
||||||
|
case entitystate_battle:
|
||||||
|
updateWorldBattleEntities(world, entity, ENTITY_IN_BATTLE);
|
||||||
|
entity->stats->entityIdToAttack =
|
||||||
|
findBestEntityToAttack(world, *entity);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// TODO(doyle): Corner case- if move out of range and entity has
|
||||||
|
// switched to idle mode, we reach the attacker entity and they continue
|
||||||
|
// attacking it since there's no check before attack if entity is idle
|
||||||
|
// or not (i.e. has moved out of frame last frame).
|
||||||
|
case entitystate_dead:
|
||||||
|
setActiveEntityAnim(entity, animlist_hero_idle);
|
||||||
|
entity->stats->busyDuration = 0;
|
||||||
|
entity->stats->actionTimer = entity->stats->actionRate;
|
||||||
|
entity->stats->queuedAttack = entityattack_invalid;
|
||||||
|
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case entitystate_attack:
|
||||||
|
default:
|
||||||
|
#ifdef DENGINE_DEBUG
|
||||||
|
ASSERT(INVALID_CODE_PATH);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case entitystate_battle:
|
||||||
|
switch (newState)
|
||||||
|
{
|
||||||
|
case entitystate_attack:
|
||||||
|
{
|
||||||
|
beginAttack(world, entity);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case entitystate_idle:
|
||||||
|
case entitystate_dead:
|
||||||
|
resetEntityState(world, entity);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
#ifdef DENGINE_DEBUG
|
||||||
|
ASSERT(INVALID_CODE_PATH);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case entitystate_attack:
|
||||||
|
switch (newState)
|
||||||
|
{
|
||||||
|
case entitystate_battle:
|
||||||
|
endAttack(world, entity);
|
||||||
|
setActiveEntityAnim(entity, animlist_hero_battlePose);
|
||||||
|
entity->stats->actionTimer = entity->stats->actionRate;
|
||||||
|
entity->stats->busyDuration = 0;
|
||||||
|
break;
|
||||||
|
// NOTE(doyle): Entity has been forced out of an attack (out of range)
|
||||||
|
case entitystate_idle:
|
||||||
|
case entitystate_dead:
|
||||||
|
resetEntityState(world, entity);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
#ifdef DENGINE_DEBUG
|
||||||
|
ASSERT(INVALID_CODE_PATH);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case entitystate_dead:
|
||||||
|
switch (newState)
|
||||||
|
{
|
||||||
|
case entitystate_idle:
|
||||||
|
case entitystate_battle:
|
||||||
|
case entitystate_attack:
|
||||||
|
default:
|
||||||
|
#ifdef DENGINE_DEBUG
|
||||||
|
ASSERT(INVALID_CODE_PATH);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
#ifdef DENGINE_DEBUG
|
||||||
|
ASSERT(INVALID_CODE_PATH);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return attacker back to non-attacking state */
|
entity->state = newState;
|
||||||
entityStateSwitch(world, attacker, newAttackerState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1003,7 +984,8 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
|||||||
if (stats->queuedAttack == entityattack_invalid)
|
if (stats->queuedAttack == entityattack_invalid)
|
||||||
stats->queuedAttack = entityattack_tackle;
|
stats->queuedAttack = entityattack_tackle;
|
||||||
|
|
||||||
beginAttack(world, entity);
|
/* Launch up attack animation */
|
||||||
|
entityStateSwitch(world, entity, entitystate_attack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (entity->state == entitystate_attack)
|
else if (entity->state == entitystate_attack)
|
||||||
@ -1011,7 +993,37 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
|||||||
// TODO(doyle): Untested if the attacker and the defender same
|
// TODO(doyle): Untested if the attacker and the defender same
|
||||||
Entity *attacker = entity;
|
Entity *attacker = entity;
|
||||||
stats->busyDuration -= dt;
|
stats->busyDuration -= dt;
|
||||||
if (stats->busyDuration <= 0) endAttack(world, attacker);
|
if (stats->busyDuration <= 0)
|
||||||
|
{
|
||||||
|
/* Apply attack damage */
|
||||||
|
entityStateSwitch(world, entity, entitystate_battle);
|
||||||
|
|
||||||
|
/* Get target entity that was attacked */
|
||||||
|
Entity *defender = NULL;
|
||||||
|
for (i32 i = 0; i < world->maxEntities; i++)
|
||||||
|
{
|
||||||
|
i32 entityIdToAttack =
|
||||||
|
attacker->stats->entityIdToAttack;
|
||||||
|
if (world->entities[i].id == entityIdToAttack)
|
||||||
|
{
|
||||||
|
defender = &world->entities[i];
|
||||||
|
#ifdef DENGINE_DEBUG
|
||||||
|
ASSERT(defender->type == entitytype_mob ||
|
||||||
|
defender->type == entitytype_hero);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defender->stats->health <= 0)
|
||||||
|
{
|
||||||
|
#ifdef DENGINE_DEBUG
|
||||||
|
DEBUG_LOG("Entity has died");
|
||||||
|
#endif
|
||||||
|
entityStateSwitch(world, defender, entitystate_dead);
|
||||||
|
entityStateSwitch(world, attacker, entitystate_idle);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1092,20 +1104,10 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
|||||||
renderer_rect(&state->renderer, cameraBounds, heroCenter,
|
renderer_rect(&state->renderer, cameraBounds, heroCenter,
|
||||||
V2(distance, 2.0f), V2(0, 0), angle, renderTex,
|
V2(distance, 2.0f), V2(0, 0), angle, renderTex,
|
||||||
V4(1, 0, 0, 1.0f));
|
V4(1, 0, 0, 1.0f));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DENGINE_DEBUG
|
#ifdef DENGINE_DEBUG
|
||||||
renderer_rect(&state->renderer, cameraBounds, V2(500, 500), V2(100, 2.0f),
|
|
||||||
V2(0, 0), DEGREES_TO_RADIANS(0.0f), renderTex,
|
|
||||||
V4(0, 0, 1, 1.0f));
|
|
||||||
renderer_rect(&state->renderer, cameraBounds, V2(500, 500), V2(100, 2.0f),
|
|
||||||
V2(0, 0), DEGREES_TO_RADIANS(5.0f), renderTex,
|
|
||||||
V4(0, 0, 1, 1.0f));
|
|
||||||
renderer_rect(&state->renderer, cameraBounds, V2(500, 500), V2(100, 2.0f),
|
|
||||||
V2(0, 0), DEGREES_TO_RADIANS(90.0f), renderTex,
|
|
||||||
V4(0, 0, 1, 1.0f));
|
|
||||||
debug_drawUi(state, dt);
|
debug_drawUi(state, dt);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user