Create notion of state switching for entities

Encapsulate the process required to switch an entity from state to state
using a state switching function. Add extra minor debug markers.
This commit is contained in:
Doyle Thai 2016-07-19 15:19:08 +10:00
parent 403999b566
commit 5fc58ca643
5 changed files with 190 additions and 79 deletions

View File

@ -43,6 +43,8 @@ void debug_pushString(char *formatString, void *data, char *dataType)
formatString, val); formatString, val);
} }
else if (common_strcmp(dataType, "char") == 0) else if (common_strcmp(dataType, "char") == 0)
{
if (data)
{ {
char *val = CAST(char *) data; char *val = CAST(char *) data;
@ -50,6 +52,14 @@ void debug_pushString(char *formatString, void *data, char *dataType)
ARRAY_COUNT(GLOBAL_debug.debugStrings[0]), ARRAY_COUNT(GLOBAL_debug.debugStrings[0]),
formatString, val); formatString, val);
} }
else
{
snprintf(GLOBAL_debug.debugStrings[numDebugStrings],
ARRAY_COUNT(GLOBAL_debug.debugStrings[0]),
formatString);
}
}
else else
{ {
ASSERT(INVALID_CODE_PATH); ASSERT(INVALID_CODE_PATH);
@ -142,9 +152,9 @@ void debug_drawUi(GameState *state, f32 dt)
color); color);
} }
for (i32 entityId = 0; entityId < world->maxEntities; entityId++) for (i32 i = 0; i < world->maxEntities; i++)
{ {
Entity *const entity = &world->entities[entityId]; Entity *const entity = &world->entities[i];
/* Render debug markers on entities */ /* Render debug markers on entities */
v4 color = V4(1, 1, 1, 1); v4 color = V4(1, 1, 1, 1);
char *debugString = NULL; char *debugString = NULL;
@ -189,8 +199,8 @@ void debug_drawUi(GameState *state, f32 dt)
strPos.y -= GLOBAL_debug.stringLineGap; strPos.y -= GLOBAL_debug.stringLineGap;
char entityIDStr[32]; char entityIDStr[32];
snprintf(entityIDStr, ARRAY_COUNT(entityIDStr), "ID: %4d/%d", entityId, snprintf(entityIDStr, ARRAY_COUNT(entityIDStr), "ID: %4d/%d", entity->id,
world->maxEntities); world->uniqueIdAccumulator-1);
renderer_string(&state->renderer, cameraBounds, font, entityIDStr, renderer_string(&state->renderer, cameraBounds, font, entityIDStr,
strPos, 0, color); strPos, 0, color);
@ -210,27 +220,48 @@ void debug_drawUi(GameState *state, f32 dt)
renderer_string(&state->renderer, cameraBounds, font, renderer_string(&state->renderer, cameraBounds, font,
entityTimer, strPos, 0, color); entityTimer, strPos, 0, color);
} }
strPos.y -= GLOBAL_debug.stringLineGap;
char *entityStateStr = debug_entitystate_string(entity->state);
renderer_string(&state->renderer, cameraBounds, font,
entityStateStr, strPos, 0, color);
} }
} }
/* Render debug info stack */ /* Render debug info stack */
DEBUG_PUSH_STRING("Hero Pos: %06.2f, %06.2f", hero->pos, "v2"); DEBUG_PUSH_STRING("== Hero Properties == ");
DEBUG_PUSH_STRING("Hero dPos: %06.2f, %06.2f", hero->dPos, "v2"); DEBUG_PUSH_VAR("Hero Pos: %06.2f, %06.2f", hero->pos, "v2");
DEBUG_PUSH_STRING("Hero Busy Duration: %05.3f", hero->stats->busyDuration, "f32"); DEBUG_PUSH_VAR("Hero dPos: %06.2f, %06.2f", hero->dPos, "v2");
DEBUG_PUSH_VAR("Hero Busy Duration: %05.3f", hero->stats->busyDuration, "f32");
char *heroStateString = debug_entitystate_string(hero->state); char *heroStateString = debug_entitystate_string(hero->state);
DEBUG_PUSH_STRING("Hero State: %s", *heroStateString, "char"); DEBUG_PUSH_VAR("Hero State: %s", *heroStateString, "char");
char *heroQueuedAttackStr = char *heroQueuedAttackStr =
debug_entityattack_string(hero->stats->queuedAttack); debug_entityattack_string(hero->stats->queuedAttack);
DEBUG_PUSH_STRING("Hero QueuedAttack: %s", *heroQueuedAttackStr, "char"); DEBUG_PUSH_VAR("Hero QueuedAttack: %s", *heroQueuedAttackStr, "char");
DEBUG_PUSH_STRING("FreeEntityIndex: %d", world->freeEntityIndex, "i32");
DEBUG_PUSH_STRING("glDrawArray Calls: %d",
GLOBAL_debug.callCount[debugcallcount_drawArrays],
"i32");
DEBUG_PUSH_STRING("== State Properties == ");
DEBUG_PUSH_VAR("FreeEntityIndex: %d", world->freeEntityIndex, "i32");
DEBUG_PUSH_VAR("glDrawArray Calls: %d",
GLOBAL_debug.callCount[debugcallcount_drawArrays], "i32");
i32 debug_kbAllocated = GLOBAL_debug.totalMemoryAllocated / 1024; i32 debug_kbAllocated = GLOBAL_debug.totalMemoryAllocated / 1024;
DEBUG_PUSH_STRING("TotalMemoryAllocated: %dkb", debug_kbAllocated, "i32"); DEBUG_PUSH_VAR("TotalMemoryAllocated: %dkb", debug_kbAllocated, "i32");
DEBUG_PUSH_STRING("== EntityIDs in Battle List == ");
DEBUG_PUSH_VAR("NumEntitiesInBattle: %d", world->numEntitiesInBattle,
"i32");
if (world->numEntitiesInBattle > 0)
{
for (i32 i = 0; i < world->maxEntities; i++)
{
if (world->entityIdInBattle[i])
DEBUG_PUSH_VAR("Entity ID: %d", i, "i32");
}
}
else
{
DEBUG_PUSH_STRING("-none-");
}
debug_stringUpdateAndRender(&state->renderer, font, dt); debug_stringUpdateAndRender(&state->renderer, font, dt);
debug_clearCallCounter(); debug_clearCallCounter();
} }

