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:
Doyle Thai 2016-08-17 20:37:56 +10:00
parent e90b31de55
commit c33b37b0ea
4 changed files with 176 additions and 79 deletions

View File

@ -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));
}

View File

@ -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,

View File

@ -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

View File

@ -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,