Doyle Thai
85becdfd6c
Until engine architecture can clearly be separated from the game, i.e. notion of entity storage belonging to game and concept of entity belonging to engine, worlds/scenes belonging to game or engine- the project will only refer to Dengine.
441 lines
13 KiB
C
441 lines
13 KiB
C
#include "Dengine/Debug.h"
|
|
#include "Dengine/AssetManager.h"
|
|
#include "Dengine/Entity.h"
|
|
#include "Dengine/MemoryArena.h"
|
|
#include "Dengine/Platform.h"
|
|
#include "Dengine/Renderer.h"
|
|
#include "Dengine/WorldTraveller.h"
|
|
|
|
typedef struct DebugState
|
|
{
|
|
Font font;
|
|
i32 *callCount;
|
|
f32 stringLineGap;
|
|
|
|
/* Debug strings rendered in top left corner */
|
|
char debugStrings[64][128];
|
|
i32 numDebugStrings;
|
|
f32 stringUpdateTimer;
|
|
f32 stringUpdateRate;
|
|
v2 initialStringP;
|
|
v2 currStringP;
|
|
|
|
/* Debug gui console log */
|
|
char console[20][128];
|
|
i32 consoleIndex;
|
|
v2 initialConsoleP;
|
|
} DebugState;
|
|
|
|
GLOBAL_VAR DebugState GLOBAL_debug;
|
|
|
|
inline char *debug_entitystate_string(i32 val)
|
|
{
|
|
char *string;
|
|
switch(val)
|
|
{
|
|
case entitystate_idle:
|
|
string = "EntityState_Idle";
|
|
break;
|
|
case entitystate_battle:
|
|
string = "EntityState_Battle";
|
|
break;
|
|
case entitystate_attack:
|
|
string = "EntityState_Attack";
|
|
break;
|
|
case entitystate_dead:
|
|
string = "EntityState_Dead";
|
|
break;
|
|
case entitystate_count:
|
|
string = "EntityState_Count (Error!)";
|
|
break;
|
|
case entitystate_invalid:
|
|
string = "EntityState_Invalid (Error!)";
|
|
break;
|
|
default:
|
|
string = "EntityState Unknown (NOT DEFINED)";
|
|
}
|
|
return string;
|
|
}
|
|
|
|
inline char *debug_entityattack_string(i32 val)
|
|
{
|
|
char *string;
|
|
switch(val)
|
|
{
|
|
case entityattack_tackle:
|
|
string = "EntityAttack_Tackle";
|
|
break;
|
|
case entityattack_count:
|
|
string = "EntityAttack_Count (Error!)";
|
|
break;
|
|
case entityattack_invalid:
|
|
string = "EntityAttack_Invalid";
|
|
break;
|
|
default:
|
|
string = "EntityAttack Unknown (NOT DEFINED)";
|
|
}
|
|
return string;
|
|
}
|
|
|
|
void debug_init(MemoryArena *arena, v2 windowSize, Font font)
|
|
{
|
|
GLOBAL_debug.font = font;
|
|
GLOBAL_debug.callCount = PLATFORM_MEM_ALLOC(arena, debugcallcount_num, i32);
|
|
GLOBAL_debug.stringLineGap = 1.1f * asset_getVFontSpacing(font.metrics);
|
|
|
|
/* Init debug string stack */
|
|
GLOBAL_debug.numDebugStrings = 0;
|
|
GLOBAL_debug.stringUpdateTimer = 0.0f;
|
|
GLOBAL_debug.stringUpdateRate = 0.15f;
|
|
|
|
GLOBAL_debug.initialStringP =
|
|
V2(0.0f, (windowSize.h - 1.8f * GLOBAL_debug.stringLineGap));
|
|
GLOBAL_debug.currStringP = GLOBAL_debug.initialStringP;
|
|
|
|
/* Init gui console */
|
|
i32 maxConsoleStrLen = ARRAY_COUNT(GLOBAL_debug.console[0]);
|
|
GLOBAL_debug.consoleIndex = 0;
|
|
|
|
// TODO(doyle): Font max size not entirely correct? using 1 * font.maxSize.w
|
|
// reveals around 4 characters ..
|
|
f32 consoleXPos = font.maxSize.w * 20;
|
|
f32 consoleYPos = windowSize.h - 1.8f * GLOBAL_debug.stringLineGap;
|
|
GLOBAL_debug.initialConsoleP = V2(consoleXPos, consoleYPos);
|
|
}
|
|
|
|
void debug_callCountIncrement(i32 id)
|
|
{
|
|
ASSERT(id < debugcallcount_num);
|
|
GLOBAL_debug.callCount[id]++;
|
|
}
|
|
|
|
void debug_clearCallCounter()
|
|
{
|
|
for (i32 i = 0; i < debugcallcount_num; i++)
|
|
GLOBAL_debug.callCount[i] = 0;
|
|
}
|
|
|
|
|
|
void debug_consoleLog(char *string, char *file, int lineNum)
|
|
{
|
|
|
|
i32 maxConsoleStrLen = ARRAY_COUNT(GLOBAL_debug.console[0]);
|
|
|
|
/* Completely clear out current console line */
|
|
for (i32 i = 0; i < maxConsoleStrLen; i++)
|
|
GLOBAL_debug.console[GLOBAL_debug.consoleIndex][i] = 0;
|
|
|
|
i32 strIndex = 0;
|
|
i32 fileStrLen = common_strlen(file);
|
|
for (i32 count = 0; strIndex < maxConsoleStrLen; strIndex++, count++)
|
|
{
|
|
if (fileStrLen <= count) break;
|
|
GLOBAL_debug.console[GLOBAL_debug.consoleIndex][strIndex] = file[count];
|
|
}
|
|
|
|
if (strIndex < maxConsoleStrLen)
|
|
GLOBAL_debug.console[GLOBAL_debug.consoleIndex][strIndex++] = ':';
|
|
|
|
char line[12] = {0};
|
|
common_itoa(lineNum, line, ARRAY_COUNT(line));
|
|
i32 lineStrLen = common_strlen(line);
|
|
for (i32 count = 0; strIndex < maxConsoleStrLen; strIndex++, count++)
|
|
{
|
|
if (lineStrLen <= count) break;
|
|
GLOBAL_debug.console[GLOBAL_debug.consoleIndex][strIndex] = line[count];
|
|
}
|
|
|
|
if (strIndex < maxConsoleStrLen)
|
|
GLOBAL_debug.console[GLOBAL_debug.consoleIndex][strIndex++] = ':';
|
|
|
|
i32 stringStrLen = common_strlen(string);
|
|
for (i32 count = 0; strIndex < maxConsoleStrLen; strIndex++, count++)
|
|
{
|
|
if (stringStrLen <= count) break;
|
|
GLOBAL_debug.console[GLOBAL_debug.consoleIndex][strIndex] = string[count];
|
|
}
|
|
|
|
if (strIndex >= maxConsoleStrLen)
|
|
{
|
|
GLOBAL_debug.console[GLOBAL_debug.consoleIndex][maxConsoleStrLen-4] = '.';
|
|
GLOBAL_debug.console[GLOBAL_debug.consoleIndex][maxConsoleStrLen-3] = '.';
|
|
GLOBAL_debug.console[GLOBAL_debug.consoleIndex][maxConsoleStrLen-2] = '.';
|
|
GLOBAL_debug.console[GLOBAL_debug.consoleIndex][maxConsoleStrLen-1] = 0;
|
|
}
|
|
|
|
i32 maxConsoleLines = ARRAY_COUNT(GLOBAL_debug.console);
|
|
GLOBAL_debug.consoleIndex++;
|
|
|
|
if (GLOBAL_debug.consoleIndex >= maxConsoleLines)
|
|
GLOBAL_debug.consoleIndex = 0;
|
|
}
|
|
|
|
void debug_pushString(char *formatString, void *data, char *dataType)
|
|
{
|
|
if (GLOBAL_debug.numDebugStrings >=
|
|
ARRAY_COUNT(GLOBAL_debug.debugStrings))
|
|
{
|
|
DEBUG_LOG("Debug string stack is full, DEBUG_PUSH() failed");
|
|
return;
|
|
}
|
|
|
|
if (GLOBAL_debug.stringUpdateTimer <= 0)
|
|
{
|
|
i32 numDebugStrings = GLOBAL_debug.numDebugStrings;
|
|
if (common_strcmp(dataType, "v2") == 0)
|
|
{
|
|
v2 val = *(CAST(v2 *) data);
|
|
snprintf(GLOBAL_debug.debugStrings[numDebugStrings],
|
|
ARRAY_COUNT(GLOBAL_debug.debugStrings[0]),
|
|
formatString, val.x, val.y);
|
|
}
|
|
else if (common_strcmp(dataType, "i32") == 0)
|
|
{
|
|
i32 val = *(CAST(i32 *) data);
|
|
snprintf(GLOBAL_debug.debugStrings[numDebugStrings],
|
|
ARRAY_COUNT(GLOBAL_debug.debugStrings[0]),
|
|
formatString, val);
|
|
}
|
|
else if (common_strcmp(dataType, "f32") == 0)
|
|
{
|
|
f32 val = *(CAST(f32 *) data);
|
|
snprintf(GLOBAL_debug.debugStrings[numDebugStrings],
|
|
ARRAY_COUNT(GLOBAL_debug.debugStrings[0]),
|
|
formatString, val);
|
|
}
|
|
else if (common_strcmp(dataType, "char") == 0)
|
|
{
|
|
if (data)
|
|
{
|
|
char *val = CAST(char *) data;
|
|
|
|
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);
|
|
}
|
|
GLOBAL_debug.numDebugStrings++;
|
|
}
|
|
}
|
|
|
|
INTERNAL void updateAndRenderDebugStack(Renderer *renderer, MemoryArena *arena,
|
|
f32 dt)
|
|
{
|
|
for (i32 i = 0; i < GLOBAL_debug.numDebugStrings; i++)
|
|
{
|
|
f32 rotate = 0;
|
|
v4 color = V4(0, 0, 0, 1);
|
|
renderer_staticString(renderer, arena, &GLOBAL_debug.font,
|
|
GLOBAL_debug.debugStrings[i],
|
|
GLOBAL_debug.currStringP, V2(0, 0), rotate, color);
|
|
GLOBAL_debug.currStringP.y -= (0.9f * GLOBAL_debug.stringLineGap);
|
|
}
|
|
|
|
if (GLOBAL_debug.stringUpdateTimer <= 0)
|
|
{
|
|
GLOBAL_debug.stringUpdateTimer =
|
|
GLOBAL_debug.stringUpdateRate;
|
|
}
|
|
else
|
|
{
|
|
GLOBAL_debug.stringUpdateTimer -= dt;
|
|
if (GLOBAL_debug.stringUpdateTimer <= 0)
|
|
{
|
|
GLOBAL_debug.numDebugStrings = 0;
|
|
}
|
|
}
|
|
|
|
GLOBAL_debug.currStringP = GLOBAL_debug.initialStringP;
|
|
|
|
}
|
|
|
|
INTERNAL void renderConsole(Renderer *renderer, MemoryArena *arena)
|
|
{
|
|
i32 maxConsoleLines = ARRAY_COUNT(GLOBAL_debug.console);
|
|
v2 consoleStrP = GLOBAL_debug.initialConsoleP;
|
|
for (i32 i = 0; i < maxConsoleLines; i++)
|
|
{
|
|
f32 rotate = 0;
|
|
v4 color = V4(0, 0, 0, 1);
|
|
renderer_staticString(renderer, arena, &GLOBAL_debug.font,
|
|
GLOBAL_debug.console[i], consoleStrP,
|
|
V2(0, 0), rotate, color);
|
|
consoleStrP.y -= (0.9f * GLOBAL_debug.stringLineGap);
|
|
}
|
|
}
|
|
|
|
void debug_drawUi(GameState *state, f32 dt)
|
|
{
|
|
AssetManager *assetManager = &state->assetManager;
|
|
Renderer *renderer = &state->renderer;
|
|
World *const world = &state->world[state->currWorldIndex];
|
|
Entity *hero = &world->entities[world->heroIndex];
|
|
|
|
// TODO(doyle): Dumb copy function from game so we don't expose api
|
|
v4 cameraBounds = math_getRect(world->cameraPos, renderer->size);
|
|
if (cameraBounds.x <= world->bounds.x)
|
|
{
|
|
cameraBounds.x = world->bounds.x;
|
|
cameraBounds.z = cameraBounds.x + renderer->size.w;
|
|
}
|
|
|
|
if (cameraBounds.y >= world->bounds.y) cameraBounds.y = world->bounds.y;
|
|
|
|
if (cameraBounds.z >= world->bounds.z)
|
|
{
|
|
cameraBounds.z = world->bounds.z;
|
|
cameraBounds.x = cameraBounds.z - renderer->size.w;
|
|
}
|
|
if (cameraBounds.w <= world->bounds.w) cameraBounds.w = world->bounds.w;
|
|
|
|
Font *font = &GLOBAL_debug.font;
|
|
if (world->numEntitiesInBattle > 0)
|
|
{
|
|
v4 color = V4(1.0f, 0, 0, 1);
|
|
char *battleStr = "IN-BATTLE RANGE";
|
|
f32 strLenInPixels =
|
|
CAST(f32)(font->maxSize.w * common_strlen(battleStr));
|
|
v2 strPos = V2((renderer->size.w * 0.5f) - (strLenInPixels * 0.5f),
|
|
renderer->size.h - 300.0f);
|
|
renderer_staticString(&state->renderer, &state->arena, font, battleStr,
|
|
strPos, V2(0, 0), 0, color);
|
|
}
|
|
|
|
for (i32 i = 0; i < world->freeEntityIndex; i++)
|
|
{
|
|
Entity *const entity = &world->entities[i];
|
|
if (entity->state == entitystate_dead) continue;
|
|
|
|
/* Render debug markers on entities */
|
|
v4 color = V4(1, 1, 1, 1);
|
|
char *debugString = NULL;
|
|
switch (entity->type)
|
|
{
|
|
case entitytype_mob:
|
|
color = V4(1, 0, 0, 1);
|
|
debugString = "MOB";
|
|
break;
|
|
|
|
case entitytype_hero:
|
|
color = V4(0, 0, 1.0f, 1);
|
|
debugString = "HERO";
|
|
break;
|
|
|
|
case entitytype_npc:
|
|
color = V4(0, 1.0f, 0, 1);
|
|
debugString = "NPC";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (debugString)
|
|
{
|
|
v2 strPos = v2_add(entity->pos, entity->hitboxSize);
|
|
i32 indexOfLowerAInMetrics = 'a' - CAST(i32) font->codepointRange.x;
|
|
strPos.y += font->charMetrics[indexOfLowerAInMetrics].offset.y;
|
|
|
|
renderer_string(&state->renderer, &state->arena, cameraBounds, font,
|
|
debugString, strPos, V2(0, 0), 0, color);
|
|
|
|
f32 stringLineGap = 1.1f * asset_getVFontSpacing(font->metrics);
|
|
strPos.y -= GLOBAL_debug.stringLineGap;
|
|
|
|
char entityPosStr[128];
|
|
snprintf(entityPosStr, ARRAY_COUNT(entityPosStr), "%06.2f, %06.2f",
|
|
entity->pos.x, entity->pos.y);
|
|
renderer_string(&state->renderer, &state->arena, cameraBounds, font,
|
|
entityPosStr, strPos, V2(0, 0), 0, color);
|
|
|
|
strPos.y -= GLOBAL_debug.stringLineGap;
|
|
char entityIDStr[32];
|
|
snprintf(entityIDStr, ARRAY_COUNT(entityIDStr), "ID: %4d/%d", entity->id,
|
|
world->uniqueIdAccumulator-1);
|
|
renderer_string(&state->renderer, &state->arena, cameraBounds, font,
|
|
entityIDStr, strPos, V2(0, 0), 0, color);
|
|
|
|
if (entity->stats)
|
|
{
|
|
strPos.y -= GLOBAL_debug.stringLineGap;
|
|
char entityHealth[32];
|
|
snprintf(entityHealth, ARRAY_COUNT(entityHealth), "HP: %3.0f/%3.0f",
|
|
entity->stats->health, entity->stats->maxHealth);
|
|
renderer_string(&state->renderer, &state->arena, cameraBounds,
|
|
font, entityHealth, strPos, V2(0, 0), 0, color);
|
|
|
|
strPos.y -= GLOBAL_debug.stringLineGap;
|
|
char entityTimer[32];
|
|
snprintf(entityTimer, ARRAY_COUNT(entityTimer), "ATB: %3.0f/%3.0f",
|
|
entity->stats->actionTimer, entity->stats->actionRate);
|
|
renderer_string(&state->renderer, &state->arena, cameraBounds,
|
|
font, entityTimer, strPos, V2(0, 0), 0, color);
|
|
|
|
strPos.y -= GLOBAL_debug.stringLineGap;
|
|
char entityIdTarget[32];
|
|
snprintf(entityIdTarget, ARRAY_COUNT(entityIdTarget),
|
|
"Targetting ID: %d", entity->stats->entityIdToAttack);
|
|
renderer_string(&state->renderer, &state->arena, cameraBounds,
|
|
font, entityIdTarget, strPos, V2(0, 0), 0, color);
|
|
}
|
|
|
|
strPos.y -= GLOBAL_debug.stringLineGap;
|
|
char *entityStateStr = debug_entitystate_string(entity->state);
|
|
renderer_string(&state->renderer, &state->arena, cameraBounds, font,
|
|
entityStateStr, strPos, V2(0, 0), 0, color);
|
|
}
|
|
}
|
|
|
|
/* Render debug info stack */
|
|
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_VAR("Hero State: %s", *heroStateString, "char");
|
|
char *heroQueuedAttackStr =
|
|
debug_entityattack_string(hero->stats->queuedAttack);
|
|
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_bAllocated = state->arena.bytesAllocated;
|
|
DEBUG_PUSH_VAR("TotalMemoryAllocated: %db", debug_bAllocated, "i32");
|
|
i32 debug_kbAllocated = state->arena.bytesAllocated / 1024;
|
|
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-");
|
|
}
|
|
|
|
updateAndRenderDebugStack(&state->renderer, &state->arena, dt);
|
|
renderConsole(&state->renderer, &state->arena);
|
|
debug_clearCallCounter();
|
|
}
|