Mobs battle with hero, engine architect. changes

Mobs battle in real-time with the hero when in range. Start encapsulating
logic into entityStateSwitch() to organise entity properties changing
between different states.

Introduce basic concept of memory management in MemoryArena. Begin passing
around a memory object to track memory usage and eventually delegate
memory allocations through. Remove the old memory tracker in the debug
object and incorporate into engine primarily.

Add a debug console for logging information and events to the screen in
a console-like fashion. Debug is now initialised after the game, this is
so that you can pass the game's memory arena and font file to the debug
services. Debug services now properly initialise debug element positions
from this information and not through the update routine.
This commit is contained in:
Doyle Thai 2016-07-19 21:19:26 +10:00
parent 5fc58ca643
commit fd94eb5afd
17 changed files with 386 additions and 163 deletions

View File

@ -140,6 +140,7 @@
<ItemGroup>
<ClInclude Include="src\include\Dengine\Assets.h" />
<ClInclude Include="src\include\Dengine\Debug.h" />
<ClInclude Include="src\include\Dengine\MemoryArena.h" />
<ClInclude Include="src\include\Dengine\Platform.h" />
<ClInclude Include="src\include\Dengine\AssetManager.h" />
<ClInclude Include="src\include\Dengine\Common.h" />

View File

@ -92,5 +92,8 @@
<ClInclude Include="src\include\Dengine\Assets.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\include\Dengine\MemoryArena.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -95,11 +95,11 @@ Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type)
return NULL;
}
INTERNAL GLuint createShaderFromPath(const char *const path, GLuint shadertype)
INTERNAL GLuint createShaderFromPath(MemoryArena *arena, const char *const path, GLuint shadertype)
{
PlatformFileRead file = {0};
i32 status = platform_readFileToBuffer(path, &file);
i32 status = platform_readFileToBuffer(arena, path, &file);
if (status)
return status;
@ -118,7 +118,7 @@ INTERNAL GLuint createShaderFromPath(const char *const path, GLuint shadertype)
printf("glCompileShader() failed: %s\n", infoLog);
}
platform_closeFileRead(&file);
platform_closeFileRead(arena, &file);
return result;
}
@ -147,14 +147,14 @@ INTERNAL i32 shaderLoadProgram(Shader *const shader, const GLuint vertexShader,
return 0;
}
const i32 asset_loadShaderFiles(AssetManager *assetManager,
const i32 asset_loadShaderFiles(AssetManager *assetManager, MemoryArena *arena,
const char *const vertexPath,
const char *const fragmentPath,
const enum ShaderList type)
{
GLuint vertexShader = createShaderFromPath(vertexPath, GL_VERTEX_SHADER);
GLuint vertexShader = createShaderFromPath(arena, vertexPath, GL_VERTEX_SHADER);
GLuint fragmentShader =
createShaderFromPath(fragmentPath, GL_FRAGMENT_SHADER);
createShaderFromPath(arena, fragmentPath, GL_FRAGMENT_SHADER);
Shader shader;
i32 result = shaderLoadProgram(&shader, vertexShader, fragmentShader);
@ -173,10 +173,11 @@ typedef struct GlyphBitmap
i32 codepoint;
} GlyphBitmap;
const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
const char *filePath)
{
PlatformFileRead fontFileRead = {0};
platform_readFileToBuffer(filePath, &fontFileRead);
platform_readFileToBuffer(arena, filePath, &fontFileRead);
stbtt_fontinfo fontInfo = {0};
stbtt_InitFont(&fontInfo, fontFileRead.buffer,
@ -188,7 +189,8 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
v2 codepointRange = font->codepointRange;
const i32 numGlyphs = CAST(i32)(codepointRange.y - codepointRange.x);
GlyphBitmap *glyphBitmaps = PLATFORM_MEM_ALLOC(numGlyphs, GlyphBitmap);
GlyphBitmap *glyphBitmaps =
PLATFORM_MEM_ALLOC(arena, numGlyphs, GlyphBitmap);
v2 largestGlyphDimension = V2(0, 0);
const f32 targetFontHeight = 15.0f;
@ -203,7 +205,7 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
font->metrics = CAST(FontMetrics){ascent, descent, lineGap};
font->charMetrics = PLATFORM_MEM_ALLOC(numGlyphs, CharMetrics);
font->charMetrics = PLATFORM_MEM_ALLOC(arena, numGlyphs, CharMetrics);
/* Use STB_TrueType to generate a series of bitmap characters */
i32 glyphIndex = 0;
@ -218,7 +220,7 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
&height, &xOffset, &yOffset);
u8 *source = monoBitmap;
u32 *colorBitmap = PLATFORM_MEM_ALLOC(width * height, u32);
u32 *colorBitmap = PLATFORM_MEM_ALLOC(arena, width * height, u32);
u32 *dest = colorBitmap;
// NOTE(doyle): STB generates 1 byte per pixel bitmaps, we use 4bpp, so
@ -294,7 +296,7 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
#endif
i32 bitmapSize = SQUARED(TARGET_TEXTURE_SIZE) * TARGET_BYTES_PER_PIXEL;
u32 *fontBitmap = PLATFORM_MEM_ALLOC(bitmapSize, u32);
u32 *fontBitmap = PLATFORM_MEM_ALLOC(arena, bitmapSize, u32);
const i32 pitch = MAX_TEXTURE_SIZE * TARGET_BYTES_PER_PIXEL;
// Check value to determine when a row of glyphs is completely printed
@ -394,7 +396,7 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
stbi_write_png("out.png", MAX_TEXTURE_SIZE, MAX_TEXTURE_SIZE, 4, fontBitmap,
MAX_TEXTURE_SIZE * 4);
#endif
PLATFORM_MEM_FREE(fontBitmap, bitmapSize);
PLATFORM_MEM_FREE(arena, fontBitmap, bitmapSize);
font->tex = &assetManager->textures[texlist_font];
font->atlas = &assetManager->texAtlas[texlist_font];
@ -404,18 +406,18 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
i32 glyphBitmapSizeInBytes = CAST(i32) glyphBitmaps[i].dimensions.w *
CAST(i32) glyphBitmaps[i].dimensions.h *
sizeof(u32);
PLATFORM_MEM_FREE(glyphBitmaps[i].pixels, glyphBitmapSizeInBytes);
PLATFORM_MEM_FREE(arena, glyphBitmaps[i].pixels, glyphBitmapSizeInBytes);
}
PLATFORM_MEM_FREE(glyphBitmaps, numGlyphs * sizeof(GlyphBitmap));
platform_closeFileRead(&fontFileRead);
PLATFORM_MEM_FREE(arena, glyphBitmaps, numGlyphs * sizeof(GlyphBitmap));
platform_closeFileRead(arena, &fontFileRead);
return 0;
}
void asset_addAnimation(AssetManager *assetManager, i32 texId,
i32 animId, i32 *frameIndex, i32 numFrames,
f32 frameDuration)
void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena,
i32 texId, i32 animId, i32 *frameIndex, i32 numFrames,
f32 frameDuration)
{
#ifdef DENGINE_DEBUG
ASSERT(assetManager && frameIndex)
@ -425,7 +427,7 @@ void asset_addAnimation(AssetManager *assetManager, i32 texId,
Animation anim = {0};
anim.atlas = asset_getTextureAtlas(assetManager, texId);
anim.frameIndex = PLATFORM_MEM_ALLOC(numFrames, i32);
anim.frameIndex = PLATFORM_MEM_ALLOC(arena, numFrames, i32);
for (i32 i = 0; i < numFrames; i++) anim.frameIndex[i] = frameIndex[i];
anim.numFrames = numFrames;

View File

@ -1,4 +1,5 @@
#include "Dengine\Common.h"
#include "Dengine/Common.h"
#include "Dengine/Math.h"
i32 common_strlen(const char *const string)
{
@ -19,3 +20,24 @@ i32 common_strcmp(const char *a, const char *b)
return ((*a < *b) ? -1 : 1);
}
void common_itoa(i32 value, char *buf, i32 bufSize)
{
if (!buf || bufSize == 0) return;
// NOTE(doyle): Max 32bit integer (+-)2147483647
i32 charIndex = 0;
b32 negative = FALSE;
if (value < 0) negative = TRUE;
if (negative) buf[charIndex++] = '-';
i32 val = ABS(value);
while (val != 0 && charIndex < bufSize)
{
i32 rem = val % 10;
buf[charIndex++] = rem + '0';
val /= 10;
}
}

View File

@ -4,16 +4,30 @@
DebugState GLOBAL_debug;
void debug_init()
void debug_init(MemoryArena *arena, v2 windowSize, Font font)
{
GLOBAL_debug.totalMemoryAllocated = 0;
GLOBAL_debug.callCount = PLATFORM_MEM_ALLOC(debugcallcount_num, i32);
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.stringLineGap = -1;
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 = windowSize.x - (font.maxSize.w * 38);
f32 consoleYPos = windowSize.h - 1.8f * GLOBAL_debug.stringLineGap;
GLOBAL_debug.initialConsoleP = V2(consoleXPos, consoleYPos);
}
void debug_pushString(char *formatString, void *data, char *dataType)
@ -65,29 +79,22 @@ void debug_pushString(char *formatString, void *data, char *dataType)
ASSERT(INVALID_CODE_PATH);
}
GLOBAL_debug.numDebugStrings++;
ASSERT(GLOBAL_debug.numDebugStrings <
ARRAY_COUNT(GLOBAL_debug.debugStrings[0]));
}
}
void debug_stringUpdateAndRender(Renderer *renderer, Font *font, f32 dt)
INTERNAL void updateAndRenderDebugStack(Renderer *renderer, MemoryArena *arena,
f32 dt)
{
if (GLOBAL_debug.stringLineGap == -1)
{
GLOBAL_debug.stringLineGap =
1.1f * asset_getVFontSpacing(font->metrics);
GLOBAL_debug.initialStringPos =
V2(0.0f,
(renderer->size.y - 1.8f * GLOBAL_debug.stringLineGap));
GLOBAL_debug.stringPos = GLOBAL_debug.initialStringPos;
}
for (i32 i = 0; i < GLOBAL_debug.numDebugStrings; i++)
{
f32 rotate = 0;
v4 color = V4(0, 0, 0, 1);
renderer_staticString(renderer, font, GLOBAL_debug.debugStrings[i],
GLOBAL_debug.stringPos, rotate, color);
GLOBAL_debug.stringPos.y -=
(0.9f * GLOBAL_debug.stringLineGap);
renderer_staticString(renderer, arena, &GLOBAL_debug.font,
GLOBAL_debug.debugStrings[i],
GLOBAL_debug.currStringP, rotate, color);
GLOBAL_debug.currStringP.y -= (0.9f * GLOBAL_debug.stringLineGap);
}
if (GLOBAL_debug.stringUpdateTimer <= 0)
@ -104,7 +111,23 @@ void debug_stringUpdateAndRender(Renderer *renderer, Font *font, f32 dt)
}
}
GLOBAL_debug.stringPos = GLOBAL_debug.initialStringPos;
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,
rotate, color);
consoleStrP.y -= (0.9f * GLOBAL_debug.stringLineGap);
}
}
void debug_drawUi(GameState *state, f32 dt)
@ -113,7 +136,6 @@ void debug_drawUi(GameState *state, f32 dt)
Renderer *renderer = &state->renderer;
World *const world = &state->world[state->currWorldIndex];
Entity *hero = &world->entities[world->heroIndex];
Font *font = &assetManager->font;
// TODO(doyle): Dumb copy function from game so we don't expose api
v4 cameraBounds = math_getRect(world->cameraPos, renderer->size);
@ -140,6 +162,7 @@ void debug_drawUi(GameState *state, f32 dt)
V2(distance, 5.0f), 0, renderTex, V4(1, 0, 0, 0.25f));
#endif
Font *font = &GLOBAL_debug.font;
if (world->numEntitiesInBattle > 0)
{
v4 color = V4(1.0f, 0, 0, 1);
@ -148,8 +171,8 @@ void debug_drawUi(GameState *state, f32 dt)
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, font, battleStr, strPos, 0,
color);
renderer_staticString(&state->renderer, &state->arena, font, battleStr,
strPos, 0, color);
}
for (i32 i = 0; i < world->maxEntities; i++)
@ -185,8 +208,8 @@ void debug_drawUi(GameState *state, f32 dt)
i32 indexOfLowerAInMetrics = 'a' - CAST(i32) font->codepointRange.x;
strPos.y += font->charMetrics[indexOfLowerAInMetrics].offset.y;
renderer_string(&state->renderer, cameraBounds, font, debugString,
strPos, 0, color);
renderer_string(&state->renderer, &state->arena, cameraBounds, font,
debugString, strPos, 0, color);
f32 stringLineGap = 1.1f * asset_getVFontSpacing(font->metrics);
strPos.y -= GLOBAL_debug.stringLineGap;
@ -194,15 +217,15 @@ void debug_drawUi(GameState *state, f32 dt)
char entityPosStr[128];
snprintf(entityPosStr, ARRAY_COUNT(entityPosStr), "%06.2f, %06.2f",
entity->pos.x, entity->pos.y);
renderer_string(&state->renderer, cameraBounds, font, entityPosStr,
strPos, 0, color);
renderer_string(&state->renderer, &state->arena, cameraBounds, font,
entityPosStr, strPos, 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, cameraBounds, font, entityIDStr,
strPos, 0, color);
renderer_string(&state->renderer, &state->arena, cameraBounds, font,
entityIDStr, strPos, 0, color);
if (entity->stats)
{
@ -210,20 +233,20 @@ void debug_drawUi(GameState *state, f32 dt)
char entityHealth[32];
snprintf(entityHealth, ARRAY_COUNT(entityHealth), "HP: %3.0f/%3.0f",
entity->stats->health, entity->stats->maxHealth);
renderer_string(&state->renderer, cameraBounds, font,
entityHealth, strPos, 0, color);
renderer_string(&state->renderer, &state->arena, cameraBounds,
font, entityHealth, strPos, 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, cameraBounds, font,
entityTimer, strPos, 0, color);
renderer_string(&state->renderer, &state->arena, cameraBounds,
font, entityTimer, strPos, 0, color);
}
strPos.y -= GLOBAL_debug.stringLineGap;
char *entityStateStr = debug_entitystate_string(entity->state);
renderer_string(&state->renderer, cameraBounds, font,
renderer_string(&state->renderer, &state->arena, cameraBounds, font,
entityStateStr, strPos, 0, color);
}
}
@ -243,7 +266,8 @@ void debug_drawUi(GameState *state, f32 dt)
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 = state->arena.bytesAllocated / 1024;
DEBUG_PUSH_VAR("TotalMemoryAllocated: %dkb", debug_kbAllocated, "i32");
DEBUG_PUSH_STRING("== EntityIDs in Battle List == ");
@ -262,6 +286,56 @@ void debug_drawUi(GameState *state, f32 dt)
DEBUG_PUSH_STRING("-none-");
}
debug_stringUpdateAndRender(&state->renderer, font, dt);
updateAndRenderDebugStack(&state->renderer, &state->arena, dt);
renderConsole(&state->renderer, &state->arena);
debug_clearCallCounter();
}
void debug_consoleLog(char *string, char *file, int lineNum)
{
i32 maxConsoleStrLen = ARRAY_COUNT(GLOBAL_debug.console[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;
}

View File

@ -5,32 +5,32 @@
#include "Dengine/Platform.h"
#include "Dengine/Debug.h"
void platform_memoryFree(void *data, i32 numBytes)
void platform_memoryFree(MemoryArena *arena, void *data, i32 numBytes)
{
if (data) free(data);
#ifdef DENGINE_DEBUG
GLOBAL_debug.totalMemoryAllocated -= numBytes;
arena->bytesAllocated -= numBytes;
#endif
}
void *platform_memoryAlloc(i32 numBytes)
void *platform_memoryAlloc(MemoryArena *arena, i32 numBytes)
{
void *result = calloc(1, numBytes);
#ifdef DENGINE_DEBUG
if (result)
GLOBAL_debug.totalMemoryAllocated += numBytes;
arena->bytesAllocated += numBytes;
#endif
return result;
}
void platform_closeFileRead(PlatformFileRead *file)
void platform_closeFileRead(MemoryArena *arena, PlatformFileRead *file)
{
PLATFORM_MEM_FREE(file->buffer, file->size);
PLATFORM_MEM_FREE(arena, file->buffer, file->size);
}
i32 platform_readFileToBuffer(const char *const filePath,
i32 platform_readFileToBuffer(MemoryArena *arena, const char *const filePath,
PlatformFileRead *file)
{
HANDLE fileHandle = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ,
@ -52,7 +52,7 @@ i32 platform_readFileToBuffer(const char *const filePath,
}
// TODO(doyle): Warning we assume files less than 4GB
file->buffer = PLATFORM_MEM_ALLOC(fileSize.LowPart, char);
file->buffer = PLATFORM_MEM_ALLOC(arena, fileSize.LowPart, char);
file->size = fileSize.LowPart;
DWORD numBytesRead = 0;
@ -63,7 +63,7 @@ i32 platform_readFileToBuffer(const char *const filePath,
{
printf("ReadFile() failed: %d error number\n",
status);
PLATFORM_MEM_FREE(file->buffer, file->size);
PLATFORM_MEM_FREE(arena, file->buffer, file->size);
return status;
}
else if (numBytesRead != file->size)
@ -71,7 +71,7 @@ i32 platform_readFileToBuffer(const char *const filePath,
printf(
"ReadFile() failed: Number of bytes read doesn't match file "
"size\n");
PLATFORM_MEM_FREE(file->buffer, file->size);
PLATFORM_MEM_FREE(arena, file->buffer, file->size);
return -1;
}

View File

@ -152,9 +152,9 @@ void renderer_rect(Renderer *const renderer, v4 cameraBounds, v2 pos, v2 size,
renderTex.tex);
}
void renderer_string(Renderer *const renderer, v4 cameraBounds,
Font *const font, const char *const string, v2 pos,
f32 rotate, v4 color)
void renderer_string(Renderer *const renderer, MemoryArena *arena,
v4 cameraBounds, Font *const font,
const char *const string, v2 pos, f32 rotate, v4 color)
{
i32 strLen = common_strlen(string);
// TODO(doyle): Scale, not too important .. but rudimentary infrastructure
@ -172,7 +172,7 @@ void renderer_string(Renderer *const renderer, v4 cameraBounds,
(leftAlignedP.y < cameraBounds.y && rightAlignedP.y >= cameraBounds.w))
{
i32 quadIndex = 0;
RenderQuad *stringQuads = PLATFORM_MEM_ALLOC(strLen, RenderQuad);
RenderQuad *stringQuads = PLATFORM_MEM_ALLOC(arena, strLen, RenderQuad);
v2 offsetFromCamOrigin = V2(cameraBounds.x, cameraBounds.w);
v2 entityRelativeToCamera = v2_sub(pos, offsetFromCamOrigin);
@ -210,7 +210,7 @@ void renderer_string(Renderer *const renderer, v4 cameraBounds,
updateBufferObject(renderer, stringQuads, quadIndex);
renderObject(renderer, V2(0.0f, 0.0f), renderer->size, rotate, color,
font->tex);
PLATFORM_MEM_FREE(stringQuads, strLen * sizeof(RenderQuad));
PLATFORM_MEM_FREE(arena, stringQuads, strLen * sizeof(RenderQuad));
}
}

View File

@ -10,8 +10,9 @@ enum State
state_win,
};
INTERNAL Entity *addEntity(World *world, v2 pos, v2 size, enum EntityType type,
enum Direction direction, Texture *tex, b32 collides)
INTERNAL Entity *addEntity(MemoryArena *arena, World *world, v2 pos, v2 size,
enum EntityType type, enum Direction direction,
Texture *tex, b32 collides)
{
#ifdef DENGINE_DEBUG
@ -35,7 +36,7 @@ INTERNAL Entity *addEntity(World *world, v2 pos, v2 size, enum EntityType type,
case entitytype_hero:
case entitytype_mob:
{
entity.stats = PLATFORM_MEM_ALLOC(1, EntityStats);
entity.stats = PLATFORM_MEM_ALLOC(arena, 1, EntityStats);
entity.stats->maxHealth = 100;
entity.stats->health = entity.stats->maxHealth;
entity.stats->actionRate = 100;
@ -107,6 +108,7 @@ INTERNAL void addAnim(AssetManager *assetManager, i32 animId, Entity *entity)
void worldTraveller_gameInit(GameState *state, v2 windowSize)
{
AssetManager *assetManager = &state->assetManager;
MemoryArena *arena = &state->arena;
/* Initialise assets */
/* Create empty 1x1 4bpp black texture */
u32 bitmap = (0xFF << 24) | (0xFF << 16) | (0xFF << 8) | (0xFF << 0);
@ -142,29 +144,33 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
texOrigin.y - atlasTileSize);
/* Load shaders */
asset_loadShaderFiles(assetManager, "data/shaders/sprite.vert.glsl",
asset_loadShaderFiles(assetManager, arena, "data/shaders/sprite.vert.glsl",
"data/shaders/sprite.frag.glsl",
shaderlist_sprite);
asset_loadTTFont(assetManager, "C:/Windows/Fonts/Arialbd.ttf");
asset_loadTTFont(assetManager, arena, "C:/Windows/Fonts/Arialbd.ttf");
glCheckError();
#ifdef DENGINE_DEBUG
DEBUG_LOG("Assets loaded");
#endif
/* Load animations */
f32 duration = 1.0f;
i32 numRects = 1;
v4 *animRects = PLATFORM_MEM_ALLOC(numRects, v4);
v4 *animRects = PLATFORM_MEM_ALLOC(arena, numRects, v4);
i32 terrainAnimAtlasIndexes[1] = {terrainrects_ground};
// TODO(doyle): Optimise animation storage, we waste space having 1:1 with
// animlist when some textures don't have certain animations
asset_addAnimation(assetManager, texlist_terrain, animlist_terrain,
asset_addAnimation(assetManager, arena, texlist_terrain, animlist_terrain,
terrainAnimAtlasIndexes, numRects, duration);
// Idle animation
duration = 1.0f;
numRects = 1;
i32 idleAnimAtlasIndexes[1] = {herorects_idle};
asset_addAnimation(assetManager, texlist_hero, animlist_hero_idle,
asset_addAnimation(assetManager, arena, texlist_hero, animlist_hero_idle,
idleAnimAtlasIndexes, numRects, duration);
// Walk animation
@ -172,21 +178,21 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
numRects = 3;
i32 walkAnimAtlasIndexes[3] = {herorects_walkA, herorects_idle,
herorects_walkB};
asset_addAnimation(assetManager, texlist_hero, animlist_hero_walk,
asset_addAnimation(assetManager, arena, texlist_hero, animlist_hero_walk,
walkAnimAtlasIndexes, numRects, duration);
// Wave animation
duration = 0.30f;
numRects = 2;
i32 waveAnimAtlasIndexes[2] = {herorects_waveA, herorects_waveB};
asset_addAnimation(assetManager, texlist_hero, animlist_hero_wave,
asset_addAnimation(assetManager, arena, texlist_hero, animlist_hero_wave,
waveAnimAtlasIndexes, numRects, duration);
// Battle Stance animation
duration = 1.0f;
numRects = 1;
i32 battleStanceAnimAtlasIndexes[1] = {herorects_battlePose};
asset_addAnimation(assetManager, texlist_hero, animlist_hero_battlePose,
asset_addAnimation(assetManager, arena, texlist_hero, animlist_hero_battlePose,
battleStanceAnimAtlasIndexes, numRects, duration);
// Battle tackle animation
@ -194,8 +200,11 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
numRects = 3;
i32 tackleAnimAtlasIndexes[3] = {herorects_castA, herorects_castB,
herorects_castC};
asset_addAnimation(assetManager, texlist_hero, animlist_hero_tackle,
asset_addAnimation(assetManager, arena, texlist_hero, animlist_hero_tackle,
tackleAnimAtlasIndexes, numRects, duration);
#ifdef DENGINE_DEBUG
DEBUG_LOG("Animations created");
#endif
state->state = state_active;
@ -204,6 +213,9 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
/* Init renderer */
rendererInit(state, windowSize);
#ifdef DENGINE_DEBUG
DEBUG_LOG("Renderer initialised");
#endif
/* Init world */
const i32 targetWorldWidth = 100 * METERS_TO_PIXEL;
@ -215,8 +227,9 @@ 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->entityIdInBattle = PLATFORM_MEM_ALLOC(world->maxEntities, i32);
world->entities = PLATFORM_MEM_ALLOC(arena, world->maxEntities, Entity);
world->entityIdInBattle =
PLATFORM_MEM_ALLOC(arena, world->maxEntities, i32);
world->numEntitiesInBattle = 0;
world->texType = texlist_terrain;
world->bounds =
@ -243,8 +256,8 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
enum Direction dir = direction_null;
Texture *tex = asset_getTexture(assetManager, world->texType);
b32 collides = FALSE;
Entity *tile =
addEntity(world, pos, size, type, dir, tex, collides);
Entity *tile = addEntity(arena, world, pos, size, type, dir,
tex, collides);
addAnim(assetManager, animlist_terrain, tile);
tile->currAnimId = animlist_terrain;
@ -265,7 +278,7 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
enum Direction dir = direction_east;
Texture *tex = asset_getTexture(assetManager, texlist_hero);
b32 collides = TRUE;
Entity *hero = addEntity(world, pos, size, type, dir, tex, collides);
Entity *hero = addEntity(arena, world, pos, size, type, dir, tex, collides);
/* Populate hero animation references */
addAnim(assetManager, animlist_hero_idle, hero);
@ -282,7 +295,7 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
dir = direction_null;
tex = hero->tex;
collides = FALSE;
Entity *npc = addEntity(world, pos, size, type, dir, tex, collides);
Entity *npc = addEntity(arena, world, pos, size, type, dir, tex, collides);
/* Populate npc animation references */
addAnim(assetManager, animlist_hero_wave, npc);
@ -296,7 +309,7 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
dir = direction_west;
tex = hero->tex;
collides = TRUE;
Entity *mob = addEntity(world, pos, size, type, dir, tex, collides);
Entity *mob = addEntity(arena, world, pos, size, type, dir, tex, collides);
/* Populate mob animation references */
addAnim(assetManager, animlist_hero_idle, mob);
@ -304,6 +317,11 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
addAnim(assetManager, animlist_hero_battlePose, mob);
addAnim(assetManager, animlist_hero_tackle, mob);
hero->currAnimId = animlist_hero_idle;
#ifdef DENGINE_DEBUG
DEBUG_LOG("World populated");
#endif
}
INTERNAL inline void setActiveEntityAnim(Entity *entity,
@ -519,8 +537,14 @@ 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)
{
@ -549,6 +573,10 @@ INTERNAL void beginAttack(Entity *attacker)
// 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:
@ -602,20 +630,69 @@ INTERNAL v4 createCameraBounds(World *world, v2 size)
#define ENTITY_IN_BATTLE TRUE
#define ENTITY_NOT_IN_BATTLE FALSE
INTERNAL i32 findBestEntityToAttack(World *world, Entity attacker)
{
#ifdef DENGINE_DEBUG
ASSERT(world);
ASSERT(attacker.type == entitytype_hero || attacker.type == entitytype_mob);
#endif
i32 result = 0;
// TODO(doyle): If attacker is mob- retrieve hero entity id directly, change
// when we have party members!
if (attacker.type == entitytype_mob)
{
Entity hero = world->entities[world->heroIndex];
result = hero.id;
return result;
}
/* Attacker is hero */
Entity hero = attacker;
for (i32 i = 0; i < world->maxEntities; i++)
{
Entity targetEntity = world->entities[i];
if (hero.id == targetEntity.id) continue;
if (world->entityIdInBattle[targetEntity.id] == ENTITY_IN_BATTLE)
{
result = targetEntity.id;
return result;
}
}
// NOTE(doyle): Not all "battling" entities have been enumerated yet in the
// update loop, guard against when using function
return ENTITY_NULL_ID;
}
INTERNAL inline void updateWorldBattleEntities(World *world, Entity *entity,
b32 isInBattle)
{
#ifdef DENGINE_DEBUG
ASSERT(isInBattle == ENTITY_IN_BATTLE ||
isInBattle == ENTITY_NOT_IN_BATTLE);
ASSERT(world && entity);
#endif
world->entityIdInBattle[entity->id] = isInBattle;
if (isInBattle)
world->numEntitiesInBattle++;
else
world->numEntitiesInBattle--;
#ifdef DENGINE_DEBUG
ASSERT(world->numEntitiesInBattle >= 0);
#endif
}
INTERNAL void entityStateSwitch(World *world, Entity *entity,
enum EntityState newState)
{
#ifdef DENGINE_DEBUG
ASSERT(world && entity)
ASSERT(entity->type == entitytype_mob || entity->type == entitytype_hero)
#endif
if (entity->state == newState) return;
switch(entity->state)
@ -625,11 +702,16 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
{
case entitystate_battle:
updateWorldBattleEntities(world, entity, ENTITY_IN_BATTLE);
entity->stats->entityIdToAttack =
findBestEntityToAttack(world, *entity);
break;
case entitystate_attack:
case entitystate_dead:
break;
default:
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
}
break;
case entitystate_battle:
@ -637,36 +719,52 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
{
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);
entity->stats->actionTimer = entity->stats->actionRate;
entity->stats->queuedAttack = entityattack_invalid;
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
break;
case entitystate_attack:
case entitystate_dead:
break;
return;
default:
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
}
break;
case entitystate_attack:
switch (newState)
{
case entitystate_idle:
case entitystate_battle:
case entitystate_idle:
return;
case entitystate_dead:
break;
default:
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
}
break;
case entitystate_dead:
switch (newState)
{
case entitystate_idle:
case entitystate_battle:
case entitystate_attack:
break;
default:
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
}
break;
default:
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
}
entity->state = newState;
@ -674,11 +772,10 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
INTERNAL void updateEntityAndRender(Renderer *renderer, World *world, f32 dt)
{
Entity *hero = &world->entities[world->heroIndex];
for (i32 i = 0; i < world->freeEntityIndex; i++)
{
Entity *const entity = &world->entities[i];
Entity *hero = &world->entities[world->heroIndex];
Entity *const entity = &world->entities[i];
switch(entity->type)
{
case entitytype_mob:
@ -696,7 +793,7 @@ INTERNAL void updateEntityAndRender(Renderer *renderer, World *world, f32 dt)
entityStateSwitch(world, entity, newState);
}
// NOTE(doyle): Allow fall through to entitytype_hero here
// NOTE(doyle): Let entitytype_mob fall through to entitytype_hero here
case entitytype_hero:
{
if (entity->state == entitystate_battle ||
@ -728,8 +825,11 @@ INTERNAL void updateEntityAndRender(Renderer *renderer, World *world, f32 dt)
}
else
{
#ifdef DENGINE_DEBUG
ASSERT(INVALID_CODE_PATH);
#endif
// TODO(doyle): Generalise for all entities
hero->stats->entityIdToAttack = -1;
hero->stats->entityIdToAttack = ENTITY_NULL_ID;
hero->state = entitystate_idle;
entity->state = entitystate_dead;
}
@ -740,38 +840,37 @@ INTERNAL void updateEntityAndRender(Renderer *renderer, World *world, f32 dt)
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 = i;
}
else
{
if (hero->state == entitystate_battle)
{
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;
}
updateEntityAnim(entity, dt);
/* Calculate region to render */
v4 cameraBounds = createCameraBounds(world, renderer->size);
renderer_entity(renderer, cameraBounds, entity, 0, V4(1, 1, 1, 1));
}
if (world->numEntitiesInBattle > 0)
{
// NOTE(doyle): If battle entities is 1 then only the hero is left
if (hero->state == entitystate_battle &&
world->numEntitiesInBattle == 1)
entityStateSwitch(world, hero, entitystate_idle);
else
entityStateSwitch(world, hero, entitystate_battle);
}
else
{
if (hero->state == entitystate_battle)
{
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;
}
}
LOCAL_PERSIST b32 count = 0;
void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt)
{
/* Update */
@ -802,8 +901,8 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt)
f32 strLenInPixels =
CAST(f32)(font->maxSize.w * common_strlen(heroAvatarStr));
v2 strPos = V2(heroAvatarP.x, heroAvatarP.y - (0.5f * heroAvatarSize.h));
renderer_staticString(&state->renderer, font, heroAvatarStr, strPos, 0,
V4(0, 0, 1, 1));
renderer_staticString(&state->renderer, &state->arena, font, heroAvatarStr,
strPos, 0, V4(0, 0, 1, 1));
#ifdef DENGINE_DEBUG
debug_drawUi(state, dt);

View File

@ -78,14 +78,15 @@ int main()
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glCullFace(GL_BACK);
#ifdef DENGINE_DEBUG
debug_init();
#endif
GameState worldTraveller = {0};
worldTraveller_gameInit(&worldTraveller,
V2i(frameBufferWidth, frameBufferHeight));
#ifdef DENGINE_DEBUG
debug_init(&worldTraveller.arena, V2i(windowWidth, windowHeight),
worldTraveller.assetManager.font);
#endif
glfwSetWindowUserPointer(window, CAST(void *)(&worldTraveller));
f32 startTime = CAST(f32)(glfwGetTime());

View File

@ -2,8 +2,10 @@
#define DENGINE_ASSET_MANAGER_H
#include "Dengine/Assets.h"
#include "Dengine/MemoryArena.h"
#include "Dengine/Shader.h"
#include "Dengine/Texture.h"
#include "Dengine/MemoryArena.h"
#define MAX_TEXTURE_SIZE 1024
@ -29,12 +31,13 @@ const i32 asset_loadTextureImage(AssetManager *assetManager,
const char *const path,
const enum TexList type);
const i32 asset_loadShaderFiles(AssetManager *assetManager,
const i32 asset_loadShaderFiles(AssetManager *assetManager, MemoryArena *arena,
const char *const vertexPath,
const char *const fragmentPath,
const enum ShaderList type);
const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath);
const i32 asset_loadTTFont(AssetManager *assetManager, MemoryArena *arena,
const char *filePath);
inline i32 asset_getVFontSpacing(FontMetrics metrics)
{
@ -43,8 +46,8 @@ inline i32 asset_getVFontSpacing(FontMetrics metrics)
return result;
}
void asset_addAnimation(AssetManager *assetManager, i32 texId,
i32 animId, i32 *atlasIndexes, i32 numFrames,
f32 frameDuration);
void asset_addAnimation(AssetManager *assetManager, MemoryArena *arena,
i32 texId, i32 animId, i32 *atlasIndexes, i32 numFrames,
f32 frameDuration);
#endif

View File

@ -28,5 +28,6 @@ typedef double f64;
i32 common_strlen(const char *const string);
i32 common_strcmp(const char *a, const char *b);
void common_itoa(i32 value, char *buf, i32 bufSize);
#endif

View File

@ -9,7 +9,7 @@
#include "WorldTraveller/WorldTraveller.h"
#define INVALID_CODE_PATH TRUE
#define INVALID_CODE_PATH 0
enum DebugCallCount
{
debugcallcount_drawArrays,
@ -18,19 +18,22 @@ enum DebugCallCount
typedef struct DebugState
{
i32 totalMemoryAllocated;
Font font;
i32 *callCount;
f32 stringLineGap;
/* Debug strings rendered in top left corner */
char debugStrings[256][64];
char debugStrings[64][128];
i32 numDebugStrings;
f32 stringUpdateTimer;
f32 stringUpdateRate;
v2 initialStringP;
v2 currStringP;
v2 initialStringPos;
v2 stringPos;
f32 stringLineGap;
/* Debug gui console log */
char console[20][128];
i32 consoleIndex;
v2 initialConsoleP;
} DebugState;
extern DebugState GLOBAL_debug;
@ -97,14 +100,16 @@ inline char *debug_entityattack_string(i32 val)
}
void debug_init();
void debug_init(MemoryArena *arena, v2 windowSize, Font font);
#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);
#define DEBUG_LOG(string) debug_consoleLog(string, __FILE__, __LINE__);
void debug_consoleLog(char *string, char *file, int lineNum);
#endif

View File

@ -65,7 +65,7 @@ typedef struct EntityAnim_
typedef struct Entity
{
u32 id;
i32 id;
v2 pos; // Position
v2 dPos; // Velocity

View File

@ -0,0 +1,9 @@
#ifndef DENGINE_MEMORY_ARENA_H
#define DENGINE_MEMORY_ARENA_H
typedef struct MemoryArena
{
i32 bytesAllocated;
} MemoryArena;
#endif

View File

@ -2,6 +2,7 @@
#define DENGINE_PLATFORM_H
#include "Dengine/Common.h"
#include "Dengine/MemoryArena.h"
typedef struct PlatformFileRead
{
@ -10,17 +11,17 @@ typedef struct PlatformFileRead
} PlatformFileRead;
// TODO(doyle): Create own custom memory allocator
#define PLATFORM_MEM_FREE(ptr, bytes) platform_memoryFree(CAST(void *) ptr, bytes)
#define PLATFORM_MEM_FREE(arena, ptr, bytes) platform_memoryFree(arena, CAST(void *) ptr, bytes)
// TODO(doyle): numBytes in mem free is temporary until we create custom
// allocator since we haven't put in a system to track memory usage per
// allocation
void platform_memoryFree(void *data, i32 numBytes);
void platform_memoryFree(MemoryArena *arena, void *data, i32 numBytes);
#define PLATFORM_MEM_ALLOC(num, type) CAST(type *) platform_memoryAlloc(num * sizeof(type))
void *platform_memoryAlloc(i32 numBytes);
#define PLATFORM_MEM_ALLOC(arena, num, type) CAST(type *) platform_memoryAlloc(arena, num * sizeof(type))
void *platform_memoryAlloc(MemoryArena *arena, i32 numBytes);
void platform_closeFileRead(PlatformFileRead *file);
i32 platform_readFileToBuffer(const char *const filePath,
void platform_closeFileRead(MemoryArena *arena, PlatformFileRead *file);
i32 platform_readFileToBuffer(MemoryArena *arena, const char *const filePath,
PlatformFileRead *file);
#endif

View File

@ -6,6 +6,7 @@
#include "Dengine/Entity.h"
#include "Dengine/Math.h"
#include "Dengine/Shader.h"
#include "Dengine/MemoryArena.h"
typedef struct Renderer
{
@ -36,18 +37,17 @@ inline void renderer_staticRect(Renderer *const renderer, v2 pos, v2 size,
color);
}
void renderer_string(Renderer *const renderer, MemoryArena *arena,
v4 cameraBounds, Font *const font,
const char *const string, v2 pos, f32 rotate, v4 color);
void renderer_string(Renderer *const renderer, v4 cameraBounds,
Font *const font, const char *const string, v2 pos,
f32 rotate, v4 color);
inline void renderer_staticString(Renderer *const renderer, Font *const font,
const char *const string, v2 pos, f32 rotate,
v4 color)
inline void renderer_staticString(Renderer *const renderer, MemoryArena *arena,
Font *const font, const char *const string,
v2 pos, f32 rotate, v4 color)
{
v4 staticCameraBounds = math_getRect(V2(0, 0), renderer->size);
renderer_string(renderer, staticCameraBounds, font, string, pos, rotate,
color);
renderer_string(renderer, arena, staticCameraBounds, font, string, pos,
rotate, color);
}
void renderer_entity(Renderer *renderer, v4 cameraBounds, Entity *entity,

View File

@ -5,6 +5,7 @@
#include "Dengine/Common.h"
#include "Dengine/Entity.h"
#include "Dengine/Math.h"
#include "Dengine/MemoryArena.h"
#include "Dengine/Renderer.h"
#define NUM_KEYS 1024
@ -41,6 +42,7 @@ typedef struct GameState
i32 tileSize;
AssetManager assetManager;
MemoryArena arena;
} GameState;
void worldTraveller_gameInit(GameState *state, v2 windowSize);