View File

@ -21,6 +21,7 @@ INTERNAL Entity *addEntity(World *world, v2 pos, v2 size, enum EntityType type,
#endif #endif
Entity entity = {0}; Entity entity = {0};
entity.id = world->uniqueIdAccumulator++;
entity.pos = pos; entity.pos = pos;
entity.hitboxSize = size; entity.hitboxSize = size;
entity.renderSize = size; entity.renderSize = size;
@ -215,12 +216,13 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
World *const world = &state->world[i]; World *const world = &state->world[i];
world->maxEntities = 16384; world->maxEntities = 16384;
world->entities = PLATFORM_MEM_ALLOC(world->maxEntities, Entity); world->entities = PLATFORM_MEM_ALLOC(world->maxEntities, Entity);
world->entitiesInBattleIds = PLATFORM_MEM_ALLOC(world->maxEntities, i32); world->entityIdInBattle = PLATFORM_MEM_ALLOC(world->maxEntities, i32);
world->numEntitiesInBattle = 0; world->numEntitiesInBattle = 0;
world->texType = texlist_terrain; world->texType = texlist_terrain;
world->bounds = world->bounds =
math_getRect(V2(0, 0), v2_scale(worldDimensionInTiles, math_getRect(V2(0, 0), v2_scale(worldDimensionInTiles,
CAST(f32) state->tileSize)); CAST(f32) state->tileSize));
world->uniqueIdAccumulator = 0;
TexAtlas *const atlas = TexAtlas *const atlas =
asset_getTextureAtlas(assetManager, world->texType); asset_getTextureAtlas(assetManager, world->texType);
@ -299,7 +301,9 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
/* Populate mob animation references */ /* Populate mob animation references */
addAnim(assetManager, animlist_hero_idle, mob); addAnim(assetManager, animlist_hero_idle, mob);
addAnim(assetManager, animlist_hero_walk, mob); addAnim(assetManager, animlist_hero_walk, mob);
mob->currAnimId = animlist_hero_idle; addAnim(assetManager, animlist_hero_battlePose, mob);
addAnim(assetManager, animlist_hero_tackle, mob);
hero->currAnimId = animlist_hero_idle;
} }
INTERNAL inline void setActiveEntityAnim(Entity *entity, INTERNAL inline void setActiveEntityAnim(Entity *entity,
@ -596,45 +600,107 @@ INTERNAL v4 createCameraBounds(World *world, v2 size)
return result; return result;
} }
#define ENTITY_IN_BATTLE TRUE
#define ENTITY_NOT_IN_BATTLE FALSE
INTERNAL inline void updateWorldBattleEntities(World *world, Entity *entity,
b32 isInBattle)
{
world->entityIdInBattle[entity->id] = isInBattle;
if (isInBattle)
world->numEntitiesInBattle++;
else
world->numEntitiesInBattle--;
}
INTERNAL void entityStateSwitch(World *world, Entity *entity,
enum EntityState newState)
{
if (entity->state == newState) return;
switch(entity->state)
{
case entitystate_idle:
switch (newState)
{
case entitystate_battle:
updateWorldBattleEntities(world, entity, ENTITY_IN_BATTLE);
case entitystate_attack:
case entitystate_dead:
break;
default:
ASSERT(INVALID_CODE_PATH);
}
break;
case entitystate_battle:
switch (newState)
{
case entitystate_idle:
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
entity->stats->actionTimer = entity->stats->actionRate;
entity->stats->queuedAttack = entityattack_invalid;
setActiveEntityAnim(entity, animlist_hero_idle);
break;
case entitystate_attack:
case entitystate_dead:
break;
default:
ASSERT(INVALID_CODE_PATH);
}
case entitystate_attack:
switch (newState)
{
case entitystate_idle:
case entitystate_battle:
case entitystate_dead:
break;
default:
ASSERT(INVALID_CODE_PATH);
}
case entitystate_dead:
switch (newState)
{
case entitystate_idle:
case entitystate_battle:
case entitystate_attack:
break;
default:
ASSERT(INVALID_CODE_PATH);
}
}
entity->state = newState;
}
INTERNAL void updateEntityAndRender(Renderer *renderer, World *world, f32 dt) INTERNAL void updateEntityAndRender(Renderer *renderer, World *world, f32 dt)
{ {
for (i32 entityId = 0; entityId < world->freeEntityIndex; entityId++) for (i32 i = 0; i < world->freeEntityIndex; i++)
{ {
Entity *const entity = &world->entities[entityId]; Entity *const entity = &world->entities[i];
Entity *hero = &world->entities[world->heroIndex]; Entity *hero = &world->entities[world->heroIndex];
if (entity->type == entitytype_mob) switch(entity->type)
{
case entitytype_mob:
{ {
f32 distance = v2_magnitude(hero->pos, entity->pos);
// 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;
if (distance <= battleThreshold) f32 distance = v2_magnitude(hero->pos, entity->pos);
{
entity->state = entitystate_battle;
if (!world->entitiesInBattleIds[entityId])
{
world->entitiesInBattleIds[entityId] = TRUE;
world->numEntitiesInBattle++;
}
}
else
{
if (world->entitiesInBattleIds[entityId])
{
world->entitiesInBattleIds[entityId] = FALSE;
world->numEntitiesInBattle--;
}
entity->state = entitystate_idle;
entity->stats->actionTimer = entity->stats->actionRate;
entity->stats->queuedAttack = entityattack_invalid;
}
}
if ((entity->state == entitystate_battle || enum EntityState newState = entitystate_invalid;
entity->state == entitystate_attack) && if (distance <= battleThreshold)
entity->type == entitytype_hero) newState = entitystate_battle;
else
newState = entitystate_idle;
entityStateSwitch(world, entity, newState);
}
// NOTE(doyle): Allow fall through to entitytype_hero here
case entitytype_hero:
{
if (entity->state == entitystate_battle ||
entity->state == entitystate_attack)
{ {
EntityStats *stats = entity->stats; EntityStats *stats = entity->stats;
if (stats->health > 0) if (stats->health > 0)
@ -668,20 +734,29 @@ INTERNAL void updateEntityAndRender(Renderer *renderer, World *world, f32 dt)
entity->state = entitystate_dead; entity->state = entitystate_dead;
} }
} }
break;
}
default:
break;
}
if (world->numEntitiesInBattle > 0) if (world->numEntitiesInBattle > 0)
{ {
if (hero->state == entitystate_idle) if (hero->state == entitystate_idle)
{
hero->state = entitystate_battle; hero->state = entitystate_battle;
world->entityIdInBattle[hero->id] = TRUE;
}
if (hero->stats->entityIdToAttack == -1) if (hero->stats->entityIdToAttack == -1)
hero->stats->entityIdToAttack = entityId; hero->stats->entityIdToAttack = i;
} }
else else
{ {
if (hero->state == entitystate_battle) if (hero->state == entitystate_battle)
{ {
hero->state = entitystate_idle; hero->state = entitystate_idle;
world->entityIdInBattle[hero->id] = FALSE;
setActiveEntityAnim(hero, animlist_hero_idle); setActiveEntityAnim(hero, animlist_hero_idle);
} }
hero->stats->entityIdToAttack = -1; hero->stats->entityIdToAttack = -1;

View File

@ -10,9 +10,6 @@
#include "WorldTraveller/WorldTraveller.h" #include "WorldTraveller/WorldTraveller.h"
#define INVALID_CODE_PATH TRUE #define INVALID_CODE_PATH TRUE
#define DEBUG_PUSH_STRING(formatString, data, type) \
debug_pushString(formatString, CAST(void *)&data, type)
enum DebugCallCount enum DebugCallCount
{ {
debugcallcount_drawArrays, debugcallcount_drawArrays,
@ -101,7 +98,12 @@ inline char *debug_entityattack_string(i32 val)
void debug_init(); void debug_init();
#define DEBUG_PUSH_STRING(string) debug_pushString(string, NULL, "char")
#define DEBUG_PUSH_VAR(formatString, data, type) \
debug_pushString(formatString, CAST(void *)&data, type)
void debug_pushString(char *formatString, void *data, char *dataType); void debug_pushString(char *formatString, void *data, char *dataType);
void debug_stringUpdateAndRender(Renderer *renderer, Font *font, f32 dt); void debug_stringUpdateAndRender(Renderer *renderer, Font *font, f32 dt);
void debug_drawUi(GameState *state, f32 dt); void debug_drawUi(GameState *state, f32 dt);

View File

@ -65,6 +65,8 @@ typedef struct EntityAnim_
typedef struct Entity typedef struct Entity
{ {
u32 id;
v2 pos; // Position v2 pos; // Position
v2 dPos; // Velocity v2 dPos; // Velocity
v2 hitboxSize; v2 hitboxSize;

View File

@ -16,7 +16,7 @@ typedef struct World
{ {
Entity *entities; Entity *entities;
i32 maxEntities; i32 maxEntities;
b32 *entitiesInBattleIds; b32 *entityIdInBattle;
i32 numEntitiesInBattle; i32 numEntitiesInBattle;
enum TexList texType; enum TexList texType;
@ -26,6 +26,7 @@ typedef struct World
i32 heroIndex; i32 heroIndex;
i32 freeEntityIndex; i32 freeEntityIndex;
u32 uniqueIdAccumulator;
} World; } World;
typedef struct GameState typedef struct GameState