2021-09-16 05:06:03 +03:00
|
|
|
#include "framework.h"
|
|
|
|
#include "animation.h"
|
|
|
|
#include "Lara.h"
|
|
|
|
#include "camera.h"
|
|
|
|
#include "level.h"
|
|
|
|
#include "Renderer11.h"
|
|
|
|
#include "Sound/sound.h"
|
|
|
|
#include "flipeffect.h"
|
|
|
|
|
|
|
|
using TEN::Renderer::g_Renderer;
|
|
|
|
|
|
|
|
BOUNDING_BOX InterpolatedBounds;
|
|
|
|
|
|
|
|
void AnimateItem(ITEM_INFO* item)
|
|
|
|
{
|
|
|
|
item->touchBits = 0;
|
|
|
|
item->hitStatus = false;
|
|
|
|
|
|
|
|
item->frameNumber++;
|
|
|
|
|
|
|
|
ANIM_STRUCT* anim = &g_Level.Anims[item->animNumber];
|
|
|
|
if (anim->numberChanges > 0 && GetChange(item, anim))
|
|
|
|
{
|
|
|
|
anim = &g_Level.Anims[item->animNumber];
|
|
|
|
|
|
|
|
item->currentAnimState = anim->currentAnimState;
|
|
|
|
|
|
|
|
if (item->requiredAnimState == item->currentAnimState)
|
|
|
|
item->requiredAnimState = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (item->frameNumber > anim->frameEnd)
|
|
|
|
{
|
|
|
|
if (anim->numberCommands > 0)
|
|
|
|
{
|
|
|
|
short* cmd = &g_Level.Commands[anim->commandIndex];
|
|
|
|
for (int i = anim->numberCommands; i > 0; i--)
|
|
|
|
{
|
|
|
|
switch (*(cmd++))
|
|
|
|
{
|
|
|
|
case COMMAND_MOVE_ORIGIN:
|
|
|
|
TranslateItem(item, cmd[0], cmd[1], cmd[2]);
|
|
|
|
cmd += 3;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case COMMAND_JUMP_VELOCITY:
|
|
|
|
item->fallspeed = *(cmd++);
|
|
|
|
item->speed = *(cmd++);
|
|
|
|
item->gravityStatus = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case COMMAND_DEACTIVATE:
|
|
|
|
if (Objects[item->objectNumber].intelligent && !item->afterDeath)
|
|
|
|
item->afterDeath = 1;
|
|
|
|
item->status = ITEM_DEACTIVATED;
|
|
|
|
break;
|
|
|
|
case COMMAND_SOUND_FX:
|
|
|
|
case COMMAND_EFFECT:
|
|
|
|
cmd += 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
item->animNumber = anim->jumpAnimNum;
|
|
|
|
item->frameNumber = anim->jumpFrameNum;
|
|
|
|
|
|
|
|
anim = &g_Level.Anims[item->animNumber];
|
|
|
|
|
|
|
|
if (item->currentAnimState != anim->currentAnimState)
|
|
|
|
{
|
|
|
|
item->currentAnimState = anim->currentAnimState;
|
|
|
|
item->goalAnimState = anim->currentAnimState;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (item->requiredAnimState == item->currentAnimState)
|
|
|
|
item->requiredAnimState = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (anim->numberCommands > 0)
|
|
|
|
{
|
|
|
|
short* cmd = &g_Level.Commands[anim->commandIndex];
|
|
|
|
int flags;
|
|
|
|
int effectID = 0;
|
|
|
|
|
|
|
|
for (int i = anim->numberCommands; i > 0; i--)
|
|
|
|
{
|
|
|
|
switch (*(cmd++))
|
|
|
|
{
|
|
|
|
case COMMAND_MOVE_ORIGIN:
|
|
|
|
cmd += 3;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case COMMAND_JUMP_VELOCITY:
|
|
|
|
cmd += 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case COMMAND_SOUND_FX:
|
|
|
|
if (item->frameNumber != *cmd)
|
|
|
|
{
|
|
|
|
cmd += 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags = cmd[1] & 0xC000;
|
|
|
|
|
|
|
|
if (!Objects[item->objectNumber].waterCreature)
|
|
|
|
{
|
|
|
|
if (item->roomNumber == NO_ROOM)
|
|
|
|
{
|
|
|
|
item->pos.xPos = LaraItem->pos.xPos;
|
|
|
|
item->pos.yPos = LaraItem->pos.yPos - 762;
|
|
|
|
item->pos.zPos = LaraItem->pos.zPos;
|
|
|
|
|
|
|
|
SoundEffect(cmd[1] & 0x3FFF, &item->pos, 2);
|
|
|
|
}
|
|
|
|
else if (g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER)
|
|
|
|
{
|
|
|
|
if (!flags || flags == SFX_WATERONLY && (g_Level.Rooms[Camera.pos.roomNumber].flags & ENV_FLAG_WATER || Objects[item->objectNumber].intelligent))
|
|
|
|
{
|
|
|
|
SoundEffect(cmd[1] & 0x3FFF, &item->pos, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!flags || flags == SFX_LANDONLY && !(g_Level.Rooms[Camera.pos.roomNumber].flags & ENV_FLAG_WATER))
|
|
|
|
{
|
|
|
|
SoundEffect(cmd[1] & 0x3FFF, &item->pos, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-09-18 01:40:06 +03:00
|
|
|
if (g_Level.Rooms[item->roomNumber].flags & ENV_FLAG_WATER)
|
2021-09-16 05:06:03 +03:00
|
|
|
SoundEffect(cmd[1] & 0x3FFF, &item->pos, 1);
|
|
|
|
else
|
|
|
|
SoundEffect(cmd[1] & 0x3FFF, &item->pos, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case COMMAND_EFFECT:
|
|
|
|
if (item->frameNumber != *cmd)
|
|
|
|
{
|
|
|
|
cmd += 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
FXType = cmd[1] & 0xC000;
|
|
|
|
effectID = cmd[1] & 0x3FFF;
|
|
|
|
DoFlipEffect(effectID, item);
|
|
|
|
|
|
|
|
cmd += 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int lateral = 0;
|
|
|
|
|
|
|
|
if (item->gravityStatus)
|
|
|
|
{
|
|
|
|
item->fallspeed += (item->fallspeed >= 128 ? 1 : 6);
|
|
|
|
item->pos.yPos += item->fallspeed;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int velocity = anim->velocity;
|
|
|
|
if (anim->acceleration)
|
|
|
|
velocity += anim->acceleration * (item->frameNumber - anim->frameBase);
|
|
|
|
item->speed = velocity >> 16;
|
|
|
|
|
|
|
|
lateral = anim->Xvelocity;
|
|
|
|
if (anim->Xacceleration)
|
|
|
|
lateral += anim->Xacceleration * (item->frameNumber - anim->frameBase);
|
|
|
|
|
|
|
|
lateral >>= 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
item->pos.xPos += item->speed * phd_sin(item->pos.yRot);
|
|
|
|
item->pos.zPos += item->speed * phd_cos(item->pos.yRot);
|
|
|
|
|
|
|
|
item->pos.xPos += lateral * phd_sin(item->pos.yRot + ANGLE(90));
|
|
|
|
item->pos.zPos += lateral * phd_cos(item->pos.yRot + ANGLE(90));
|
|
|
|
|
|
|
|
// Update matrices
|
|
|
|
short itemNumber = item - g_Level.Items.data();
|
|
|
|
g_Renderer.updateItemAnimations(itemNumber, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TranslateItem(ITEM_INFO* item, int x, int y, int z)
|
|
|
|
{
|
|
|
|
float c = phd_cos(item->pos.yRot);
|
|
|
|
float s = phd_sin(item->pos.yRot);
|
|
|
|
|
|
|
|
item->pos.xPos += c * x + s * z;
|
|
|
|
item->pos.yPos += y;
|
|
|
|
item->pos.zPos += -s * x + c * z;
|
|
|
|
}
|
|
|
|
|
|
|
|
int GetChange(ITEM_INFO* item, ANIM_STRUCT* anim)
|
|
|
|
{
|
|
|
|
if (item->currentAnimState == item->goalAnimState)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (anim->numberChanges <= 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < anim->numberChanges; i++)
|
|
|
|
{
|
|
|
|
CHANGE_STRUCT* change = &g_Level.Changes[anim->changeIndex + i];
|
|
|
|
if (change->goalAnimState == item->goalAnimState)
|
|
|
|
{
|
|
|
|
for (int j = 0; j < change->numberRanges; j++)
|
|
|
|
{
|
|
|
|
RANGE_STRUCT* range = &g_Level.Ranges[change->rangeIndex + j];
|
|
|
|
if (item->frameNumber >= range->startFrame && item->frameNumber <= range->endFrame)
|
|
|
|
{
|
|
|
|
item->animNumber = range->linkAnimNum;
|
|
|
|
item->frameNumber = range->linkFrameNum;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOUNDING_BOX* GetBoundsAccurate(ITEM_INFO* item)
|
|
|
|
{
|
|
|
|
int rate = 0;
|
|
|
|
ANIM_FRAME* framePtr[2];
|
|
|
|
|
|
|
|
int frac = GetFrame(item, framePtr, &rate);
|
|
|
|
if (frac == 0)
|
|
|
|
return &framePtr[0]->boundingBox;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
InterpolatedBounds.X1 = framePtr[0]->boundingBox.X1 + (framePtr[1]->boundingBox.X1 - framePtr[0]->boundingBox.X1) * frac / rate;
|
|
|
|
InterpolatedBounds.X2 = framePtr[0]->boundingBox.X2 + (framePtr[1]->boundingBox.X2 - framePtr[0]->boundingBox.X2) * frac / rate;
|
|
|
|
InterpolatedBounds.Y1 = framePtr[0]->boundingBox.Y1 + (framePtr[1]->boundingBox.Y1 - framePtr[0]->boundingBox.Y1) * frac / rate;
|
|
|
|
InterpolatedBounds.Y2 = framePtr[0]->boundingBox.Y2 + (framePtr[1]->boundingBox.Y2 - framePtr[0]->boundingBox.Y2) * frac / rate;
|
|
|
|
InterpolatedBounds.Z1 = framePtr[0]->boundingBox.Z1 + (framePtr[1]->boundingBox.Z1 - framePtr[0]->boundingBox.Z1) * frac / rate;
|
|
|
|
InterpolatedBounds.Z2 = framePtr[0]->boundingBox.Z2 + (framePtr[1]->boundingBox.Z2 - framePtr[0]->boundingBox.Z2) * frac / rate;
|
|
|
|
|
|
|
|
return &InterpolatedBounds;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ANIM_FRAME* GetBestFrame(ITEM_INFO* item)
|
|
|
|
{
|
|
|
|
int rate = 0;
|
|
|
|
ANIM_FRAME* framePtr[2];
|
|
|
|
|
|
|
|
int frac = GetFrame(item, framePtr, &rate);
|
|
|
|
|
|
|
|
if (frac <= (rate >> 1))
|
|
|
|
return framePtr[0];
|
|
|
|
else
|
|
|
|
return framePtr[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
int GetFrame(ITEM_INFO* item, ANIM_FRAME* framePtr[], int* rate)
|
|
|
|
{
|
|
|
|
ANIM_STRUCT *anim;
|
|
|
|
int frm;
|
|
|
|
int first, second;
|
|
|
|
int interp, rat;
|
|
|
|
|
|
|
|
frm = item->frameNumber;
|
|
|
|
anim = &g_Level.Anims[item->animNumber];
|
|
|
|
framePtr[0] = framePtr[1] = &g_Level.Frames[anim->framePtr];
|
|
|
|
rat = *rate = anim->interpolation & 0x00ff;
|
|
|
|
frm -= anim->frameBase;
|
|
|
|
first = frm / rat;
|
|
|
|
interp = frm % rat;
|
|
|
|
framePtr[0] += first; // Get Frame pointers
|
|
|
|
framePtr[1] = framePtr[0] + 1; // and store away
|
|
|
|
if (interp == 0)
|
|
|
|
return(0);
|
|
|
|
second = first * rat + rat;
|
|
|
|
if (second>anim->frameEnd) // Clamp KeyFrame to End if need be
|
|
|
|
*rate = anim->frameEnd - (second - rat);
|
|
|
|
return(interp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DrawAnimatingItem(ITEM_INFO* item)
|
|
|
|
{
|
|
|
|
// TODO: to refactor
|
|
|
|
// Empty stub because actually we disable items drawing when drawRoutine pointer is NULL in OBJECT_INFO
|
|
|
|
}
|
|
|
|
|
|
|
|
void GetLaraJointPosition(PHD_VECTOR* pos, int LM_enum)
|
|
|
|
{
|
|
|
|
if (LM_enum >= NUM_LARA_MESHES)
|
|
|
|
LM_enum = LM_HEAD;
|
|
|
|
|
|
|
|
Vector3 p = Vector3(pos->x, pos->y, pos->z);
|
|
|
|
g_Renderer.getLaraAbsBonePosition(&p, LM_enum);
|
|
|
|
|
|
|
|
pos->x = p.x;
|
|
|
|
pos->y = p.y;
|
|
|
|
pos->z = p.z;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClampRotation(PHD_3DPOS* pos, short angle, short rot)
|
|
|
|
{
|
|
|
|
if (angle <= rot)
|
|
|
|
{
|
|
|
|
if (angle >= -rot)
|
|
|
|
pos->yRot += angle;
|
|
|
|
else
|
|
|
|
pos->yRot -= rot;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pos->yRot += rot;
|
|
|
|
}
|
2021-09-19 18:32:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
short GF(short animIndex, short frameToStart)
|
|
|
|
{
|
|
|
|
return g_Level.Anims[animIndex].frameBase + frameToStart;
|
|
|
|
}
|
|
|
|
|
|
|
|
short GF2(short objectID, short animIndex, short frameToStart)
|
|
|
|
{
|
|
|
|
return g_Level.Anims[Objects[objectID].animIndex + animIndex].frameBase + frameToStart;
|
|
|
|
}
|