Inline entityUpdateAndRender, clean up if branches

This commit is contained in:
Doyle Thai 2016-07-20 20:13:44 +10:00
parent 4f5270881f
commit 906d66a12a
2 changed files with 176 additions and 129 deletions

View File

@ -10,6 +10,12 @@ enum State
state_win, state_win,
}; };
INTERNAL Entity *getHeroEntity(World *world)
{
Entity *result = &world->entities[world->heroIndex];
return result;
}
INTERNAL Entity *addEntity(MemoryArena *arena, World *world, v2 pos, v2 size, INTERNAL Entity *addEntity(MemoryArena *arena, World *world, v2 pos, v2 size,
enum EntityType type, enum Direction direction, enum EntityType type, enum Direction direction,
Texture *tex, b32 collides) Texture *tex, b32 collides)
@ -34,6 +40,16 @@ INTERNAL Entity *addEntity(MemoryArena *arena, World *world, v2 pos, v2 size,
switch(type) switch(type)
{ {
case entitytype_hero: case entitytype_hero:
entity.stats = PLATFORM_MEM_ALLOC(arena, 1, EntityStats);
entity.stats->maxHealth = 100;
entity.stats->health = entity.stats->maxHealth;
entity.stats->actionRate = 100;
entity.stats->actionTimer = entity.stats->actionRate;
entity.stats->actionSpdMul = 100;
entity.stats->entityIdToAttack = -1;
entity.stats->queuedAttack = entityattack_invalid;
entity.state = entitystate_idle;
break;
case entitytype_mob: case entitytype_mob:
{ {
entity.stats = PLATFORM_MEM_ALLOC(arena, 1, EntityStats); entity.stats = PLATFORM_MEM_ALLOC(arena, 1, EntityStats);
@ -41,7 +57,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 = 200;
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;
@ -566,79 +582,6 @@ INTERNAL void updateEntityAnim(Entity *entity, f32 dt)
} }
} }
#define ENTITY_NULL_ID -1
INTERNAL void beginAttack(Entity *attacker)
{
#ifdef DENGINE_DEBUG
ASSERT(attacker->stats->entityIdToAttack != ENTITY_NULL_ID);
#endif
attacker->state = entitystate_attack;
switch (attacker->stats->queuedAttack)
{
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);
if (attacker->direction == direction_east)
attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
else
attacker->dPos.x -= (1.0f * METERS_TO_PIXEL);
break;
default:
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
}
}
// TODO(doyle): Calculate the battle damage, transition back into battle pose ..
// etc
INTERNAL void endAttack(World *world, Entity *attacker)
{
#ifdef DENGINE_DEBUG
ASSERT(attacker->stats->entityIdToAttack != ENTITY_NULL_ID);
#endif
switch (attacker->stats->queuedAttack)
{
case entityattack_tackle:
if (attacker->direction == direction_east)
attacker->dPos.x -= (1.0f * METERS_TO_PIXEL);
else
attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
break;
default:
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
}
// TODO(doyle): Use attacker stats in battle equations
attacker->state = entitystate_battle;
attacker->stats->actionTimer = attacker->stats->actionRate;
attacker->stats->busyDuration = 0;
setActiveEntityAnim(attacker, animlist_hero_battlePose);
Entity *defender = &world->entities[attacker->stats->entityIdToAttack];
if (attacker->type == entitytype_hero)
{
defender->stats->health -= 50;
}
else
{
defender->stats->health--;
}
}
// TODO(doyle): Exposed because of debug .. rework debug system so it we don't // TODO(doyle): Exposed because of debug .. rework debug system so it we don't
// need to expose any game API to it. // need to expose any game API to it.
INTERNAL v4 createCameraBounds(World *world, v2 size) INTERNAL v4 createCameraBounds(World *world, v2 size)
@ -666,6 +609,7 @@ INTERNAL v4 createCameraBounds(World *world, v2 size)
#define ENTITY_IN_BATTLE TRUE #define ENTITY_IN_BATTLE TRUE
#define ENTITY_NOT_IN_BATTLE FALSE #define ENTITY_NOT_IN_BATTLE FALSE
#define ENTITY_NULL_ID -1
INTERNAL i32 findBestEntityToAttack(World *world, Entity attacker) INTERNAL i32 findBestEntityToAttack(World *world, Entity attacker)
{ {
#ifdef DENGINE_DEBUG #ifdef DENGINE_DEBUG
@ -741,7 +685,6 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
entity->stats->entityIdToAttack = entity->stats->entityIdToAttack =
findBestEntityToAttack(world, *entity); findBestEntityToAttack(world, *entity);
break; break;
case entitystate_attack: case entitystate_attack:
case entitystate_dead: case entitystate_dead:
default: default:
@ -756,11 +699,9 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
case entitystate_idle: case entitystate_idle:
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE); updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
setActiveEntityAnim(entity, animlist_hero_idle); setActiveEntityAnim(entity, animlist_hero_idle);
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;
break; break;
case entitystate_attack: case entitystate_attack:
break; break;
@ -781,6 +722,10 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
switch (newState) switch (newState)
{ {
case entitystate_battle: case entitystate_battle:
entity->stats->actionTimer = entity->stats->actionRate;
entity->stats->busyDuration = 0;
setActiveEntityAnim(entity, animlist_hero_battlePose);
break;
case entitystate_idle: case entitystate_idle:
return; return;
@ -819,27 +764,135 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
entity->state = newState; entity->state = newState;
} }
INTERNAL void updateEntityAndRender(MemoryArena *arena, Renderer *renderer, INTERNAL void beginAttack(Entity *attacker)
World *world, f32 dt)
{ {
Entity *hero = &world->entities[world->heroIndex];
#ifdef DENGINE_DEBUG
ASSERT(attacker->stats->entityIdToAttack != ENTITY_NULL_ID);
#endif
attacker->state = entitystate_attack;
switch (attacker->stats->queuedAttack)
{
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);
if (attacker->direction == direction_east)
attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
else
attacker->dPos.x -= (1.0f * METERS_TO_PIXEL);
break;
default:
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
}
}
// TODO(doyle): Calculate the battle damage, transition back into battle pose ..
// etc
INTERNAL void endAttack(World *world, Entity *attacker)
{
#ifdef DENGINE_DEBUG
ASSERT(attacker->stats->entityIdToAttack != ENTITY_NULL_ID);
#endif
switch (attacker->stats->queuedAttack)
{
case entityattack_tackle:
// TODO(doyle): Move animation offsets out and into animation type
if (attacker->direction == direction_east)
attacker->dPos.x -= (1.0f * METERS_TO_PIXEL);
else
attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
break;
default:
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
}
/* Compute attack damage */
// TODO(doyle): Use attacker stats in battle equations
Entity *defender = &world->entities[attacker->stats->entityIdToAttack];
if (attacker->type == entitytype_hero)
defender->stats->health -= 50;
else
defender->stats->health--;
if (defender->stats->health <= 0)
{
#ifdef DENGINE_DEBUG
DEBUG_LOG("Entity has died");
#endif
entityStateSwitch(world, defender, entitystate_dead);
}
/* Return attacker back to non-attacking state */
entityStateSwitch(world, attacker, entitystate_battle);
}
void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
{
if (dt >= 1.0f) dt = 1.0f;
/* Update */
parseInput(state, dt);
glCheckError();
AssetManager *assetManager = &state->assetManager;
Renderer *renderer = &state->renderer;
World *const world = &state->world[state->currWorldIndex];
Font *font = &assetManager->font;
/*
******************************
* Update entities and render
******************************
*/
Entity *hero = getHeroEntity(world);
ASSERT(world->freeEntityIndex < world->maxEntities);
for (i32 i = 0; i < world->freeEntityIndex; i++) for (i32 i = 0; i < world->freeEntityIndex; i++)
{ {
Entity *const entity = &world->entities[i]; Entity *const entity = &world->entities[i];
switch(entity->type) if (entity->state == entitystate_dead)
{ {
case entitytype_mob: #ifdef DENGINE_DEBUG
{ switch(entity->type)
if (entity->state == entitystate_dead)
{ {
// TODO(doyle): Accumulate all dead entities and delete at the case entitytype_hero:
// end. Hence resort/organise entity array once, not every time case entitytype_mob:
// an entity dies break;
i32 entityIndexInArray = i; default:
deleteEntity(arena, world, entityIndexInArray); // TODO(doyle): Implement variable expansion in DEBUG_LOG
continue; DEBUG_LOG("Unexpected entity has been deleted");
} }
#endif
// TODO(doyle): Accumulate all dead entities and delete at the
// end. Hence resort/organise entity array once, not every time
// an entity dies
i32 entityIndexInArray = i;
deleteEntity(&state->arena, world, entityIndexInArray);
// TODO(doyle): DeleteEntity moves elements down 1, so account for i
i--;
continue;
}
/*
*****************************************************
* Set mob to battle mode if within distance from hero
*****************************************************
*/
if (entity->type == entitytype_mob)
{
// TODO(doyle): Currently calculated in pixels, how about meaningful // TODO(doyle): Currently calculated in pixels, how about meaningful
// game units? // game units?
f32 battleThreshold = 500.0f; f32 battleThreshold = 500.0f;
@ -847,14 +900,32 @@ INTERNAL void updateEntityAndRender(MemoryArena *arena, Renderer *renderer,
enum EntityState newState = entitystate_invalid; enum EntityState newState = entitystate_invalid;
if (distance <= battleThreshold) if (distance <= battleThreshold)
newState = entitystate_battle; {
// NOTE(doyle): If in range but in battle, no state change
if (entity->state == entitystate_battle ||
entity->state == entitystate_attack)
{
newState = entity->state;
}
else
{
newState = entitystate_battle;
}
}
else else
{
newState = entitystate_idle; newState = entitystate_idle;
}
entityStateSwitch(world, entity, newState); entityStateSwitch(world, entity, newState);
} }
// NOTE(doyle): Let entitytype_mob fall through to entitytype_hero here
case entitytype_hero: /*
**************************************************
* Conduct battle for humanoid entities if in range
**************************************************
*/
if (entity->type == entitytype_mob || entity->type == entitytype_hero)
{ {
EntityStats *stats = entity->stats; EntityStats *stats = entity->stats;
if (entity->state == entitystate_battle) if (entity->state == entitystate_battle)
@ -862,7 +933,7 @@ INTERNAL void updateEntityAndRender(MemoryArena *arena, Renderer *renderer,
if (stats->actionTimer > 0) if (stats->actionTimer > 0)
stats->actionTimer -= dt * stats->actionSpdMul; stats->actionTimer -= dt * stats->actionSpdMul;
if (stats->actionTimer < 0) if (stats->actionTimer <= 0)
{ {
stats->actionTimer = 0; stats->actionTimer = 0;
if (stats->queuedAttack == entityattack_invalid) if (stats->queuedAttack == entityattack_invalid)
@ -877,38 +948,31 @@ INTERNAL void updateEntityAndRender(MemoryArena *arena, Renderer *renderer,
Entity *attacker = entity; Entity *attacker = entity;
stats->busyDuration -= dt; stats->busyDuration -= dt;
if (stats->busyDuration <= 0) endAttack(world, attacker); if (stats->busyDuration <= 0) endAttack(world, attacker);
Entity *defender =
&world->entities[attacker->stats->entityIdToAttack];
if (defender->stats->health <= 0)
{
#ifdef DENGINE_DEBUG
DEBUG_LOG("Entity has died");
#endif
entityStateSwitch(world, defender, entitystate_dead);
}
} }
break;
}
default:
break;
} }
/*
**************************************************
* Update animations and render entity
**************************************************
*/
updateEntityAnim(entity, dt); updateEntityAnim(entity, dt);
/* Calculate region to render */ /* Calculate region to render */
v4 cameraBounds = createCameraBounds(world, renderer->size); v4 cameraBounds = createCameraBounds(world, renderer->size);
renderer_entity(renderer, cameraBounds, entity, 0, V4(1, 1, 1, 1)); renderer_entity(renderer, cameraBounds, entity, 0, V4(1, 1, 1, 1));
} }
// TODO(doyle): Dead hero not accounted for here
if (world->numEntitiesInBattle > 0) if (world->numEntitiesInBattle > 0)
{ {
// NOTE(doyle): If battle entities is 1 then only the hero is left // NOTE(doyle): If battle entities is 1 then only the hero left
if (hero->state == entitystate_battle && if (hero->state == entitystate_battle &&
world->numEntitiesInBattle == 1) world->numEntitiesInBattle == 1)
entityStateSwitch(world, hero, entitystate_idle); entityStateSwitch(world, hero, entitystate_idle);
else else if (hero->state != entitystate_attack)
{
entityStateSwitch(world, hero, entitystate_battle); entityStateSwitch(world, hero, entitystate_battle);
}
} }
else else
{ {
@ -922,23 +986,6 @@ INTERNAL void updateEntityAndRender(MemoryArena *arena, Renderer *renderer,
hero->stats->actionTimer = hero->stats->actionRate; hero->stats->actionTimer = hero->stats->actionRate;
hero->stats->busyDuration = 0; hero->stats->busyDuration = 0;
} }
}
LOCAL_PERSIST b32 count = 0;
void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt)
{
/* Update */
parseInput(state, dt);
glCheckError();
AssetManager *assetManager = &state->assetManager;
Renderer *renderer = &state->renderer;
World *const world = &state->world[state->currWorldIndex];
Entity *hero = &world->entities[world->heroIndex];
Font *font = &assetManager->font;
ASSERT(world->freeEntityIndex < world->maxEntities);
updateEntityAndRender(&state->arena, renderer, world, dt);
/* Draw ui */ /* Draw ui */
TexAtlas *heroAtlas = asset_getTextureAtlas(assetManager, texlist_hero); TexAtlas *heroAtlas = asset_getTextureAtlas(assetManager, texlist_hero);

View File

@ -46,5 +46,5 @@ typedef struct GameState
} GameState; } GameState;
void worldTraveller_gameInit(GameState *state, v2 windowSize); void worldTraveller_gameInit(GameState *state, v2 windowSize);
void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt); void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt);
#endif #endif