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)
|
else if (common_strcmp(dataType, "char") == 0)
|
||||||
{
|
{
|
||||||
char *val = CAST(char *)data;
|
if (data)
|
||||||
|
{
|
||||||
|
char *val = CAST(char *) data;
|
||||||
|
|
||||||
snprintf(GLOBAL_debug.debugStrings[numDebugStrings],
|
snprintf(GLOBAL_debug.debugStrings[numDebugStrings],
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
@ -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,97 +600,168 @@ 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;
|
||||||
|
f32 distance = v2_magnitude(hero->pos, entity->pos);
|
||||||
|
|
||||||
|
enum EntityState newState = entitystate_invalid;
|
||||||
if (distance <= battleThreshold)
|
if (distance <= battleThreshold)
|
||||||
{
|
newState = entitystate_battle;
|
||||||
entity->state = entitystate_battle;
|
|
||||||
if (!world->entitiesInBattleIds[entityId])
|
|
||||||
{
|
|
||||||
world->entitiesInBattleIds[entityId] = TRUE;
|
|
||||||
world->numEntitiesInBattle++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
newState = entitystate_idle;
|
||||||
if (world->entitiesInBattleIds[entityId])
|
|
||||||
{
|
entityStateSwitch(world, entity, newState);
|
||||||
world->entitiesInBattleIds[entityId] = FALSE;
|
|
||||||
world->numEntitiesInBattle--;
|
|
||||||
}
|
|
||||||
entity->state = entitystate_idle;
|
|
||||||
entity->stats->actionTimer = entity->stats->actionRate;
|
|
||||||
entity->stats->queuedAttack = entityattack_invalid;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// NOTE(doyle): Allow fall through to entitytype_hero here
|
||||||
if ((entity->state == entitystate_battle ||
|
case entitytype_hero:
|
||||||
entity->state == entitystate_attack) &&
|
|
||||||
entity->type == entitytype_hero)
|
|
||||||
{
|
{
|
||||||
EntityStats *stats = entity->stats;
|
if (entity->state == entitystate_battle ||
|
||||||
if (stats->health > 0)
|
entity->state == entitystate_attack)
|
||||||
{
|
{
|
||||||
if (entity->state == entitystate_battle)
|
EntityStats *stats = entity->stats;
|
||||||
|
if (stats->health > 0)
|
||||||
{
|
{
|
||||||
if (stats->actionTimer > 0)
|
if (entity->state == entitystate_battle)
|
||||||
stats->actionTimer -= dt * stats->actionSpdMul;
|
|
||||||
|
|
||||||
if (stats->actionTimer < 0)
|
|
||||||
{
|
{
|
||||||
stats->actionTimer = 0;
|
if (stats->actionTimer > 0)
|
||||||
if (stats->queuedAttack == entityattack_invalid)
|
stats->actionTimer -= dt * stats->actionSpdMul;
|
||||||
stats->queuedAttack = entityattack_tackle;
|
|
||||||
|
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
stats->busyDuration -= dt;
|
// TODO(doyle): Generalise for all entities
|
||||||
if (stats->busyDuration <= 0)
|
hero->stats->entityIdToAttack = -1;
|
||||||
endAttack(world, entity);
|
hero->state = entitystate_idle;
|
||||||
|
entity->state = entitystate_dead;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
break;
|
||||||
{
|
}
|
||||||
// TODO(doyle): Generalise for all entities
|
default:
|
||||||
hero->stats->entityIdToAttack = -1;
|
break;
|
||||||
hero->state = entitystate_idle;
|
|
||||||
entity->state = entitystate_dead;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
hero->stats->actionTimer = hero->stats->actionRate;
|
hero->stats->actionTimer = hero->stats->actionRate;
|
||||||
hero->stats->busyDuration = 0;
|
hero->stats->busyDuration = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateEntityAnim(entity, dt);
|
updateEntityAnim(entity, dt);
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user