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:
parent
403999b566
commit
5fc58ca643
71
src/Debug.c
71
src/Debug.c
@ -44,12 +44,22 @@ void debug_pushString(char *formatString, void *data, char *dataType)
|
||||
}
|
||||
else if (common_strcmp(dataType, "char") == 0)
|
||||
{
|
||||
char *val = CAST(char *)data;
|
||||
if (data)
|
||||
{
|
||||
char *val = CAST(char *) data;
|
||||
|
||||
snprintf(GLOBAL_debug.debugStrings[numDebugStrings],
|
||||
ARRAY_COUNT(GLOBAL_debug.debugStrings[0]),
|
||||
formatString, val);
|
||||
snprintf(GLOBAL_debug.debugStrings[numDebugStrings],
|
||||
ARRAY_COUNT(GLOBAL_debug.debugStrings[0]),
|
||||
formatString, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(GLOBAL_debug.debugStrings[numDebugStrings],
|
||||
ARRAY_COUNT(GLOBAL_debug.debugStrings[0]),
|
||||
formatString);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
@ -142,9 +152,9 @@ void debug_drawUi(GameState *state, f32 dt)
|
||||
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 */
|
||||
v4 color = V4(1, 1, 1, 1);
|
||||
char *debugString = NULL;
|
||||
@ -189,8 +199,8 @@ void debug_drawUi(GameState *state, f32 dt)
|
||||
|
||||
strPos.y -= GLOBAL_debug.stringLineGap;
|
||||
char entityIDStr[32];
|
||||
snprintf(entityIDStr, ARRAY_COUNT(entityIDStr), "ID: %4d/%d", entityId,
|
||||
world->maxEntities);
|
||||
snprintf(entityIDStr, ARRAY_COUNT(entityIDStr), "ID: %4d/%d", entity->id,
|
||||
world->uniqueIdAccumulator-1);
|
||||
renderer_string(&state->renderer, cameraBounds, font, entityIDStr,
|
||||
strPos, 0, color);
|
||||
|
||||
@ -210,27 +220,48 @@ void debug_drawUi(GameState *state, f32 dt)
|
||||
renderer_string(&state->renderer, cameraBounds, font,
|
||||
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 */
|
||||
DEBUG_PUSH_STRING("Hero Pos: %06.2f, %06.2f", hero->pos, "v2");
|
||||
DEBUG_PUSH_STRING("Hero dPos: %06.2f, %06.2f", hero->dPos, "v2");
|
||||
DEBUG_PUSH_STRING("Hero Busy Duration: %05.3f", hero->stats->busyDuration, "f32");
|
||||
DEBUG_PUSH_STRING("== Hero Properties == ");
|
||||
DEBUG_PUSH_VAR("Hero Pos: %06.2f, %06.2f", hero->pos, "v2");
|
||||
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);
|
||||
DEBUG_PUSH_STRING("Hero State: %s", *heroStateString, "char");
|
||||
DEBUG_PUSH_VAR("Hero State: %s", *heroStateString, "char");
|
||||
char *heroQueuedAttackStr =
|
||||
debug_entityattack_string(hero->stats->queuedAttack);
|
||||
DEBUG_PUSH_STRING("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_VAR("Hero QueuedAttack: %s", *heroQueuedAttackStr, "char");
|
||||
|
||||
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;
|
||||
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_clearCallCounter();
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ INTERNAL Entity *addEntity(World *world, v2 pos, v2 size, enum EntityType type,
|
||||
#endif
|
||||
|
||||
Entity entity = {0};
|
||||
entity.id = world->uniqueIdAccumulator++;
|
||||
entity.pos = pos;
|
||||
entity.hitboxSize = size;
|
||||
entity.renderSize = size;
|
||||
@ -215,12 +216,13 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
|
||||
World *const world = &state->world[i];
|
||||
world->maxEntities = 16384;
|
||||
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->texType = texlist_terrain;
|
||||
world->bounds =
|
||||
math_getRect(V2(0, 0), v2_scale(worldDimensionInTiles,
|
||||
CAST(f32) state->tileSize));
|
||||
world->uniqueIdAccumulator = 0;
|
||||
|
||||
TexAtlas *const atlas =
|
||||
asset_getTextureAtlas(assetManager, world->texType);
|
||||
@ -299,7 +301,9 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
|
||||
/* Populate mob animation references */
|
||||
addAnim(assetManager, animlist_hero_idle, 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,
|
||||
@ -596,97 +600,168 @@ INTERNAL v4 createCameraBounds(World *world, v2 size)
|
||||
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)
|
||||
{
|
||||
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];
|
||||
|
||||
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
|
||||
// game units?
|
||||
f32 battleThreshold = 500.0f;
|
||||
f32 distance = v2_magnitude(hero->pos, entity->pos);
|
||||
|
||||
enum EntityState newState = entitystate_invalid;
|
||||
if (distance <= battleThreshold)
|
||||
{
|
||||
entity->state = entitystate_battle;
|
||||
if (!world->entitiesInBattleIds[entityId])
|
||||
{
|
||||
world->entitiesInBattleIds[entityId] = TRUE;
|
||||
world->numEntitiesInBattle++;
|
||||
}
|
||||
}
|
||||
newState = entitystate_battle;
|
||||
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;
|
||||
}
|
||||
newState = entitystate_idle;
|
||||
|
||||
entityStateSwitch(world, entity, newState);
|
||||
}
|
||||
|
||||
if ((entity->state == entitystate_battle ||
|
||||
entity->state == entitystate_attack) &&
|
||||
entity->type == entitytype_hero)
|
||||
// NOTE(doyle): Allow fall through to entitytype_hero here
|
||||
case entitytype_hero:
|
||||
{
|
||||
EntityStats *stats = entity->stats;
|
||||
if (stats->health > 0)
|
||||
if (entity->state == entitystate_battle ||
|
||||
entity->state == entitystate_attack)
|
||||
{
|
||||
if (entity->state == entitystate_battle)
|
||||
EntityStats *stats = entity->stats;
|
||||
if (stats->health > 0)
|
||||
{
|
||||
if (stats->actionTimer > 0)
|
||||
stats->actionTimer -= dt * stats->actionSpdMul;
|
||||
|
||||
if (stats->actionTimer < 0)
|
||||
if (entity->state == entitystate_battle)
|
||||
{
|
||||
stats->actionTimer = 0;
|
||||
if (stats->queuedAttack == entityattack_invalid)
|
||||
stats->queuedAttack = entityattack_tackle;
|
||||
if (stats->actionTimer > 0)
|
||||
stats->actionTimer -= dt * stats->actionSpdMul;
|
||||
|
||||
beginAttack(entity);
|
||||
if (stats->actionTimer < 0)
|
||||
{
|
||||
stats->actionTimer = 0;
|
||||
if (stats->queuedAttack == entityattack_invalid)
|
||||
stats->queuedAttack = entityattack_tackle;
|
||||
|
||||
beginAttack(entity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stats->busyDuration -= dt;
|
||||
if (stats->busyDuration <= 0)
|
||||
endAttack(world, entity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stats->busyDuration -= dt;
|
||||
if (stats->busyDuration <= 0)
|
||||
endAttack(world, entity);
|
||||
// TODO(doyle): Generalise for all entities
|
||||
hero->stats->entityIdToAttack = -1;
|
||||
hero->state = entitystate_idle;
|
||||
entity->state = entitystate_dead;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(doyle): Generalise for all entities
|
||||
hero->stats->entityIdToAttack = -1;
|
||||
hero->state = entitystate_idle;
|
||||
entity->state = entitystate_dead;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (world->numEntitiesInBattle > 0)
|
||||
{
|
||||
if (hero->state == entitystate_idle)
|
||||
{
|
||||
hero->state = entitystate_battle;
|
||||
world->entityIdInBattle[hero->id] = TRUE;
|
||||
}
|
||||
|
||||
if (hero->stats->entityIdToAttack == -1)
|
||||
hero->stats->entityIdToAttack = entityId;
|
||||
hero->stats->entityIdToAttack = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hero->state == entitystate_battle)
|
||||
{
|
||||
hero->state = entitystate_idle;
|
||||
hero->state = entitystate_idle;
|
||||
world->entityIdInBattle[hero->id] = FALSE;
|
||||
setActiveEntityAnim(hero, animlist_hero_idle);
|
||||
}
|
||||
hero->stats->entityIdToAttack = -1;
|
||||
hero->stats->actionTimer = hero->stats->actionRate;
|
||||
hero->stats->busyDuration = 0;
|
||||
hero->stats->actionTimer = hero->stats->actionRate;
|
||||
hero->stats->busyDuration = 0;
|
||||
}
|
||||
|
||||
updateEntityAnim(entity, dt);
|
||||
|
@ -10,9 +10,6 @@
|
||||
#include "WorldTraveller/WorldTraveller.h"
|
||||
|
||||
#define INVALID_CODE_PATH TRUE
|
||||
#define DEBUG_PUSH_STRING(formatString, data, type) \
|
||||
debug_pushString(formatString, CAST(void *)&data, type)
|
||||
|
||||
enum DebugCallCount
|
||||
{
|
||||
debugcallcount_drawArrays,
|
||||
@ -101,7 +98,12 @@ inline char *debug_entityattack_string(i32 val)
|
||||
|
||||
|
||||
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_stringUpdateAndRender(Renderer *renderer, Font *font, f32 dt);
|
||||
void debug_drawUi(GameState *state, f32 dt);
|
||||
|
||||
|
@ -65,6 +65,8 @@ typedef struct EntityAnim_
|
||||
|
||||
typedef struct Entity
|
||||
{
|
||||
u32 id;
|
||||
|
||||
v2 pos; // Position
|
||||
v2 dPos; // Velocity
|
||||
v2 hitboxSize;
|
||||
|
@ -16,7 +16,7 @@ typedef struct World
|
||||
{
|
||||
Entity *entities;
|
||||
i32 maxEntities;
|
||||
b32 *entitiesInBattleIds;
|
||||
b32 *entityIdInBattle;
|
||||
i32 numEntitiesInBattle;
|
||||
|
||||
enum TexList texType;
|
||||
@ -26,6 +26,7 @@ typedef struct World
|
||||
|
||||
i32 heroIndex;
|
||||
i32 freeEntityIndex;
|
||||
u32 uniqueIdAccumulator;
|
||||
} World;
|
||||
|
||||
typedef struct GameState
|
||||
|
Loading…
Reference in New Issue
Block a user