More corner case checks for battle interaction

This commit is contained in:
Doyle Thai 2016-07-21 02:04:17 +10:00
parent 2707c46df1
commit 7b13615614
2 changed files with 60 additions and 55 deletions

View File

@ -330,6 +330,8 @@ void debug_drawUi(GameState *state, f32 dt)
DEBUG_PUSH_VAR("glDrawArray Calls: %d", DEBUG_PUSH_VAR("glDrawArray Calls: %d",
GLOBAL_debug.callCount[debugcallcount_drawArrays], "i32"); GLOBAL_debug.callCount[debugcallcount_drawArrays], "i32");
i32 debug_bAllocated = state->arena.bytesAllocated;
DEBUG_PUSH_VAR("TotalMemoryAllocated: %db", debug_bAllocated, "i32");
i32 debug_kbAllocated = state->arena.bytesAllocated / 1024; i32 debug_kbAllocated = state->arena.bytesAllocated / 1024;
DEBUG_PUSH_VAR("TotalMemoryAllocated: %dkb", debug_kbAllocated, "i32"); DEBUG_PUSH_VAR("TotalMemoryAllocated: %dkb", debug_kbAllocated, "i32");

View File

@ -45,7 +45,7 @@ INTERNAL Entity *addEntity(MemoryArena *arena, World *world, v2 pos, v2 size,
entity.stats->health = entity.stats->maxHealth; entity.stats->health = entity.stats->maxHealth;
entity.stats->actionRate = 100; entity.stats->actionRate = 100;
entity.stats->actionTimer = entity.stats->actionRate; entity.stats->actionTimer = entity.stats->actionRate;
entity.stats->actionSpdMul = 100; entity.stats->actionSpdMul = 1000;
entity.stats->entityIdToAttack = -1; entity.stats->entityIdToAttack = -1;
entity.stats->queuedAttack = entityattack_invalid; entity.stats->queuedAttack = entityattack_invalid;
entity.state = entitystate_idle; entity.state = entitystate_idle;
@ -450,7 +450,7 @@ INTERNAL void parseInput(GameState *state, const f32 dt)
} }
// TODO(doyle): Revisit key input with state checking for last ended down // TODO(doyle): Revisit key input with state checking for last ended down
if (state->keys[GLFW_KEY_SPACE] && !spaceBarWasDown) if (state->keys[GLFW_KEY_SPACE])
{ {
Renderer *renderer = &state->renderer; Renderer *renderer = &state->renderer;
f32 yPos = CAST(f32)(rand() % CAST(i32)renderer->size.h); f32 yPos = CAST(f32)(rand() % CAST(i32)renderer->size.h);
@ -460,10 +460,6 @@ INTERNAL void parseInput(GameState *state, const f32 dt)
addGenericMob(&state->arena, &state->assetManager, world, pos); addGenericMob(&state->arena, &state->assetManager, world, pos);
spaceBarWasDown = TRUE; spaceBarWasDown = TRUE;
} }
else if (!state->keys[GLFW_KEY_SPACE])
{
spaceBarWasDown = FALSE;
}
} }
// NOTE(doyle): Clipping threshold for snapping velocity to 0 // NOTE(doyle): Clipping threshold for snapping velocity to 0
@ -507,10 +503,13 @@ INTERNAL void parseInput(GameState *state, const f32 dt)
b32 heroCollided = FALSE; b32 heroCollided = FALSE;
if (hero->collides == TRUE) if (hero->collides == TRUE)
{ {
for (i32 i = 0; i < world->freeEntityIndex; i++) for (i32 i = 0; i < world->maxEntities; i++)
{ {
if (i == world->heroIndex) continue; if (i == world->heroIndex) continue;
Entity entity = world->entities[i]; Entity entity = world->entities[i];
if (entity.state == entitystate_dead) continue;
if (entity.collides) if (entity.collides)
{ {
v4 heroRect = v4 heroRect =
@ -623,7 +622,10 @@ INTERNAL i32 findBestEntityToAttack(World *world, Entity attacker)
if (attacker.type == entitytype_mob) if (attacker.type == entitytype_mob)
{ {
Entity hero = world->entities[world->heroIndex]; Entity hero = world->entities[world->heroIndex];
result = hero.id;
if (hero.state == entitystate_dead) result = ENTITY_NULL_ID;
else result = hero.id;
return result; return result;
} }
@ -666,6 +668,16 @@ INTERNAL inline void updateWorldBattleEntities(World *world, Entity *entity,
#endif #endif
} }
INTERNAL inline void resetEntityState(World *world, Entity *entity)
{
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
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;
}
INTERNAL void entityStateSwitch(World *world, Entity *entity, INTERNAL void entityStateSwitch(World *world, Entity *entity,
enum EntityState newState) enum EntityState newState)
{ {
@ -691,8 +703,8 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
// attacking it since there's no check before attack if entity is idle // attacking it since there's no check before attack if entity is idle
// or not (i.e. has moved out of frame last frame). // or not (i.e. has moved out of frame last frame).
case entitystate_dead: case entitystate_dead:
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
setActiveEntityAnim(entity, animlist_hero_idle); setActiveEntityAnim(entity, animlist_hero_idle);
entity->stats->busyDuration = 0;
entity->stats->actionTimer = entity->stats->actionRate; entity->stats->actionTimer = entity->stats->actionRate;
entity->stats->queuedAttack = entityattack_invalid; entity->stats->queuedAttack = entityattack_invalid;
entity->stats->entityIdToAttack = ENTITY_NULL_ID; entity->stats->entityIdToAttack = ENTITY_NULL_ID;
@ -709,14 +721,16 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
switch (newState) switch (newState)
{ {
case entitystate_attack: case entitystate_attack:
{
EntityAnim_ attackAnim = entity->anim[entity->stats->queuedAttack];
f32 busyDuration = attackAnim.anim->frameDuration *
CAST(f32) attackAnim.anim->numFrames;
entity->stats->busyDuration = busyDuration;
break; break;
}
case entitystate_idle: case entitystate_idle:
case entitystate_dead: case entitystate_dead:
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE); resetEntityState(world, entity);
setActiveEntityAnim(entity, animlist_hero_idle);
entity->stats->actionTimer = entity->stats->actionRate;
entity->stats->queuedAttack = entityattack_invalid;
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
break; break;
default: default:
#ifdef DENGINE_DEBUG #ifdef DENGINE_DEBUG
@ -728,27 +742,14 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
switch (newState) switch (newState)
{ {
case entitystate_battle: case entitystate_battle:
setActiveEntityAnim(entity, animlist_hero_battlePose);
entity->stats->actionTimer = entity->stats->actionRate; entity->stats->actionTimer = entity->stats->actionRate;
entity->stats->busyDuration = 0; entity->stats->busyDuration = 0;
setActiveEntityAnim(entity, animlist_hero_battlePose);
break; break;
case entitystate_dead:
// TODO(doyle): Repeated logic with battle -> dead
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
setActiveEntityAnim(entity, animlist_hero_wave);
entity->stats->actionTimer = entity->stats->actionRate;
entity->stats->queuedAttack = entityattack_invalid;
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
break;
// NOTE(doyle): Entity has been forced out of an attack (out of range) // NOTE(doyle): Entity has been forced out of an attack (out of range)
case entitystate_idle: case entitystate_idle:
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE); case entitystate_dead:
setActiveEntityAnim(entity, animlist_hero_idle); resetEntityState(world, entity);
entity->stats->busyDuration = 0;
entity->stats->actionTimer = entity->stats->actionRate;
entity->stats->queuedAttack = entityattack_invalid;
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
break; break;
default: default:
#ifdef DENGINE_DEBUG #ifdef DENGINE_DEBUG
@ -777,24 +778,18 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
entity->state = newState; entity->state = newState;
} }
INTERNAL void beginAttack(Entity *attacker) INTERNAL void beginAttack(World *world, Entity *attacker)
{ {
#ifdef DENGINE_DEBUG #ifdef DENGINE_DEBUG
ASSERT(attacker->stats->entityIdToAttack != ENTITY_NULL_ID); ASSERT(attacker->stats->entityIdToAttack != ENTITY_NULL_ID);
#endif #endif
entityStateSwitch(world, attacker, entitystate_attack);
attacker->state = entitystate_attack;
switch (attacker->stats->queuedAttack) switch (attacker->stats->queuedAttack)
{ {
case entityattack_tackle: case entityattack_tackle:
EntityAnim_ attackAnim = attacker->anim[animlist_hero_tackle];
f32 busyDuration = attackAnim.anim->frameDuration *
CAST(f32) attackAnim.anim->numFrames;
attacker->stats->busyDuration = busyDuration;
setActiveEntityAnim(attacker, animlist_hero_tackle); setActiveEntityAnim(attacker, animlist_hero_tackle);
if (attacker->direction == direction_east) if (attacker->direction == direction_east)
attacker->dPos.x += (1.0f * METERS_TO_PIXEL); attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
else else
@ -836,6 +831,7 @@ INTERNAL void endAttack(World *world, Entity *attacker)
Entity *defender = NULL; Entity *defender = NULL;
// TODO(doyle): Implement faster lookup for entity id in entity table // TODO(doyle): Implement faster lookup for entity id in entity table
b32 noMoreValidTargets = FALSE;
do do
{ {
/* Get target entity to attack */ /* Get target entity to attack */
@ -845,38 +841,39 @@ INTERNAL void endAttack(World *world, Entity *attacker)
if (world->entities[i].id == entityIdToAttack) if (world->entities[i].id == entityIdToAttack)
{ {
defender = &world->entities[i]; defender = &world->entities[i];
#ifdef DENGINE_DEBUG
ASSERT(defender->type == entitytype_mob ||
defender->type == entitytype_hero);
#endif
break;
} }
} }
/* If no longer exists, find next best */ /* If no longer exists, find next best */
if (!defender) if (!defender)
{ {
if (world->numEntitiesInBattle > 1) i32 entityIdToAttack = findBestEntityToAttack(world, *attacker);
if (entityIdToAttack == ENTITY_NULL_ID)
{
noMoreValidTargets = TRUE;
}
else
{ {
attacker->stats->entityIdToAttack = attacker->stats->entityIdToAttack =
findBestEntityToAttack(world, *attacker); findBestEntityToAttack(world, *attacker);
} }
else
{
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
} }
} } while (!defender && noMoreValidTargets == TRUE);
} while (!defender);
// TODO(doyle): Very susceptible to bugs- ensure if defender is dead the enum EntityState newAttackerState = entitystate_invalid;
// attacker immediately locates a new target or exits battle mode. But this if (!noMoreValidTargets)
// is necessary since it's possible that by the time the hero is ready to
// apply attack damage another person may of killed it (i.e. party member)
if (defender)
{ {
// 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 -= 50; defender->stats->health -= 50;
else else
{ {
//defender->stats->health--; // defender->stats->health--;
} }
if (defender->stats->health <= 0) if (defender->stats->health <= 0)
@ -888,10 +885,16 @@ INTERNAL void endAttack(World *world, Entity *attacker)
attacker->stats->entityIdToAttack = attacker->stats->entityIdToAttack =
findBestEntityToAttack(world, *attacker); findBestEntityToAttack(world, *attacker);
} }
newAttackerState = entitystate_battle;
}
else
{
newAttackerState = entitystate_idle;
} }
/* Return attacker back to non-attacking state */ /* Return attacker back to non-attacking state */
entityStateSwitch(world, attacker, entitystate_battle); entityStateSwitch(world, attacker, newAttackerState);
} }
@ -933,7 +936,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
// TODO(doyle): Accumulate all dead entities and delete at the // TODO(doyle): Accumulate all dead entities and delete at the
// end. Hence resort/organise entity array once, not every time // end. Hence resort/organise entity array once, not every time
// an entity dies // an entity dies
#if 0 #if 1
i32 entityIndexInArray = i; i32 entityIndexInArray = i;
deleteEntity(&state->arena, world, entityIndexInArray); deleteEntity(&state->arena, world, entityIndexInArray);
@ -996,7 +999,7 @@ 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(entity); beginAttack(world, entity);
} }
} }
else if (entity->state == entitystate_attack) else if (entity->state == entitystate_attack)