Add projectile attack skill
This commit is contained in:
parent
5b682ddcf6
commit
8ddac9c110
15
src/Entity.c
15
src/Entity.c
@ -55,6 +55,8 @@ void entity_updateAnim(Entity *const entity, const f32 dt)
|
||||
case entitytype_hero:
|
||||
case entitytype_mob:
|
||||
case entitytype_npc:
|
||||
case entitytype_weapon:
|
||||
case entitytype_projectile:
|
||||
char *frameName = anim->frameList[currEntityAnim->currFrame];
|
||||
Rect texRect =
|
||||
asset_getAtlasSubTex(anim->atlas, frameName);
|
||||
@ -118,6 +120,7 @@ Entity *const entity_add(MemoryArena *const arena, World *const world,
|
||||
entity.stats->entityIdToAttack = -1;
|
||||
entity.stats->queuedAttack = entityattack_invalid;
|
||||
entity.state = entitystate_idle;
|
||||
entity.collidesWith[entitytype_mob] = TRUE;
|
||||
break;
|
||||
case entitytype_mob:
|
||||
{
|
||||
@ -130,8 +133,20 @@ Entity *const entity_add(MemoryArena *const arena, World *const world,
|
||||
entity.stats->entityIdToAttack = -1;
|
||||
entity.stats->queuedAttack = entityattack_invalid;
|
||||
entity.state = entitystate_idle;
|
||||
entity.collidesWith[entitytype_hero] = TRUE;
|
||||
break;
|
||||
}
|
||||
case entitytype_projectile:
|
||||
entity.stats = PLATFORM_MEM_ALLOC(arena, 1, EntityStats);
|
||||
entity.stats->maxHealth = 100;
|
||||
entity.stats->health = entity.stats->maxHealth;
|
||||
entity.stats->actionRate = 100;
|
||||
entity.stats->actionTimer = entity.stats->actionRate;
|
||||
entity.stats->actionSpdMul = 100;
|
||||
entity.stats->entityIdToAttack = -1;
|
||||
entity.stats->queuedAttack = entityattack_invalid;
|
||||
entity.state = entitystate_idle;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
@ -272,6 +272,6 @@ void renderer_entity(Renderer *renderer, Rect camera, Entity *entity,
|
||||
v2 posInCameraSpace = v2_sub(entity->pos, camera.pos);
|
||||
// TODO(doyle): Scale temporarily
|
||||
renderObject(renderer, posInCameraSpace, v2_scale(entity->renderSize, 2),
|
||||
pivotPoint, rotate, color, entity->tex);
|
||||
pivotPoint, entity->rotation + rotate, color, entity->tex);
|
||||
}
|
||||
}
|
||||
|
@ -267,6 +267,55 @@ INTERNAL void assetInit(GameState *state)
|
||||
asset_addAnimation(assetManager, arena, "claudeEnergySword",
|
||||
claudeAtlas, claudeEnergySword, numRects,
|
||||
duration);
|
||||
|
||||
char *claudeAirSlash[7] = {"ClaudeSprite_Attack_AirSlash_01",
|
||||
"ClaudeSprite_Attack_AirSlash_02",
|
||||
"ClaudeSprite_Attack_AirSlash_03",
|
||||
"ClaudeSprite_Attack_AirSlash_04",
|
||||
"ClaudeSprite_Attack_AirSlash_05",
|
||||
"ClaudeSprite_Attack_AirSlash_06",
|
||||
"ClaudeSprite_Attack_AirSlash_07"};
|
||||
numRects = ARRAY_COUNT(claudeAirSlash);
|
||||
duration = 0.075f;
|
||||
asset_addAnimation(assetManager, arena, "claudeAirSlash",
|
||||
claudeAtlas, claudeAirSlash, numRects,
|
||||
duration);
|
||||
|
||||
char *claudeAirSlashVfx[7] = {"ClaudeSprite_Attack_AirSlash_Vfx_01",
|
||||
"ClaudeSprite_Attack_AirSlash_Vfx_02",
|
||||
"ClaudeSprite_Attack_AirSlash_Vfx_03",
|
||||
"ClaudeSprite_Attack_AirSlash_Vfx_04",
|
||||
"ClaudeSprite_Attack_AirSlash_Vfx_05",
|
||||
"ClaudeSprite_Attack_AirSlash_Vfx_06",
|
||||
"ClaudeSprite_Attack_AirSlash_Vfx_07"};
|
||||
numRects = ARRAY_COUNT(claudeAirSlashVfx);
|
||||
duration = 0.075f;
|
||||
asset_addAnimation(assetManager, arena, "claudeAirSlashVfx",
|
||||
claudeAtlas, claudeAirSlashVfx, numRects,
|
||||
duration);
|
||||
|
||||
char *claudeSword[1] = {
|
||||
"ClaudeSprite_Sword_01",
|
||||
};
|
||||
|
||||
numRects = ARRAY_COUNT(claudeSword);
|
||||
duration = 0.4f;
|
||||
asset_addAnimation(assetManager, arena, "claudeSword",
|
||||
claudeAtlas, claudeSword, numRects,
|
||||
duration);
|
||||
|
||||
char *claudeAttackSlashLeft[4] = {
|
||||
"ClaudeSprite_Attack_Slash_Left_01",
|
||||
"ClaudeSprite_Attack_Slash_Left_02",
|
||||
"ClaudeSprite_Attack_Slash_Left_03",
|
||||
"ClaudeSprite_Attack_Slash_Left_04",
|
||||
};
|
||||
|
||||
numRects = ARRAY_COUNT(claudeAttackSlashLeft);
|
||||
duration = 0.1f;
|
||||
asset_addAnimation(assetManager, arena, "claudeAttackSlashLeft",
|
||||
claudeAtlas, claudeAttackSlashLeft, numRects,
|
||||
duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -473,6 +522,17 @@ INTERNAL void entityInit(GameState *state, v2 windowSize)
|
||||
Entity *hero =
|
||||
entity_add(arena, world, pos, size, type, dir, tex, collides);
|
||||
|
||||
#if 0
|
||||
Entity *heroWeapon =
|
||||
entity_add(arena, world, pos, V2(0, 0), entitytype_weapon,
|
||||
hero->direction, hero->tex, FALSE);
|
||||
heroWeapon->rotation = -90.0f;
|
||||
heroWeapon->invisible = TRUE;
|
||||
entity_addAnim(assetManager, heroWeapon, "claudeSword");
|
||||
|
||||
hero->stats->weapon = heroWeapon;
|
||||
#endif
|
||||
|
||||
hero->numAudioRenderers = 4;
|
||||
hero->audioRenderer =
|
||||
PLATFORM_MEM_ALLOC(arena, hero->numAudioRenderers, AudioRenderer);
|
||||
@ -489,6 +549,7 @@ INTERNAL void entityInit(GameState *state, v2 windowSize)
|
||||
entity_addAnim(assetManager, hero, "claudeBattleIdle");
|
||||
entity_addAnim(assetManager, hero, "claudeAttack");
|
||||
entity_addAnim(assetManager, hero, "claudeEnergySword");
|
||||
entity_addAnim(assetManager, hero, "claudeAirSlash");
|
||||
entity_setActiveAnim(hero, "claudeIdle");
|
||||
|
||||
/* Create a NPC */
|
||||
@ -992,7 +1053,8 @@ typedef struct AttackSpec
|
||||
i32 damage;
|
||||
} AttackSpec;
|
||||
|
||||
INTERNAL void beginAttack(EventQueue *eventQueue, World *world,
|
||||
INTERNAL void beginAttack(AssetManager *assetManager, MemoryArena *arena,
|
||||
EventQueue *eventQueue, World *world,
|
||||
Entity *attacker)
|
||||
{
|
||||
#ifdef DENGINE_DEBUG
|
||||
@ -1006,11 +1068,12 @@ INTERNAL void beginAttack(EventQueue *eventQueue, World *world,
|
||||
case entityattack_tackle:
|
||||
{
|
||||
entity_setActiveAnim(attacker, "claudeAttack");
|
||||
EntityAnim attackAnim = attacker->animList[attacker->currAnimId];
|
||||
f32 busyDuration = attackAnim.anim->frameDuration *
|
||||
CAST(f32) attackAnim.anim->numFrames;
|
||||
attacker->stats->busyDuration = busyDuration;
|
||||
|
||||
if (attacker->stats->weapon)
|
||||
{
|
||||
attacker->stats->weapon->invisible = FALSE;
|
||||
entity_setActiveAnim(attacker->stats->weapon,
|
||||
"claudeAttackSlashLeft");
|
||||
}
|
||||
if (attacker->direction == direction_east)
|
||||
attacker->dPos.x += (1.0f * METERS_TO_PIXEL);
|
||||
else
|
||||
@ -1021,10 +1084,22 @@ INTERNAL void beginAttack(EventQueue *eventQueue, World *world,
|
||||
case entityattack_energySword:
|
||||
{
|
||||
entity_setActiveAnim(attacker, "claudeEnergySword");
|
||||
EntityAnim attackAnim = attacker->animList[attacker->currAnimId];
|
||||
f32 busyDuration = attackAnim.anim->frameDuration *
|
||||
CAST(f32) attackAnim.anim->numFrames;
|
||||
attacker->stats->busyDuration = busyDuration;
|
||||
break;
|
||||
}
|
||||
|
||||
case entityattack_airSlash:
|
||||
{
|
||||
entity_setActiveAnim(attacker, "claudeAirSlash");
|
||||
Entity *projectile = entity_add(
|
||||
arena, world, attacker->pos, V2(20, 20), entitytype_projectile,
|
||||
attacker->direction, attacker->tex, TRUE);
|
||||
|
||||
projectile->collidesWith[entitytype_hero] = FALSE;
|
||||
projectile->collidesWith[entitytype_mob] = TRUE;
|
||||
|
||||
projectile->stats->entityIdToAttack = attacker->stats->entityIdToAttack;
|
||||
entity_addAnim(assetManager, projectile, "claudeAirSlashVfx");
|
||||
entity_setActiveAnim(projectile, "claudeAirSlashVfx");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1034,6 +1109,11 @@ INTERNAL void beginAttack(EventQueue *eventQueue, World *world,
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
EntityAnim attackAnim = attacker->animList[attacker->currAnimId];
|
||||
f32 busyDuration =
|
||||
attackAnim.anim->frameDuration * CAST(f32) attackAnim.anim->numFrames;
|
||||
attacker->stats->busyDuration = busyDuration;
|
||||
}
|
||||
|
||||
// TODO(doyle): MemArena here is temporary until we incorporate AttackSpec to
|
||||
@ -1050,6 +1130,12 @@ INTERNAL void endAttack(MemoryArena *arena, EventQueue *eventQueue,
|
||||
{
|
||||
case entityattack_tackle:
|
||||
// TODO(doyle): Move animation offsets out and into animation type
|
||||
|
||||
if (attacker->stats->weapon)
|
||||
{
|
||||
attacker->stats->weapon->invisible = TRUE;
|
||||
}
|
||||
|
||||
if (attacker->direction == direction_east)
|
||||
attacker->dPos.x -= (1.0f * METERS_TO_PIXEL);
|
||||
else
|
||||
@ -1057,9 +1143,11 @@ INTERNAL void endAttack(MemoryArena *arena, EventQueue *eventQueue,
|
||||
|
||||
break;
|
||||
|
||||
case entityattack_airSlash:
|
||||
break;
|
||||
|
||||
case entityattack_energySword:
|
||||
attacker->stats->health += 80;
|
||||
|
||||
AttackSpec *attackSpec = PLATFORM_MEM_ALLOC(arena, 1, AttackSpec);
|
||||
attackSpec->attacker = attacker;
|
||||
attackSpec->defender = attacker;
|
||||
@ -1110,7 +1198,7 @@ INTERNAL void endAttack(MemoryArena *arena, EventQueue *eventQueue,
|
||||
|
||||
if (!noMoreValidTargets)
|
||||
{
|
||||
i32 damage = 50;
|
||||
i32 damage = 25;
|
||||
|
||||
AttackSpec *attackSpec = PLATFORM_MEM_ALLOC(arena, 1, AttackSpec);
|
||||
attackSpec->attacker = attacker;
|
||||
@ -1122,14 +1210,14 @@ INTERNAL void endAttack(MemoryArena *arena, EventQueue *eventQueue,
|
||||
if (attacker->type == entitytype_hero)
|
||||
{
|
||||
defender->stats->health -= damage;
|
||||
if (defender->stats->health <= 0.0f) defender->stats->health = 10.0f;
|
||||
}
|
||||
else if (attacker->type == entitytype_mob)
|
||||
{
|
||||
defender->stats->health -= damage * 0.25f;
|
||||
if (defender->stats->health <= 0.0f) defender->stats->health = 10.0f;
|
||||
}
|
||||
|
||||
if (defender->stats->health <= 0.0f) defender->stats->health = 10.0f;
|
||||
|
||||
if (defender->stats->health <= 0)
|
||||
{
|
||||
entityStateSwitch(eventQueue, world, defender, entitystate_dead);
|
||||
@ -1171,7 +1259,16 @@ INTERNAL enum EntityAttack selectBestAttack(Entity *entity)
|
||||
}
|
||||
else
|
||||
{
|
||||
return entityattack_tackle;
|
||||
enum EntityAttack attack = entityattack_tackle;;
|
||||
if (entity->type == entitytype_hero)
|
||||
{
|
||||
b32 choice = rand() % 2;
|
||||
attack =
|
||||
(choice == TRUE) ? entityattack_airSlash : entityattack_tackle;
|
||||
//attack = entityattack_tackle;
|
||||
}
|
||||
|
||||
return attack;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1204,6 +1301,110 @@ typedef struct BattleState
|
||||
|
||||
GLOBAL_VAR BattleState battleState = {0};
|
||||
|
||||
INTERNAL b32 checkCollision(Entity *a, Entity *b)
|
||||
{
|
||||
b32 result = FALSE;
|
||||
if (a->collides && b->collides && a->collidesWith[b->type])
|
||||
{
|
||||
Rect aRect = {a->pos, a->hitboxSize};
|
||||
|
||||
v2 aTopLeftP =
|
||||
getPosRelativeToRect(aRect, V2(0, 0), rectbaseline_topLeft);
|
||||
v2 aTopRightP =
|
||||
getPosRelativeToRect(aRect, V2(0, 0), rectbaseline_topRight);
|
||||
v2 aBottomLeftP =
|
||||
getPosRelativeToRect(aRect, V2(0, 0), rectbaseline_bottomLeft);
|
||||
v2 aBottomRightP =
|
||||
getPosRelativeToRect(aRect, V2(0, 0), rectbaseline_bottomRight);
|
||||
|
||||
Rect bRect = {b->pos, b->hitboxSize};
|
||||
|
||||
if (math_pointInRect(bRect, aTopLeftP) ||
|
||||
math_pointInRect(bRect, aTopRightP) ||
|
||||
math_pointInRect(bRect, aBottomLeftP) ||
|
||||
math_pointInRect(bRect, aBottomRightP))
|
||||
{
|
||||
result = TRUE;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
INTERNAL b32 moveEntityAndReturnCollision(World *world, Entity *entity,
|
||||
v2 ddPos, f32 speedInMs, f32 dt)
|
||||
{
|
||||
/*
|
||||
**************************
|
||||
* Calculate Hero Speed
|
||||
**************************
|
||||
*/
|
||||
// NOTE(doyle): Clipping threshold for snapping velocity to 0
|
||||
f32 epsilon = 15.0f;
|
||||
v2 epsilonDpos = v2_sub(V2(epsilon, epsilon),
|
||||
V2(ABS(entity->dPos.x), ABS(entity->dPos.y)));
|
||||
|
||||
if (epsilonDpos.x >= 0.0f && epsilonDpos.y >= 0.0f)
|
||||
entity->dPos = V2(0.0f, 0.0f);
|
||||
|
||||
f32 speedInPixels = speedInMs * METERS_TO_PIXEL;
|
||||
|
||||
ddPos = v2_scale(ddPos, speedInPixels);
|
||||
// TODO(doyle): Counteracting force on player's acceleration is
|
||||
// arbitrary
|
||||
ddPos = v2_sub(ddPos, v2_scale(entity->dPos, 5.5f));
|
||||
|
||||
/*
|
||||
NOTE(doyle): Calculate new position from acceleration with old
|
||||
velocity
|
||||
new Position = (a/2) * (t^2) + (v*t) + p,
|
||||
acceleration = (a/2) * (t^2)
|
||||
old velocity = (v*t)
|
||||
*/
|
||||
v2 ddPosNew = v2_scale(v2_scale(ddPos, 0.5f), SQUARED(dt));
|
||||
v2 dPos = v2_scale(entity->dPos, dt);
|
||||
|
||||
v2 oldP = entity->pos;
|
||||
v2 newP = v2_add(v2_add(ddPosNew, dPos), entity->pos);
|
||||
entity->pos = newP;
|
||||
|
||||
/*
|
||||
**************************
|
||||
* Collision Detection
|
||||
**************************
|
||||
*/
|
||||
// TODO(doyle): Only check collision for entities within small
|
||||
// bounding box of the hero
|
||||
b32 entityCollided = FALSE;
|
||||
if (entity->collides)
|
||||
{
|
||||
for (i32 i = 0; i < world->maxEntities; i++)
|
||||
{
|
||||
Entity *collider = &world->entities[i];
|
||||
if (collider->state == entitystate_dead) continue;
|
||||
if (collider->id == entity->id) continue;
|
||||
|
||||
entityCollided = checkCollision(entity, collider);
|
||||
if (entityCollided) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (entityCollided)
|
||||
{
|
||||
entity->dPos = V2(0.0f, 0.0f);
|
||||
entity->pos = oldP;
|
||||
}
|
||||
else
|
||||
{
|
||||
// f'(t) = curr velocity = a*t + v, where v is old velocity
|
||||
entity->dPos = v2_add(entity->dPos, v2_scale(ddPos, dt));
|
||||
entity->pos = newP;
|
||||
}
|
||||
|
||||
return entityCollided;
|
||||
}
|
||||
|
||||
void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
{
|
||||
// TODO(doyle): use proper dt limiting techniques
|
||||
@ -1325,6 +1526,7 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
}
|
||||
|
||||
if (entity->state == entitystate_dead) continue;
|
||||
if (entity->invisible == TRUE) continue;
|
||||
|
||||
if (entity->type == entitytype_soundscape)
|
||||
{
|
||||
@ -1453,14 +1655,14 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
**************************
|
||||
* Calculate Hero Movement
|
||||
**************************
|
||||
*/
|
||||
if (entity->type == entitytype_hero)
|
||||
else if (entity->type == entitytype_hero)
|
||||
{
|
||||
/*
|
||||
**************************
|
||||
* Calculate Hero Movement
|
||||
**************************
|
||||
*/
|
||||
|
||||
/*
|
||||
Equations of Motion
|
||||
f(t) = position m
|
||||
@ -1514,111 +1716,71 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
**************************
|
||||
* Calculate Hero Speed
|
||||
**************************
|
||||
*/
|
||||
// NOTE(doyle): Clipping threshold for snapping velocity to 0
|
||||
f32 epsilon = 15.0f;
|
||||
v2 epsilonDpos = v2_sub(V2(epsilon, epsilon),
|
||||
V2(ABS(hero->dPos.x), ABS(hero->dPos.y)));
|
||||
|
||||
char *currAnimName = hero->animList[hero->currAnimId].anim->key;
|
||||
if (epsilonDpos.x >= 0.0f && epsilonDpos.y >= 0.0f)
|
||||
{
|
||||
hero->dPos = V2(0.0f, 0.0f);
|
||||
if (common_strcmp(currAnimName, "claudeRun") == 0)
|
||||
{
|
||||
entity_setActiveAnim(hero, "claudeIdle");
|
||||
}
|
||||
}
|
||||
else if (common_strcmp(currAnimName, "claudeIdle") == 0)
|
||||
{
|
||||
entity_setActiveAnim(hero, "claudeRun");
|
||||
}
|
||||
|
||||
f32 heroSpeed = 6.2f * METERS_TO_PIXEL;
|
||||
f32 heroSpeed = 6.2f;
|
||||
if (getKeyStatus(&state->input.keys[keycode_leftShift],
|
||||
readkeytype_repeat, KEY_DELAY_NONE, dt))
|
||||
{
|
||||
// TODO: Context sensitive command separation
|
||||
state->uiState.keyMod = keycode_leftShift;
|
||||
heroSpeed = CAST(f32)(22.0f * 10.0f * METERS_TO_PIXEL);
|
||||
heroSpeed *= 10.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->uiState.keyMod = keycode_null;
|
||||
}
|
||||
|
||||
ddPos = v2_scale(ddPos, heroSpeed);
|
||||
// TODO(doyle): Counteracting force on player's acceleration is
|
||||
// arbitrary
|
||||
ddPos = v2_sub(ddPos, v2_scale(hero->dPos, 5.5f));
|
||||
moveEntityAndReturnCollision(world, hero, ddPos, heroSpeed, dt);
|
||||
|
||||
/*
|
||||
NOTE(doyle): Calculate new position from acceleration with old
|
||||
velocity
|
||||
new Position = (a/2) * (t^2) + (v*t) + p,
|
||||
acceleration = (a/2) * (t^2)
|
||||
old velocity = (v*t)
|
||||
*/
|
||||
v2 ddPosNew = v2_scale(v2_scale(ddPos, 0.5f), SQUARED(dt));
|
||||
v2 dPos = v2_scale(hero->dPos, dt);
|
||||
v2 newHeroP = v2_add(v2_add(ddPosNew, dPos), hero->pos);
|
||||
|
||||
/*
|
||||
**************************
|
||||
* Collision Detection
|
||||
**************************
|
||||
*/
|
||||
// TODO(doyle): Only check collision for entities within small
|
||||
// bounding box of the hero
|
||||
b32 heroCollided = FALSE;
|
||||
if (hero->collides == TRUE)
|
||||
char *currAnimName = hero->animList[hero->currAnimId].anim->key;
|
||||
if (ABS(entity->dPos.x) > 0.0f || ABS(entity->dPos.y) > 0.0f)
|
||||
{
|
||||
for (i32 i = 0; i < world->maxEntities; i++)
|
||||
if (common_strcmp(currAnimName, "claudeIdle") == 0)
|
||||
{
|
||||
Entity collider = world->entities[i];
|
||||
if (collider.state == entitystate_dead) continue;
|
||||
if (collider.id == world->heroId) continue;
|
||||
|
||||
if (collider.collides)
|
||||
{
|
||||
Rect heroRect = {newHeroP, hero->hitboxSize};
|
||||
|
||||
v2 heroTopLeftP = getPosRelativeToRect(
|
||||
heroRect, V2(0, 0), rectbaseline_topLeft);
|
||||
v2 heroTopRightP = getPosRelativeToRect(
|
||||
heroRect, V2(0, 0), rectbaseline_topRight);
|
||||
v2 heroBottomLeftP = getPosRelativeToRect(
|
||||
heroRect, V2(0, 0), rectbaseline_bottomLeft);
|
||||
v2 heroBottomRightP = getPosRelativeToRect(
|
||||
heroRect, V2(0, 0), rectbaseline_bottomRight);
|
||||
|
||||
Rect colliderRect = {collider.pos, collider.hitboxSize};
|
||||
|
||||
if (math_pointInRect(colliderRect, heroTopLeftP) ||
|
||||
math_pointInRect(colliderRect, heroTopRightP) ||
|
||||
math_pointInRect(colliderRect, heroBottomLeftP) ||
|
||||
math_pointInRect(colliderRect, heroBottomRightP))
|
||||
{
|
||||
heroCollided = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
entity_setActiveAnim(hero, "claudeRun");
|
||||
}
|
||||
}
|
||||
|
||||
if (heroCollided)
|
||||
{
|
||||
hero->dPos = V2(0.0f, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// f'(t) = curr velocity = a*t + v, where v is old velocity
|
||||
hero->dPos = v2_add(hero->dPos, v2_scale(ddPos, dt));
|
||||
hero->pos = newHeroP;
|
||||
if (common_strcmp(currAnimName, "claudeRun") == 0)
|
||||
{
|
||||
entity_setActiveAnim(hero, "claudeIdle");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (entity->type == entitytype_projectile)
|
||||
{
|
||||
Entity *projectile = entity;
|
||||
i32 targetIndex =
|
||||
entity_getIndex(world, projectile->stats->entityIdToAttack);
|
||||
Entity *target = &world->entities[targetIndex];
|
||||
|
||||
v2 ddPos = V2(0, 0);
|
||||
f32 projectileSpeed = 10.0f;
|
||||
|
||||
v2 difference = v2_sub(projectile->pos, target->pos);
|
||||
f32 longSide = (ABS(difference.x) > ABS(difference.y))
|
||||
? difference.x
|
||||
: difference.y;
|
||||
|
||||
ddPos.x = (difference.x / longSide);
|
||||
ddPos.y = (difference.y / longSide);
|
||||
|
||||
if (ddPos.x != 0.0f && ddPos.y != 0.0f)
|
||||
{
|
||||
// NOTE(doyle): Cheese it and pre-compute the vector for
|
||||
// diagonal using pythagoras theorem on a unit triangle 1^2
|
||||
// + 1^2 = c^2
|
||||
ddPos = v2_scale(ddPos, 0.70710678118f);
|
||||
}
|
||||
|
||||
if (moveEntityAndReturnCollision(world, projectile, ddPos,
|
||||
projectileSpeed, dt))
|
||||
{
|
||||
// TODO(doyle): Unify concept of dead entity for mobs and
|
||||
// projectiles
|
||||
projectile->state = entitystate_dead;
|
||||
registerEvent(&eventQueue, eventtype_entity_died, projectile);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1630,6 +1792,12 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
if (entity->type == entitytype_mob || entity->type == entitytype_hero)
|
||||
{
|
||||
EntityStats *stats = entity->stats;
|
||||
|
||||
if (stats->weapon)
|
||||
{
|
||||
stats->weapon->pos = entity->pos;
|
||||
}
|
||||
|
||||
if (entity->state == entitystate_battle)
|
||||
{
|
||||
if (stats->actionTimer > 0)
|
||||
@ -1645,7 +1813,8 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
}
|
||||
|
||||
/* Launch up attack animation */
|
||||
beginAttack(&eventQueue, world, entity);
|
||||
beginAttack(assetManager, &state->arena, &eventQueue, world,
|
||||
entity);
|
||||
}
|
||||
}
|
||||
else if (entity->state == entitystate_attack)
|
||||
@ -1676,7 +1845,8 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
{
|
||||
entity_updateAnim(entity, dt);
|
||||
/* Calculate region to render */
|
||||
renderer_entity(renderer, camera, entity, V2(0, 0), 0,
|
||||
renderer_entity(renderer, camera, entity,
|
||||
v2_scale(entity->renderSize, 0.5f), 0,
|
||||
V4(1, 1, 1, 1));
|
||||
}
|
||||
}
|
||||
@ -1705,25 +1875,11 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
i32 freeAudioIndex = entityGetFreeAudioRendererIndex(attacker);
|
||||
if (freeAudioIndex != -1)
|
||||
{
|
||||
char *attackSfx[19] = {"Attack_1",
|
||||
"Attack_2",
|
||||
"Attack_3",
|
||||
"Attack_4",
|
||||
"Attack_5",
|
||||
"Attack_6",
|
||||
"Attack_7",
|
||||
"Attack_hit_it",
|
||||
"Attack_take_that",
|
||||
"Attack_hya",
|
||||
"Attack_air_slash",
|
||||
"Attack_Dragon_howl",
|
||||
"Attack_burn",
|
||||
"Attack_burst_knuckle",
|
||||
"Attack_mirror_slice",
|
||||
"Attack_shooting_star",
|
||||
"Attack_sword_bomber",
|
||||
"Attack_tear_into_pieces",
|
||||
"Attack_twin_slash"};
|
||||
char *attackSfx[11] = {
|
||||
"Attack_1", "Attack_2", "Attack_3",
|
||||
"Attack_4", "Attack_5", "Attack_6",
|
||||
"Attack_7", "Attack_hit_it", "Attack_take_that",
|
||||
"Attack_hya", "Attack_burn"};
|
||||
|
||||
i32 attackSfxIndex = rand() % ARRAY_COUNT(attackSfx);
|
||||
audio_playVorbis(arena, audioManager,
|
||||
@ -1763,9 +1919,21 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
1);
|
||||
}
|
||||
}
|
||||
else if (attacker->stats->queuedAttack == entityattack_airSlash)
|
||||
{
|
||||
i32 freeAudioIndex = entityGetFreeAudioRendererIndex(attacker);
|
||||
if (freeAudioIndex != -1)
|
||||
{
|
||||
audio_playVorbis(
|
||||
arena, audioManager,
|
||||
&attacker->audioRenderer[freeAudioIndex],
|
||||
asset_getVorbis(assetManager, "Attack_air_slash"),
|
||||
1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(INVALID_CODE_PATH);
|
||||
//ASSERT(INVALID_CODE_PATH);
|
||||
}
|
||||
|
||||
attacker->stats->queuedAttack = entityattack_invalid;
|
||||
@ -1820,7 +1988,6 @@ void worldTraveller_gameUpdateAndRender(GameState *state, f32 dt)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
****************************
|
||||
* Update World From Results
|
||||
|
@ -11,6 +11,8 @@ typedef struct Texture Texture;
|
||||
typedef struct Animation Animation;
|
||||
typedef struct World World;
|
||||
|
||||
typedef struct Entity Entity;
|
||||
|
||||
enum Direction
|
||||
{
|
||||
direction_north,
|
||||
@ -25,6 +27,8 @@ enum EntityType
|
||||
{
|
||||
entitytype_null,
|
||||
entitytype_hero,
|
||||
entitytype_weapon,
|
||||
entitytype_projectile,
|
||||
entitytype_npc,
|
||||
entitytype_mob,
|
||||
entitytype_tile,
|
||||
@ -45,6 +49,7 @@ enum EntityState
|
||||
enum EntityAttack
|
||||
{
|
||||
entityattack_tackle,
|
||||
entityattack_airSlash,
|
||||
entityattack_energySword,
|
||||
entityattack_count,
|
||||
entityattack_invalid,
|
||||
@ -62,6 +67,8 @@ typedef struct EntityStats
|
||||
f32 busyDuration;
|
||||
i32 entityIdToAttack;
|
||||
enum EntityAttack queuedAttack;
|
||||
|
||||
Entity *weapon;
|
||||
} EntityStats;
|
||||
|
||||
typedef struct EntityAnim
|
||||
@ -71,7 +78,7 @@ typedef struct EntityAnim
|
||||
f32 currDuration;
|
||||
} EntityAnim;
|
||||
|
||||
typedef struct Entity
|
||||
struct Entity
|
||||
{
|
||||
i32 id;
|
||||
|
||||
@ -79,13 +86,23 @@ typedef struct Entity
|
||||
v2 dPos; // Velocity
|
||||
v2 hitboxSize;
|
||||
v2 renderSize;
|
||||
f32 rotation;
|
||||
|
||||
b32 invisible;
|
||||
|
||||
enum EntityState state;
|
||||
enum EntityType type;
|
||||
enum Direction direction;
|
||||
|
||||
Texture *tex;
|
||||
|
||||
// TODO(doyle): Two collision flags, we want certain entities to collide
|
||||
// with certain types of entities only (i.e. projectile from hero to enemy,
|
||||
// only collides with enemy). Having two flags is redundant, but! it does
|
||||
// allow for early-exit in collision check if the entity doesn't collide at
|
||||
// all
|
||||
b32 collides;
|
||||
enum EntityType collidesWith[entitytype_count];
|
||||
|
||||
EntityAnim animList[16];
|
||||
i32 currAnimId;
|
||||
@ -95,7 +112,7 @@ typedef struct Entity
|
||||
// TODO(doyle): Audio mixing instead of multiple renderers
|
||||
AudioRenderer *audioRenderer;
|
||||
i32 numAudioRenderers;
|
||||
} Entity;
|
||||
};
|
||||
|
||||
void entity_setActiveAnim(Entity *const entity, const char *const animName);
|
||||
void entity_updateAnim(Entity *const entity, const f32 dt);
|
||||
|
Loading…
Reference in New Issue
Block a user