Add support for more attack-animations

Exploratory code to determine how to architect the engine for increasing
requirements in game play.
This commit is contained in:
Doyle Thai 2016-09-14 01:34:57 +10:00
parent fc6f6e086f
commit 5c4f493979
5 changed files with 365 additions and 94 deletions

View File

@ -62,9 +62,6 @@ inline char *debug_entityattack_string(i32 val)
char *string;
switch(val)
{
case entityattack_tackle:
string = "EntityAttack_Tackle";
break;
case entityattack_count:
string = "EntityAttack_Count (Error!)";
break;

View File

@ -3,12 +3,14 @@
#include "Dengine/Platform.h"
#include "Dengine/WorldTraveller.h"
void entity_setActiveAnim(Entity *const entity, const char *const animName)
void entity_setActiveAnim(EventQueue *eventQueue, Entity *const entity,
const char *const animName)
{
/* Reset current anim data */
EntityAnim *currEntityAnim = &entity->animList[entity->currAnimId];
currEntityAnim->currDuration = currEntityAnim->anim->frameDuration;
currEntityAnim->currFrame = 0;
currEntityAnim->timesCompleted = 0;
/* Set entity active animation */
for (i32 i = 0; i < ARRAY_COUNT(entity->animList); i++)
@ -24,6 +26,9 @@ void entity_setActiveAnim(Entity *const entity, const char *const animName)
newEntityAnim->currDuration =
newEntityAnim->anim->frameDuration;
newEntityAnim->currFrame = 0;
worldTraveller_registerEvent(eventQueue, eventtype_start_anim,
newEntityAnim);
return;
}
}
@ -32,7 +37,8 @@ void entity_setActiveAnim(Entity *const entity, const char *const animName)
DEBUG_LOG("Entity does not have access to desired anim");
}
void entity_updateAnim(Entity *const entity, const f32 dt)
void entity_updateAnim(EventQueue *eventQueue, Entity *const entity,
const f32 dt)
{
if (!entity->tex)
return;
@ -45,6 +51,13 @@ void entity_updateAnim(Entity *const entity, const f32 dt)
{
currEntityAnim->currFrame++;
currEntityAnim->currFrame = currEntityAnim->currFrame % anim->numFrames;
if (currEntityAnim->currFrame == 0)
{
worldTraveller_registerEvent(eventQueue, eventtype_end_anim,
currEntityAnim);
currEntityAnim->timesCompleted++;
}
currEntityAnim->currDuration = anim->frameDuration;
}
@ -138,6 +151,7 @@ Entity *const entity_add(MemoryArena *const arena, World *const world,
break;
}
case entitytype_projectile:
case entitytype_attackObject:
entity.stats = PLATFORM_MEM_ALLOC(arena, 1, EntityStats);
entity.stats->maxHealth = 100;
entity.stats->health = entity.stats->maxHealth;

View File

@ -13,37 +13,14 @@ enum State
state_win,
};
enum EventType
{
eventtype_null = 0,
eventtype_start_attack,
eventtype_end_attack,
eventtype_start_anim,
eventtype_end_anim,
eventtype_entity_died,
eventtype_count,
};
typedef struct Event
{
enum EventType type;
void *data;
} Event;
typedef struct EventQueue
{
Event queue[1024];
i32 numEvents;
} EventQueue;
INTERNAL Entity *getHeroEntity(World *world)
{
Entity *result = &world->entities[entity_getIndex(world, world->heroId)];
return result;
}
INTERNAL void addGenericMob(MemoryArena *arena, AssetManager *assetManager,
World *world, v2 pos)
INTERNAL void addGenericMob(EventQueue *eventQueue, MemoryArena *arena,
AssetManager *assetManager, World *world, v2 pos)
{
#ifdef DENGINE_DEBUG
DEBUG_LOG("Mob entity spawned");
@ -71,8 +48,17 @@ INTERNAL void addGenericMob(MemoryArena *arena, AssetManager *assetManager,
entity_addAnim(assetManager, mob, "claudeIdle");
entity_addAnim(assetManager, mob, "claudeRun");
entity_addAnim(assetManager, mob, "claudeBattleIdle");
entity_addAnim(assetManager, mob, "claudeAttack");
entity_setActiveAnim(mob, "claudeIdle");
entity_addAnim(assetManager, mob, "claudeAttackUp");
entity_addAnim(assetManager, mob, "claudeAttackDown");
entity_addAnim(assetManager, mob, "claudeAirSlash");
entity_addAnim(assetManager, mob, "claudeDragonHowl");
entity_addAnim(assetManager, mob, "claudeEnergySword");
entity_addAnim(assetManager, mob, "claudeRipperBlast");
entity_setActiveAnim(eventQueue, mob, "claudeIdle");
}
INTERNAL void rendererInit(GameState *state, v2 windowSize)
@ -244,6 +230,95 @@ INTERNAL void assetInit(GameState *state)
asset_addAnimation(assetManager, arena, "claudeAttack", claudeAtlas,
claudeAttack, numRects, duration);
char *claudeAttackDown[7] = {
"ClaudeSprite_Attack_Down_01",
"ClaudeSprite_Attack_Down_02",
"ClaudeSprite_Attack_Down_03",
"ClaudeSprite_Attack_Down_04",
"ClaudeSprite_Attack_Down_05",
"ClaudeSprite_Attack_Down_06",
"ClaudeSprite_Attack_Down_07",
};
numRects = ARRAY_COUNT(claudeAttackDown);
duration = 0.1f;
asset_addAnimation(assetManager, arena, "claudeAttackDown",
claudeAtlas, claudeAttackDown, numRects,
duration);
char *claudeAttackUp[3] = {
"ClaudeSprite_Attack_Up_01",
"ClaudeSprite_Attack_Up_02",
"ClaudeSprite_Attack_Up_03",
};
numRects = ARRAY_COUNT(claudeAttackUp);
duration = 0.2f;
asset_addAnimation(assetManager, arena, "claudeAttackUp",
claudeAtlas, claudeAttackUp, numRects,
duration);
char *claudeDragonHowl[3] = {
"ClaudeSprite_Attack_DragonHowl_01",
"ClaudeSprite_Attack_DragonHowl_02",
"ClaudeSprite_Attack_DragonHowl_03",
};
numRects = ARRAY_COUNT(claudeDragonHowl);
duration = 0.2f;
asset_addAnimation(assetManager, arena, "claudeDragonHowl",
claudeAtlas, claudeDragonHowl, numRects,
duration);
char *claudeDragonHowlVfx[7] = {
"ClaudeSprite_Attack_DragonHowl_Vfx_01",
"ClaudeSprite_Attack_DragonHowl_Vfx_02",
"ClaudeSprite_Attack_DragonHowl_Vfx_03",
"ClaudeSprite_Attack_DragonHowl_Vfx_04",
"ClaudeSprite_Attack_DragonHowl_Vfx_05",
"ClaudeSprite_Attack_DragonHowl_Vfx_06",
"ClaudeSprite_Attack_DragonHowl_Vfx_07",
};
numRects = ARRAY_COUNT(claudeDragonHowlVfx);
duration = 0.1f;
asset_addAnimation(assetManager, arena, "claudeDragonHowlVfx",
claudeAtlas, claudeDragonHowlVfx, numRects,
duration);
char *claudeRipperBlast[6] = {
"ClaudeSprite_Attack_RipperBlast_01",
"ClaudeSprite_Attack_RipperBlast_02",
"ClaudeSprite_Attack_RipperBlast_03",
"ClaudeSprite_Attack_RipperBlast_04",
"ClaudeSprite_Attack_RipperBlast_05",
"ClaudeSprite_Attack_RipperBlast_06",
};
numRects = ARRAY_COUNT(claudeRipperBlast);
duration = 0.1f;
asset_addAnimation(assetManager, arena, "claudeRipperBlast",
claudeAtlas, claudeRipperBlast, numRects,
duration);
char *claudeRipperBlastVfx[9] = {
"ClaudeSprite_Attack_RipperBlast_Vfx_04",
"ClaudeSprite_Attack_RipperBlast_Vfx_05",
"ClaudeSprite_Attack_RipperBlast_Vfx_06",
"ClaudeSprite_Attack_RipperBlast_Vfx_07",
"ClaudeSprite_Attack_RipperBlast_Vfx_08",
"ClaudeSprite_Attack_RipperBlast_Vfx_09",
"ClaudeSprite_Attack_RipperBlast_Vfx_10",
"ClaudeSprite_Attack_RipperBlast_Vfx_11",
"ClaudeSprite_Attack_RipperBlast_Vfx_12",
};
numRects = ARRAY_COUNT(claudeRipperBlastVfx);
duration = 0.1f;
asset_addAnimation(assetManager, arena, "claudeRipperBlastVfx",
claudeAtlas, claudeRipperBlastVfx, numRects,
duration);
// Victory animation
char *claudeVictory[8] = {"ClaudeSprite_Battle_Victory_01",
"ClaudeSprite_Battle_Victory_02",
@ -318,6 +393,7 @@ INTERNAL void assetInit(GameState *state)
asset_addAnimation(assetManager, arena, "claudeAttackSlashLeft",
claudeAtlas, claudeAttackSlashLeft, numRects,
duration);
}
else
{
@ -451,6 +527,7 @@ INTERNAL void entityInit(GameState *state, v2 windowSize)
CAST(i32) windowSize.h / state->tileSize);
#endif
EventQueue *eventQueue = &state->eventQueue;
for (i32 i = 0; i < ARRAY_COUNT(state->world); i++)
{
World *const world = &state->world[i];
@ -488,7 +565,7 @@ INTERNAL void entityInit(GameState *state, v2 windowSize)
dir, tex, collides);
entity_addAnim(assetManager, tile, "terrainGrass");
entity_setActiveAnim(tile, "terrainGrass");
entity_setActiveAnim(eventQueue, tile, "terrainGrass");
}
}
#endif
@ -552,10 +629,17 @@ INTERNAL void entityInit(GameState *state, v2 windowSize)
entity_addAnim(assetManager, hero, "claudeIdle");
entity_addAnim(assetManager, hero, "claudeRun");
entity_addAnim(assetManager, hero, "claudeBattleIdle");
entity_addAnim(assetManager, hero, "claudeAttack");
entity_addAnim(assetManager, hero, "claudeAttackUp");
entity_addAnim(assetManager, hero, "claudeAttackDown");
entity_addAnim(assetManager, hero, "claudeDragonHowl");
entity_addAnim(assetManager, hero, "claudeEnergySword");
entity_addAnim(assetManager, hero, "claudeRipperBlast");
entity_addAnim(assetManager, hero, "claudeAirSlash");
entity_setActiveAnim(hero, "claudeIdle");
entity_setActiveAnim(eventQueue, hero, "claudeIdle");
/* Create a NPC */
pos = V2(hero->pos.x * 3, CAST(f32) state->tileSize);
@ -569,12 +653,12 @@ INTERNAL void entityInit(GameState *state, v2 windowSize)
/* Populate npc animation references */
entity_addAnim(assetManager, npc, "claudeVictory");
entity_setActiveAnim(npc, "claudeVictory");
entity_setActiveAnim(eventQueue, npc, "claudeVictory");
/* Create a Mob */
pos = V2(renderer->size.w - (renderer->size.w / 3.0f),
CAST(f32) state->tileSize);
addGenericMob(arena, assetManager, world, pos);
addGenericMob(eventQueue, arena, assetManager, world, pos);
#ifdef DENGINE_DEBUG
DEBUG_LOG("World populated");
@ -923,17 +1007,18 @@ INTERNAL inline void updateWorldBattleEntities(World *world, Entity *entity,
}
// TODO(doyle): Function too vague
INTERNAL inline void resetEntityState(World *world, Entity *entity)
INTERNAL inline void resetEntityState(EventQueue *eventQueue, World *world,
Entity *entity)
{
updateWorldBattleEntities(world, entity, ENTITY_NOT_IN_BATTLE);
entity_setActiveAnim(entity, "claudeIdle");
entity_setActiveAnim(eventQueue, entity, "claudeIdle");
entity->stats->busyDuration = 0;
entity->stats->actionTimer = entity->stats->actionRate;
entity->stats->queuedAttack = entityattack_invalid;
entity->stats->entityIdToAttack = ENTITY_NULL_ID;
}
INTERNAL registerEvent(EventQueue *eventQueue, enum EventType type, void *data)
void worldTraveller_registerEvent(EventQueue *eventQueue, enum EventType type, void *data)
{
#ifdef DENGINE_DEBUG
ASSERT(eventQueue && type < eventtype_count);
@ -969,8 +1054,9 @@ INTERNAL void entityStateSwitch(EventQueue *eventQueue, World *world,
// 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:
registerEvent(eventQueue, eventtype_entity_died, CAST(void *)entity);
entity_setActiveAnim(entity, "claudeIdle");
worldTraveller_registerEvent(eventQueue, eventtype_entity_died,
CAST(void *) entity);
entity_setActiveAnim(eventQueue, entity, "claudeIdle");
entity->stats->busyDuration = 0;
entity->stats->actionTimer = entity->stats->actionRate;
entity->stats->queuedAttack = entityattack_invalid;
@ -993,11 +1079,12 @@ INTERNAL void entityStateSwitch(EventQueue *eventQueue, World *world,
break;
}
case entitystate_idle:
resetEntityState(world, entity);
resetEntityState(eventQueue, world, entity);
break;
case entitystate_dead:
registerEvent(eventQueue, eventtype_entity_died, CAST(void *)entity);
resetEntityState(world, entity);
worldTraveller_registerEvent(eventQueue, eventtype_entity_died,
CAST(void *) entity);
resetEntityState(eventQueue, world, entity);
break;
default:
#ifdef DENGINE_DEBUG
@ -1010,17 +1097,18 @@ INTERNAL void entityStateSwitch(EventQueue *eventQueue, World *world,
switch (newState)
{
case entitystate_battle:
entity_setActiveAnim(entity, "claudeBattleIdle");
entity_setActiveAnim(eventQueue, entity, "claudeBattleIdle");
entity->stats->actionTimer = entity->stats->actionRate;
entity->stats->busyDuration = 0;
break;
// NOTE(doyle): Entity has been forced out of an attack (out of range)
case entitystate_idle:
resetEntityState(world, entity);
resetEntityState(eventQueue, world, entity);
break;
case entitystate_dead:
registerEvent(eventQueue, eventtype_entity_died, CAST(void *)entity);
resetEntityState(world, entity);
worldTraveller_registerEvent(eventQueue, eventtype_entity_died,
CAST(void *) entity);
resetEntityState(eventQueue, world, entity);
break;
default:
#ifdef DENGINE_DEBUG
@ -1071,13 +1159,13 @@ INTERNAL void beginAttack(AssetManager *assetManager, MemoryArena *arena,
entityStateSwitch(eventQueue, world, attacker, entitystate_attack);
switch (attacker->stats->queuedAttack)
{
case entityattack_tackle:
case entityattack_claudeAttack:
{
entity_setActiveAnim(attacker, "claudeAttack");
entity_setActiveAnim(eventQueue, attacker, "claudeAttack");
if (attacker->stats->weapon)
{
attacker->stats->weapon->invisible = FALSE;
entity_setActiveAnim(attacker->stats->weapon,
entity_setActiveAnim(eventQueue, attacker->stats->weapon,
"claudeAttackSlashLeft");
}
if (attacker->direction == direction_east)
@ -1087,27 +1175,106 @@ INTERNAL void beginAttack(AssetManager *assetManager, MemoryArena *arena,
break;
}
case entityattack_energySword:
case entityattack_claudeAttackUp:
{
entity_setActiveAnim(attacker, "claudeEnergySword");
entity_setActiveAnim(eventQueue, attacker, "claudeAttackUp");
if (attacker->stats->weapon)
{
attacker->stats->weapon->invisible = FALSE;
entity_setActiveAnim(eventQueue, attacker->stats->weapon,
"claudeAttackSlashLeft");
}
if (attacker->direction == direction_east)
attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
else
attacker->dPos.x -= (1.0f * METERS_TO_PIXEL);
break;
}
case entityattack_airSlash:
case entityattack_claudeAttackDown:
{
entity_setActiveAnim(attacker, "claudeAirSlash");
entity_setActiveAnim(eventQueue, attacker, "claudeAttackDown");
if (attacker->stats->weapon)
{
attacker->stats->weapon->invisible = FALSE;
entity_setActiveAnim(eventQueue, attacker->stats->weapon,
"claudeAttackSlashLeft");
}
if (attacker->direction == direction_east)
attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
else
attacker->dPos.x -= (1.0f * METERS_TO_PIXEL);
break;
}
case entityattack_claudeDragonHowl:
{
entity_setActiveAnim(eventQueue, attacker, "claudeDragonHowl");
f32 scale = 1.5f;
v2 size = V2(40, 40);
Entity *projectile = entity_add(
arena, world, attacker->pos, size, scale,
entitytype_projectile, attacker->direction, attacker->tex, TRUE);
projectile->collidesWith[entitytype_hero] = TRUE;
projectile->collidesWith[entitytype_mob] = TRUE;
projectile->collidesWith[attacker->type] = FALSE;
projectile->stats->entityIdToAttack = attacker->stats->entityIdToAttack;
entity_addAnim(assetManager, projectile, "claudeDragonHowlVfx");
entity_setActiveAnim(eventQueue, projectile, "claudeDragonHowlVfx");
break;
}
case entityattack_claudeEnergySword:
{
entity_setActiveAnim(eventQueue, attacker, "claudeEnergySword");
break;
}
case entityattack_claudeRipperBlast:
{
entity_setActiveAnim(eventQueue, attacker, "claudeRipperBlast");
f32 scale = 5.0f;
v2 size = V2(20, 20);
Entity *target = entity_get(world, attacker->stats->entityIdToAttack);
v2 targetPos = v2_add(attacker->pos, V2(100, 0));
if (target)
{
targetPos = target->pos;
}
Entity *projectile = entity_add(
arena, world, targetPos, size, scale,
entitytype_attackObject, attacker->direction, attacker->tex, TRUE);
projectile->collidesWith[entitytype_hero] = TRUE;
projectile->collidesWith[entitytype_mob] = TRUE;
projectile->collidesWith[attacker->type] = FALSE;
projectile->stats->entityIdToAttack = attacker->stats->entityIdToAttack;
entity_addAnim(assetManager, projectile, "claudeRipperBlastVfx");
entity_setActiveAnim(eventQueue, projectile, "claudeRipperBlastVfx");
break;
}
case entityattack_claudeAirSlash:
{
entity_setActiveAnim(eventQueue, attacker, "claudeAirSlash");
f32 scale = 1.5f;
v2 size = V2(20, 20);
Entity *projectile = entity_add(
arena, world, attacker->pos, size, scale,
entitytype_projectile, attacker->direction, attacker->tex, TRUE);
projectile->collidesWith[entitytype_hero] = FALSE;
projectile->collidesWith[entitytype_hero] = TRUE;
projectile->collidesWith[entitytype_mob] = TRUE;
projectile->collidesWith[attacker->type] = FALSE;
projectile->stats->entityIdToAttack = attacker->stats->entityIdToAttack;
entity_addAnim(assetManager, projectile, "claudeAirSlashVfx");
entity_setActiveAnim(projectile, "claudeAirSlashVfx");
entity_setActiveAnim(eventQueue, projectile, "claudeAirSlashVfx");
v2 initialOffset = V2(size.x * 0.5f, 0);
f32 deltaScale = 0.3f;
@ -1128,7 +1295,7 @@ INTERNAL void beginAttack(AssetManager *assetManager, MemoryArena *arena,
child->stats->entityIdToAttack =
projectile->stats->entityIdToAttack;
entity_addAnim(assetManager, child, "claudeAirSlashVfx");
entity_setActiveAnim(child, "claudeAirSlashVfx");
entity_setActiveAnim(eventQueue, child, "claudeAirSlashVfx");
projectile->childIds[i] = child->id;
}
break;
@ -1159,7 +1326,9 @@ INTERNAL void endAttack(MemoryArena *arena, EventQueue *eventQueue,
entityStateSwitch(eventQueue, world, attacker, entitystate_battle);
switch (attacker->stats->queuedAttack)
{
case entityattack_tackle:
case entityattack_claudeAttack:
case entityattack_claudeAttackUp:
case entityattack_claudeAttackDown:
// TODO(doyle): Move animation offsets out and into animation type
if (attacker->stats->weapon)
@ -1174,16 +1343,19 @@ INTERNAL void endAttack(MemoryArena *arena, EventQueue *eventQueue,
break;
case entityattack_airSlash:
case entityattack_claudeAirSlash:
case entityattack_claudeRipperBlast:
case entityattack_claudeDragonHowl:
break;
case entityattack_energySword:
case entityattack_claudeEnergySword:
attacker->stats->health += 80;
AttackSpec *attackSpec = PLATFORM_MEM_ALLOC(arena, 1, AttackSpec);
attackSpec->attacker = attacker;
attackSpec->defender = attacker;
attackSpec->damage = 30;
registerEvent(eventQueue, eventtype_end_attack, attackSpec);
worldTraveller_registerEvent(eventQueue, eventtype_end_attack,
attackSpec);
return;
default:
@ -1236,7 +1408,8 @@ INTERNAL void endAttack(MemoryArena *arena, EventQueue *eventQueue,
attackSpec->defender = defender;
attackSpec->damage = damage;
registerEvent(eventQueue, eventtype_end_attack, attackSpec);
worldTraveller_registerEvent(eventQueue, eventtype_end_attack,
attackSpec);
// TODO(doyle): Use attacker stats in battle equations
if (attacker->type == entitytype_hero)
{
@ -1286,17 +1459,22 @@ INTERNAL enum EntityAttack selectBestAttack(Entity *entity)
{
if (entity->stats->health <= 50.0f && entity->type == entitytype_hero)
{
return entityattack_energySword;
return entityattack_claudeEnergySword;
}
else
{
enum EntityAttack attack = entityattack_tackle;;
if (entity->type == entitytype_hero)
enum EntityAttack attack = entityattack_claudeAttack;
i32 choice = rand() % 7;
switch(choice)
{
b32 choice = rand() % 2;
attack =
(choice == TRUE) ? entityattack_airSlash : entityattack_tackle;
//attack = entityattack_tackle;
case 0: attack = entityattack_claudeAttack; break;
case 1: attack = entityattack_claudeAttackUp; break;
case 2: attack = entityattack_claudeAttackDown; break;
case 3: attack = entityattack_claudeDragonHowl; break;
case 4: attack = entityattack_claudeEnergySword; break;
case 5: attack = entityattack_claudeRipperBlast; break;
case 6: attack = entityattack_claudeAirSlash; break;
default: break;
}
return attack;
@ -1447,6 +1625,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
World *const world = &state->world[state->currWorldIndex];
Font *font = &assetManager->font;
MemoryArena *arena = &state->arena;
EventQueue *eventQueue = &state->eventQueue;
/*
**********************
@ -1526,7 +1705,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
v2 pos =
V2(renderer->size.w - (renderer->size.w / xModifier), yPos);
addGenericMob(&state->arena, &state->assetManager, world, pos);
addGenericMob(eventQueue, &state->arena, &state->assetManager, world, pos);
}
}
@ -1535,7 +1714,6 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
* Update entities and render
******************************
*/
EventQueue eventQueue = {0};
Rect camera = createWorldBoundedCamera(world, renderer->size);
AudioManager *audioManager = &state->audioManager;
@ -1655,7 +1833,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
}
i32 numEntitiesInBattleBefore = world->numEntitiesInBattle;
entityStateSwitch(&eventQueue, world, entity, newState);
entityStateSwitch(eventQueue, world, entity, newState);
if (numEntitiesInBattleBefore == 0 &&
world->numEntitiesInBattle > 0)
@ -1767,14 +1945,14 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
{
if (common_strcmp(currAnimName, "claudeIdle") == 0)
{
entity_setActiveAnim(hero, "claudeRun");
entity_setActiveAnim(eventQueue, hero, "claudeRun");
}
}
else
{
if (common_strcmp(currAnimName, "claudeRun") == 0)
{
entity_setActiveAnim(hero, "claudeIdle");
entity_setActiveAnim(eventQueue, hero, "claudeIdle");
}
}
@ -1813,7 +1991,18 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
// TODO(doyle): Unify concept of dead entity for mobs and
// projectiles
projectile->state = entitystate_dead;
registerEvent(&eventQueue, eventtype_entity_died, projectile);
// TODO(doyle): register endattack event
worldTraveller_registerEvent(eventQueue, eventtype_entity_died,
projectile);
}
}
else if (entity->type == entitytype_attackObject)
{
if (entity->animList[entity->currAnimId].timesCompleted == 1)
{
entity->state = entitystate_dead;
worldTraveller_registerEvent(eventQueue, eventtype_entity_died,
entity);
}
}
@ -1846,7 +2035,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
}
/* Launch up attack animation */
beginAttack(assetManager, &state->arena, &eventQueue, world,
beginAttack(assetManager, &state->arena, eventQueue, world,
entity);
}
}
@ -1858,7 +2047,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
if (stats->busyDuration <= 0)
{
/* Apply attack damage */
endAttack(&state->arena, &eventQueue, world, entity);
endAttack(&state->arena, eventQueue, world, entity);
}
}
}
@ -1876,7 +2065,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
if (entity->tex)
{
entity_updateAnim(entity, dt);
entity_updateAnim(eventQueue, entity, dt);
/* Calculate region to render */
renderer_entity(renderer, camera, entity,
v2_scale(entity->renderSize, 0.5f), 0,
@ -1890,9 +2079,13 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
*****************************************
*/
i32 numDeadEntities = 0;
for (i32 i = 0; i < eventQueue.numEvents; i++)
for (i32 i = 0; i < eventQueue->numEvents; i++)
{
Event event = eventQueue.queue[i];
Event event = eventQueue->queue[i];
eventQueue->queue[i].type = eventtype_null;
eventQueue->queue[i].data = NULL;
switch(event.type)
{
case eventtype_end_attack:
@ -1903,7 +2096,9 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
Entity *attacker = attackSpec->attacker;
Entity *defender = attackSpec->defender;
if (attacker->stats->queuedAttack == entityattack_tackle)
if (attacker->stats->queuedAttack == entityattack_claudeAttack ||
attacker->stats->queuedAttack == entityattack_claudeAttackUp ||
attacker->stats->queuedAttack == entityattack_claudeAttackDown)
{
i32 freeAudioIndex = entityGetFreeAudioRendererIndex(attacker);
if (freeAudioIndex != -1)
@ -1940,7 +2135,8 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
1);
}
}
else if (attacker->stats->queuedAttack == entityattack_energySword)
else if (attacker->stats->queuedAttack ==
entityattack_claudeEnergySword)
{
i32 freeAudioIndex = entityGetFreeAudioRendererIndex(attacker);
if (freeAudioIndex != -1)
@ -1952,7 +2148,8 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
1);
}
}
else if (attacker->stats->queuedAttack == entityattack_airSlash)
else if (attacker->stats->queuedAttack ==
entityattack_claudeAirSlash)
{
i32 freeAudioIndex = entityGetFreeAudioRendererIndex(attacker);
if (freeAudioIndex != -1)
@ -1964,6 +2161,32 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
1);
}
}
else if (attacker->stats->queuedAttack ==
entityattack_claudeDragonHowl)
{
i32 freeAudioIndex = entityGetFreeAudioRendererIndex(attacker);
if (freeAudioIndex != -1)
{
audio_playVorbis(
arena, audioManager,
&attacker->audioRenderer[freeAudioIndex],
asset_getVorbis(assetManager, "Attack_Dragon_howl"),
1);
}
}
else if (attacker->stats->queuedAttack ==
entityattack_claudeRipperBlast)
{
i32 freeAudioIndex = entityGetFreeAudioRendererIndex(attacker);
if (freeAudioIndex != -1)
{
audio_playVorbis(
arena, audioManager,
&attacker->audioRenderer[freeAudioIndex],
asset_getVorbis(assetManager, "Attack_tear_into_pieces"),
1);
}
}
else
{
//ASSERT(INVALID_CODE_PATH);
@ -2039,6 +2262,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
}
}
}
eventQueue->numEvents = 0;
/*
****************************
@ -2053,11 +2277,11 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
if (hero->state == entitystate_battle &&
world->numEntitiesInBattle == 1)
{
entityStateSwitch(&eventQueue, world, hero, entitystate_idle);
entityStateSwitch(eventQueue, world, hero, entitystate_idle);
}
else if (hero->state != entitystate_attack)
{
entityStateSwitch(&eventQueue, world, hero, entitystate_battle);
entityStateSwitch(eventQueue, world, hero, entitystate_battle);
}
}
else
@ -2066,7 +2290,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
{
hero->state = entitystate_idle;
world->entityIdInBattle[hero->id] = FALSE;
entity_setActiveAnim(hero, "claudeIdle");
entity_setActiveAnim(eventQueue, hero, "claudeIdle");
}
hero->stats->entityIdToAttack = -1;
hero->stats->actionTimer = hero->stats->actionRate;

View File

@ -10,6 +10,7 @@ typedef struct MemoryArena MemoryArena;
typedef struct Texture Texture;
typedef struct Animation Animation;
typedef struct World World;
typedef struct EventQueue EventQueue;
typedef struct Entity Entity;
@ -29,6 +30,7 @@ enum EntityType
entitytype_hero,
entitytype_weapon,
entitytype_projectile,
entitytype_attackObject,
entitytype_npc,
entitytype_mob,
entitytype_tile,
@ -48,9 +50,13 @@ enum EntityState
enum EntityAttack
{
entityattack_tackle,
entityattack_airSlash,
entityattack_energySword,
entityattack_claudeAttack,
entityattack_claudeAttackUp,
entityattack_claudeAttackDown,
entityattack_claudeDragonHowl,
entityattack_claudeEnergySword,
entityattack_claudeRipperBlast,
entityattack_claudeAirSlash,
entityattack_count,
entityattack_invalid,
};
@ -76,6 +82,8 @@ typedef struct EntityAnim
Animation *anim;
i32 currFrame;
f32 currDuration;
u32 timesCompleted;
} EntityAnim;
struct Entity
@ -118,8 +126,10 @@ struct Entity
i32 numAudioRenderers;
};
void entity_setActiveAnim(Entity *const entity, const char *const animName);
void entity_updateAnim(Entity *const entity, const f32 dt);
void entity_setActiveAnim(EventQueue *eventQueue, Entity *const entity,
const char *const animName);
void entity_updateAnim(EventQueue *eventQueue, Entity *const entity,
const f32 dt);
void entity_addAnim(AssetManager *const assetManager, Entity *const entity,
const char *const animName);
Entity *const entity_add(MemoryArena *const arena, World *const world,

View File

@ -16,6 +16,29 @@
/* Forward declaration */
typedef struct Entity Entity;
enum EventType
{
eventtype_null = 0,
eventtype_start_attack,
eventtype_end_attack,
eventtype_start_anim,
eventtype_end_anim,
eventtype_entity_died,
eventtype_count,
};
typedef struct Event
{
enum EventType type;
void *data;
} Event;
typedef struct EventQueue
{
Event queue[1024];
i32 numEvents;
} EventQueue;
enum RectBaseline
{
rectbaseline_top,
@ -73,8 +96,11 @@ typedef struct GameState
Config config;
MemoryArena arena;
UiState uiState;
EventQueue eventQueue;
} GameState;
void worldTraveller_gameInit(GameState *state, v2 windowSize);
void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt);
void worldTraveller_registerEvent(EventQueue *eventQueue, enum EventType type,
void *data);
#endif