Render battle damage on screen, draft implementation
Crudely done to get an understanding of the kind of storage needed to store UI on screen that may have a limited life time.
This commit is contained in:
parent
e90b31de55
commit
c33b37b0ea
24
src/Common.c
24
src/Common.c
@ -21,6 +21,27 @@ i32 common_strcmp(const char *a, const char *b)
|
||||
return ((*a < *b) ? -1 : 1);
|
||||
}
|
||||
|
||||
char *common_memset(char *const ptr, const i32 value, const i32 numBytes)
|
||||
{
|
||||
for (i32 i = 0; i < numBytes; i++)
|
||||
ptr[i] = value;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
INTERNAL void reverseString(char *const buf, const i32 bufSize)
|
||||
{
|
||||
if (!buf || bufSize == 0 || bufSize == 1) return;
|
||||
i32 mid = bufSize / 2;
|
||||
|
||||
for (i32 i = 0; i < mid; i++)
|
||||
{
|
||||
char tmp = buf[i];
|
||||
buf[i] = buf[(bufSize-1) - i];
|
||||
buf[(bufSize-1) - i] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void common_itoa(i32 value, char *buf, i32 bufSize)
|
||||
{
|
||||
if (!buf || bufSize == 0) return;
|
||||
@ -40,4 +61,7 @@ void common_itoa(i32 value, char *buf, i32 bufSize)
|
||||
buf[charIndex++] = rem + '0';
|
||||
val /= 10;
|
||||
}
|
||||
|
||||
// NOTE(doyle): The actual string length may differ from the bufSize
|
||||
reverseString(buf, common_strlen(buf));
|
||||
}
|
||||
|
@ -78,34 +78,17 @@ INTERNAL void rendererInit(GameState *state, v2 windowSize)
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
GL_CHECK_ERROR();
|
||||
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Renderer initialised");
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO(doyle): Remove and implement own random generator!
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
void worldTraveller_gameInit(GameState *state, v2 windowSize)
|
||||
INTERNAL void assetInit(GameState *state)
|
||||
{
|
||||
AssetManager *assetManager = &state->assetManager;
|
||||
MemoryArena *arena = &state->arena;
|
||||
|
||||
/*
|
||||
************************
|
||||
* INITIALISE GAME AUDIO
|
||||
************************
|
||||
*/
|
||||
i32 result = audio_init(&state->audioManager);
|
||||
if (result)
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
*******************
|
||||
* INITIALISE ASSETS
|
||||
*******************
|
||||
*/
|
||||
/* Create empty 1x1 4bpp black texture */
|
||||
u32 bitmap = (0xFF << 24) | (0xFF << 16) | (0xFF << 8) | (0xFF << 0);
|
||||
Texture emptyTex = texture_gen(1, 1, 4, CAST(u8 *)(&bitmap));
|
||||
@ -144,7 +127,7 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
|
||||
"data/shaders/sprite.frag.glsl",
|
||||
shaderlist_sprite);
|
||||
|
||||
result =
|
||||
i32 result =
|
||||
asset_loadTTFont(assetManager, arena, "C:/Windows/Fonts/Arialbd.ttf");
|
||||
if (result) DEBUG_LOG("Font loading failed");
|
||||
|
||||
@ -217,16 +200,12 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Sound assets initialised");
|
||||
#endif
|
||||
}
|
||||
|
||||
state->state = state_active;
|
||||
state->currWorldIndex = 0;
|
||||
state->tileSize = 64;
|
||||
|
||||
/* Init renderer */
|
||||
rendererInit(state, windowSize);
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("Renderer initialised");
|
||||
#endif
|
||||
INTERNAL void entityInit(GameState *state, v2 windowSize)
|
||||
{
|
||||
AssetManager *assetManager = &state->assetManager;
|
||||
MemoryArena *arena = &state->arena;
|
||||
|
||||
/* Init world */
|
||||
const i32 targetWorldWidth = 100 * METERS_TO_PIXEL;
|
||||
@ -235,8 +214,9 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
|
||||
v2 worldDimensionInTiles = V2i(targetWorldWidth / state->tileSize,
|
||||
targetWorldHeight / state->tileSize);
|
||||
#else
|
||||
v2 worldDimensionInTiles = V2i(CAST(i32) (windowSize.w / state->tileSize) * 2,
|
||||
CAST(i32) windowSize.h / state->tileSize);
|
||||
v2 worldDimensionInTiles =
|
||||
V2i(CAST(i32)(windowSize.w / state->tileSize) * 2,
|
||||
CAST(i32) windowSize.h / state->tileSize);
|
||||
#endif
|
||||
|
||||
for (i32 i = 0; i < ARRAY_COUNT(state->world); i++)
|
||||
@ -338,11 +318,35 @@ void worldTraveller_gameInit(GameState *state, v2 windowSize)
|
||||
pos = V2(renderer->size.w - (renderer->size.w / 3.0f),
|
||||
CAST(f32) state->tileSize);
|
||||
entity_addGenericMob(arena, assetManager, world, pos);
|
||||
|
||||
#ifdef DENGINE_DEBUG
|
||||
DEBUG_LOG("World populated");
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO(doyle): Remove and implement own random generator!
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
void worldTraveller_gameInit(GameState *state, v2 windowSize)
|
||||
{
|
||||
i32 result = audio_init(&state->audioManager);
|
||||
if (result)
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
#endif
|
||||
}
|
||||
|
||||
state->state = state_active;
|
||||
state->currWorldIndex = 0;
|
||||
state->tileSize = 64;
|
||||
state->uiState.keyEntered = keycode_null;
|
||||
state->uiState.keyMod = keycode_null;
|
||||
state->uiState.keyChar = keycode_null;
|
||||
|
||||
assetInit(state);
|
||||
rendererInit(state, windowSize);
|
||||
entityInit(state, windowSize);
|
||||
|
||||
srand(CAST(u32)(time(NULL)));
|
||||
}
|
||||
@ -985,6 +989,7 @@ INTERNAL void sortWorldEntityList(World *world, i32 numDeadEntities)
|
||||
listHasChanged = FALSE;
|
||||
for (i32 i = 0; i < numUnsortedEntities-1; i++)
|
||||
{
|
||||
// TODO(doyle): Better sort algorithm
|
||||
Entity *entityA = &world->entities[i];
|
||||
Entity *entityB = &world->entities[i+1];
|
||||
if (entityA->type == entitytype_null ||
|
||||
@ -1002,6 +1007,20 @@ INTERNAL void sortWorldEntityList(World *world, i32 numDeadEntities)
|
||||
world->freeEntityIndex -= numDeadEntities;
|
||||
}
|
||||
|
||||
typedef struct DamageDisplay
|
||||
{
|
||||
char damageStr[12];
|
||||
v2 pos;
|
||||
f32 lifetime;
|
||||
} DamageDisplay;
|
||||
|
||||
typedef struct BattleState
|
||||
{
|
||||
DamageDisplay damageDisplay[128];
|
||||
} BattleState;
|
||||
|
||||
BattleState battleState = {0};
|
||||
|
||||
void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
{
|
||||
if (dt >= 1.0f) dt = 1.0f;
|
||||
@ -1172,53 +1191,79 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
Event event = eventQueue.queue[i];
|
||||
switch(event.type)
|
||||
{
|
||||
case eventtype_end_attack:
|
||||
case eventtype_end_attack:
|
||||
{
|
||||
if (!event.data) continue;
|
||||
|
||||
AttackSpec *attackSpec = (CAST(AttackSpec *) event.data);
|
||||
Entity *attacker = attackSpec->attacker;
|
||||
Entity *defender = attackSpec->defender;
|
||||
|
||||
audio_playVorbis(arena, audioManager, attacker->audioRenderer,
|
||||
asset_getVorbis(assetManager, audiolist_tackle),
|
||||
1);
|
||||
|
||||
/* Get first free string position and store the damage str data */
|
||||
for (i32 i = 0; i < ARRAY_COUNT(battleState.damageDisplay); i++)
|
||||
{
|
||||
if (!event.data) continue;
|
||||
if (battleState.damageDisplay[i].lifetime <= 0.0f)
|
||||
{
|
||||
common_memset(
|
||||
battleState.damageDisplay[i].damageStr, 0,
|
||||
ARRAY_COUNT(battleState.damageDisplay[i].damageStr));
|
||||
|
||||
AttackSpec *attackSpec = (CAST(AttackSpec *)event.data);
|
||||
Entity *attacker = attackSpec->attacker;
|
||||
Entity *defender = attackSpec->defender;
|
||||
battleState.damageDisplay[i].lifetime = 1.0f;
|
||||
battleState.damageDisplay[i].pos = defender->pos;
|
||||
common_itoa(
|
||||
attackSpec->damage,
|
||||
battleState.damageDisplay[i].damageStr,
|
||||
ARRAY_COUNT(battleState.damageDisplay[i].damageStr));
|
||||
|
||||
audio_playVorbis(
|
||||
arena, audioManager, attacker->audioRenderer,
|
||||
asset_getVorbis(assetManager, audiolist_tackle), 1);
|
||||
|
||||
char damageStr[12];
|
||||
common_itoa(attackSpec->damage, damageStr,
|
||||
ARRAY_COUNT(damageStr));
|
||||
|
||||
// TODO(doyle): Incorporate UI into entity list or it's own list
|
||||
// with a rendering lifetime value
|
||||
renderer_string(renderer, &state->arena,
|
||||
camera, font,
|
||||
damageStr, defender->pos, V2(0, 0),
|
||||
0, V4(1, 1, 1, 1));
|
||||
|
||||
PLATFORM_MEM_FREE(&state->arena, attackSpec,
|
||||
sizeof(AttackSpec));
|
||||
|
||||
break;
|
||||
}
|
||||
// NOTE(doyle): We delete dead entities at the end of the update
|
||||
// loop incase a later indexed entity deletes an earlier indexed
|
||||
// entity, the entity will exist for an additional frame
|
||||
case eventtype_entity_died:
|
||||
{
|
||||
DEBUG_LOG("Entity died: being deleted");
|
||||
if (!event.data) continue;
|
||||
|
||||
Entity *entity = (CAST(Entity *) event.data);
|
||||
audio_stopVorbis(&state->arena, audioManager,
|
||||
entity->audioRenderer);
|
||||
entity_clearData(&state->arena, world, entity);
|
||||
numDeadEntities++;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PLATFORM_MEM_FREE(&state->arena, attackSpec, sizeof(AttackSpec));
|
||||
break;
|
||||
}
|
||||
// NOTE(doyle): We delete dead entities at the end of the update
|
||||
// loop incase a later indexed entity deletes an earlier indexed
|
||||
// entity, the entity will exist for an additional frame
|
||||
case eventtype_entity_died:
|
||||
{
|
||||
DEBUG_LOG("Entity died: being deleted");
|
||||
if (!event.data) continue;
|
||||
|
||||
Entity *entity = (CAST(Entity *) event.data);
|
||||
audio_stopVorbis(&state->arena, audioManager,
|
||||
entity->audioRenderer);
|
||||
entity_clearData(&state->arena, world, entity);
|
||||
numDeadEntities++;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Render all damage strings */
|
||||
for (i32 i = 0; i < ARRAY_COUNT(battleState.damageDisplay); i++)
|
||||
{
|
||||
if (battleState.damageDisplay[i].lifetime > 0.0f)
|
||||
{
|
||||
battleState.damageDisplay[i].lifetime -= dt;
|
||||
|
||||
char *damageString = battleState.damageDisplay[i].damageStr;
|
||||
v2 damagePos = battleState.damageDisplay[i].pos;
|
||||
f32 lifetime = battleState.damageDisplay[i].lifetime;
|
||||
|
||||
// TODO(doyle): Incorporate UI into entity list or it's own list
|
||||
// with a rendering lifetime value
|
||||
renderer_string(renderer, &state->arena, camera, font,
|
||||
damageString, damagePos, V2(0, 0), 0,
|
||||
V4(1, 1, 1, lifetime));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1251,6 +1296,13 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
// INIT IMGUI
|
||||
state->uiState.hotItem = 0;
|
||||
|
||||
#if 0
|
||||
UiItem damageString = {0};
|
||||
damageString.id = 6;
|
||||
damageString.rect = {V2(500, 500), V2(font->maxSize.w * 30, font->maxSize.h)};
|
||||
damageString.type = uitype_string;
|
||||
#endif
|
||||
|
||||
/* Draw ui */
|
||||
Rect buttonRectA = {V2(300, 500), V2(100, 50)};
|
||||
userInterface_button(&state->uiState, assetManager, renderer, state->input,
|
||||
|
@ -30,6 +30,9 @@ typedef double f64;
|
||||
|
||||
i32 common_strlen(const char *const string);
|
||||
i32 common_strcmp(const char *a, const char *b);
|
||||
char *common_memset(char *const ptr, const i32 value, const i32 numBytes);
|
||||
|
||||
// Max buffer size should be 11 for 32 bit integers
|
||||
void common_itoa(i32 value, char *buf, i32 bufSize);
|
||||
|
||||
#endif
|
||||
|
@ -11,17 +11,35 @@ typedef struct Font Font;
|
||||
typedef struct MemoryArena MemoryArena;
|
||||
typedef struct Renderer Renderer;
|
||||
|
||||
enum UiType
|
||||
{
|
||||
uitype_button,
|
||||
uitype_scrollbar,
|
||||
uitype_textfield,
|
||||
uitype_string,
|
||||
uitype_count,
|
||||
};
|
||||
|
||||
typedef struct UiItem
|
||||
{
|
||||
i32 id;
|
||||
Rect rect;
|
||||
enum UiType type;
|
||||
} UiItem;
|
||||
|
||||
typedef struct UiState
|
||||
{
|
||||
UiItem uiList[128];
|
||||
i32 numItems;
|
||||
|
||||
i32 hotItem;
|
||||
i32 activeItem;
|
||||
i32 lastWidget;
|
||||
|
||||
i32 kbdItem;
|
||||
enum KeyCode keyEntered;
|
||||
enum KeyCode keyMod;
|
||||
enum KeyCode keyChar;
|
||||
|
||||
i32 lastWidget;
|
||||
} UiState;
|
||||
|
||||
i32 userInterface_button(UiState *const uiState,
|
||||
|
Loading…
Reference in New Issue
Block a user