Add better support for multi-entity battles
This commit is contained in:
parent
906d66a12a
commit
2707c46df1
116
src/Debug.c
116
src/Debug.c
@ -30,8 +30,64 @@ void debug_init(MemoryArena *arena, v2 windowSize, Font font)
|
|||||||
GLOBAL_debug.initialConsoleP = V2(consoleXPos, consoleYPos);
|
GLOBAL_debug.initialConsoleP = V2(consoleXPos, consoleYPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
void debug_pushString(char *formatString, void *data, char *dataType)
|
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)
|
if (GLOBAL_debug.stringUpdateTimer <= 0)
|
||||||
{
|
{
|
||||||
i32 numDebugStrings = GLOBAL_debug.numDebugStrings;
|
i32 numDebugStrings = GLOBAL_debug.numDebugStrings;
|
||||||
@ -79,8 +135,6 @@ void debug_pushString(char *formatString, void *data, char *dataType)
|
|||||||
ASSERT(INVALID_CODE_PATH);
|
ASSERT(INVALID_CODE_PATH);
|
||||||
}
|
}
|
||||||
GLOBAL_debug.numDebugStrings++;
|
GLOBAL_debug.numDebugStrings++;
|
||||||
ASSERT(GLOBAL_debug.numDebugStrings <
|
|
||||||
ARRAY_COUNT(GLOBAL_debug.debugStrings[0]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,6 +232,8 @@ void debug_drawUi(GameState *state, f32 dt)
|
|||||||
for (i32 i = 0; i < world->freeEntityIndex; i++)
|
for (i32 i = 0; i < world->freeEntityIndex; i++)
|
||||||
{
|
{
|
||||||
Entity *const entity = &world->entities[i];
|
Entity *const entity = &world->entities[i];
|
||||||
|
if (entity->state == entitystate_dead) continue;
|
||||||
|
|
||||||
/* 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;
|
||||||
@ -242,6 +298,13 @@ void debug_drawUi(GameState *state, f32 dt)
|
|||||||
entity->stats->actionTimer, entity->stats->actionRate);
|
entity->stats->actionTimer, entity->stats->actionRate);
|
||||||
renderer_string(&state->renderer, &state->arena, cameraBounds,
|
renderer_string(&state->renderer, &state->arena, cameraBounds,
|
||||||
font, entityTimer, strPos, 0, color);
|
font, entityTimer, strPos, 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, 0, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
strPos.y -= GLOBAL_debug.stringLineGap;
|
strPos.y -= GLOBAL_debug.stringLineGap;
|
||||||
@ -290,52 +353,3 @@ void debug_drawUi(GameState *state, f32 dt)
|
|||||||
renderConsole(&state->renderer, &state->arena);
|
renderConsole(&state->renderer, &state->arena);
|
||||||
debug_clearCallCounter();
|
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;
|
|
||||||
}
|
|
||||||
|
@ -685,8 +685,20 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
|
|||||||
entity->stats->entityIdToAttack =
|
entity->stats->entityIdToAttack =
|
||||||
findBestEntityToAttack(world, *entity);
|
findBestEntityToAttack(world, *entity);
|
||||||
break;
|
break;
|
||||||
case entitystate_attack:
|
|
||||||
|
// TODO(doyle): Corner case- if move out of range and entity has
|
||||||
|
// switched to idle mode, we reach the attacker entity and they continue
|
||||||
|
// attacking it since there's no check before attack if entity is idle
|
||||||
|
// or not (i.e. has moved out of frame last frame).
|
||||||
case entitystate_dead:
|
case entitystate_dead:
|
||||||
|
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
|
||||||
|
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:
|
||||||
default:
|
default:
|
||||||
#ifdef DENGINE_DEBUG
|
#ifdef DENGINE_DEBUG
|
||||||
ASSERT(INVALID_CODE_PATH);
|
ASSERT(INVALID_CODE_PATH);
|
||||||
@ -696,18 +708,12 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
|
|||||||
case entitystate_battle:
|
case entitystate_battle:
|
||||||
switch (newState)
|
switch (newState)
|
||||||
{
|
{
|
||||||
case entitystate_idle:
|
|
||||||
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
|
|
||||||
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_attack:
|
||||||
break;
|
break;
|
||||||
|
case entitystate_idle:
|
||||||
case entitystate_dead:
|
case entitystate_dead:
|
||||||
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
|
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
|
||||||
setActiveEntityAnim(entity, animlist_hero_wave);
|
setActiveEntityAnim(entity, animlist_hero_idle);
|
||||||
entity->stats->actionTimer = entity->stats->actionRate;
|
entity->stats->actionTimer = entity->stats->actionRate;
|
||||||
entity->stats->queuedAttack = entityattack_invalid;
|
entity->stats->queuedAttack = entityattack_invalid;
|
||||||
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
|
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
|
||||||
@ -726,9 +732,6 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
|
|||||||
entity->stats->busyDuration = 0;
|
entity->stats->busyDuration = 0;
|
||||||
setActiveEntityAnim(entity, animlist_hero_battlePose);
|
setActiveEntityAnim(entity, animlist_hero_battlePose);
|
||||||
break;
|
break;
|
||||||
case entitystate_idle:
|
|
||||||
return;
|
|
||||||
|
|
||||||
case entitystate_dead:
|
case entitystate_dead:
|
||||||
// TODO(doyle): Repeated logic with battle -> dead
|
// TODO(doyle): Repeated logic with battle -> dead
|
||||||
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
|
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
|
||||||
@ -737,6 +740,16 @@ INTERNAL void entityStateSwitch(World *world, Entity *entity,
|
|||||||
entity->stats->queuedAttack = entityattack_invalid;
|
entity->stats->queuedAttack = entityattack_invalid;
|
||||||
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
|
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// NOTE(doyle): Entity has been forced out of an attack (out of range)
|
||||||
|
case entitystate_idle:
|
||||||
|
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
|
||||||
|
setActiveEntityAnim(entity, animlist_hero_idle);
|
||||||
|
entity->stats->busyDuration = 0;
|
||||||
|
entity->stats->actionTimer = entity->stats->actionRate;
|
||||||
|
entity->stats->queuedAttack = entityattack_invalid;
|
||||||
|
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
#ifdef DENGINE_DEBUG
|
#ifdef DENGINE_DEBUG
|
||||||
ASSERT(INVALID_CODE_PATH);
|
ASSERT(INVALID_CODE_PATH);
|
||||||
@ -820,12 +833,51 @@ INTERNAL void endAttack(World *world, Entity *attacker)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Compute attack damage */
|
/* Compute attack damage */
|
||||||
|
Entity *defender = NULL;
|
||||||
|
|
||||||
|
// TODO(doyle): Implement faster lookup for entity id in entity table
|
||||||
|
do
|
||||||
|
{
|
||||||
|
/* Get target entity to attack */
|
||||||
|
for (i32 i = 0; i < world->maxEntities; i++)
|
||||||
|
{
|
||||||
|
i32 entityIdToAttack = attacker->stats->entityIdToAttack;
|
||||||
|
if (world->entities[i].id == entityIdToAttack)
|
||||||
|
{
|
||||||
|
defender = &world->entities[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If no longer exists, find next best */
|
||||||
|
if (!defender)
|
||||||
|
{
|
||||||
|
if (world->numEntitiesInBattle > 1)
|
||||||
|
{
|
||||||
|
attacker->stats->entityIdToAttack =
|
||||||
|
findBestEntityToAttack(world, *attacker);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef DENGINE_DEBUG
|
||||||
|
ASSERT(INVALID_CODE_PATH);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!defender);
|
||||||
|
|
||||||
|
// TODO(doyle): Very susceptible to bugs- ensure if defender is dead the
|
||||||
|
// attacker immediately locates a new target or exits battle mode. But this
|
||||||
|
// is necessary since it's possible that by the time the hero is ready to
|
||||||
|
// apply attack damage another person may of killed it (i.e. party member)
|
||||||
|
if (defender)
|
||||||
|
{
|
||||||
// TODO(doyle): Use attacker stats in battle equations
|
// TODO(doyle): Use attacker stats in battle equations
|
||||||
Entity *defender = &world->entities[attacker->stats->entityIdToAttack];
|
|
||||||
if (attacker->type == entitytype_hero)
|
if (attacker->type == entitytype_hero)
|
||||||
defender->stats->health -= 50;
|
defender->stats->health -= 50;
|
||||||
else
|
else
|
||||||
defender->stats->health--;
|
{
|
||||||
|
//defender->stats->health--;
|
||||||
|
}
|
||||||
|
|
||||||
if (defender->stats->health <= 0)
|
if (defender->stats->health <= 0)
|
||||||
{
|
{
|
||||||
@ -833,6 +885,9 @@ INTERNAL void endAttack(World *world, Entity *attacker)
|
|||||||
DEBUG_LOG("Entity has died");
|
DEBUG_LOG("Entity has died");
|
||||||
#endif
|
#endif
|
||||||
entityStateSwitch(world, defender, entitystate_dead);
|
entityStateSwitch(world, defender, entitystate_dead);
|
||||||
|
attacker->stats->entityIdToAttack =
|
||||||
|
findBestEntityToAttack(world, *attacker);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return attacker back to non-attacking state */
|
/* Return attacker back to non-attacking state */
|
||||||
@ -878,11 +933,13 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
|||||||
// TODO(doyle): Accumulate all dead entities and delete at the
|
// TODO(doyle): Accumulate all dead entities and delete at the
|
||||||
// end. Hence resort/organise entity array once, not every time
|
// end. Hence resort/organise entity array once, not every time
|
||||||
// an entity dies
|
// an entity dies
|
||||||
|
#if 0
|
||||||
i32 entityIndexInArray = i;
|
i32 entityIndexInArray = i;
|
||||||
deleteEntity(&state->arena, world, entityIndexInArray);
|
deleteEntity(&state->arena, world, entityIndexInArray);
|
||||||
|
|
||||||
// TODO(doyle): DeleteEntity moves elements down 1, so account for i
|
// TODO(doyle): DeleteEntity moves elements down 1, so account for i
|
||||||
i--;
|
i--;
|
||||||
|
#endif
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +102,9 @@ inline char *debug_entityattack_string(i32 val)
|
|||||||
|
|
||||||
void debug_init(MemoryArena *arena, v2 windowSize, Font font);
|
void debug_init(MemoryArena *arena, v2 windowSize, Font font);
|
||||||
|
|
||||||
|
#define DEBUG_LOG(string) debug_consoleLog(string, __FILE__, __LINE__);
|
||||||
|
void debug_consoleLog(char *string, char *file, int lineNum);
|
||||||
|
|
||||||
#define DEBUG_PUSH_STRING(string) debug_pushString(string, NULL, "char")
|
#define DEBUG_PUSH_STRING(string) debug_pushString(string, NULL, "char")
|
||||||
#define DEBUG_PUSH_VAR(formatString, data, type) \
|
#define DEBUG_PUSH_VAR(formatString, data, type) \
|
||||||
debug_pushString(formatString, CAST(void *)&data, type)
|
debug_pushString(formatString, CAST(void *)&data, type)
|
||||||
@ -109,7 +112,4 @@ void debug_pushString(char *formatString, void *data, char *dataType);
|
|||||||
|
|
||||||
void debug_drawUi(GameState *state, 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
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user