Add better support for multi-entity battles

This commit is contained in:
Doyle Thai 2016-07-20 21:42:45 +10:00
parent 906d66a12a
commit 2707c46df1
3 changed files with 146 additions and 75 deletions

View File

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

View File

@ -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,19 +833,61 @@ INTERNAL void endAttack(World *world, Entity *attacker)
} }
/* Compute attack damage */ /* Compute attack damage */
// TODO(doyle): Use attacker stats in battle equations Entity *defender = NULL;
Entity *defender = &world->entities[attacker->stats->entityIdToAttack];
if (attacker->type == entitytype_hero)
defender->stats->health -= 50;
else
defender->stats->health--;
if (defender->stats->health <= 0) // 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 #ifdef DENGINE_DEBUG
DEBUG_LOG("Entity has died"); ASSERT(INVALID_CODE_PATH);
#endif #endif
entityStateSwitch(world, defender, entitystate_dead); }
}
} 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
if (attacker->type == entitytype_hero)
defender->stats->health -= 50;
else
{
//defender->stats->health--;
}
if (defender->stats->health <= 0)
{
#ifdef DENGINE_DEBUG
DEBUG_LOG("Entity has died");
#endif
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;
} }

View File

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