mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-28 15:57:59 +03:00
Merge pull request #733 from MontyTRC89/creature_entity_demagic
Creature entity demagic
This commit is contained in:
commit
926e44210a
179 changed files with 2581 additions and 2599 deletions
|
@ -475,7 +475,9 @@ BOUNDING_BOX* GetBoundsAccurate(ItemInfo* item)
|
|||
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;
|
||||
{
|
||||
return &InterpolatedBounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -271,10 +271,10 @@ bool TestWithGlobalCollisionBounds(ItemInfo* item, ItemInfo* laraItem, Collision
|
|||
{
|
||||
auto* framePtr = GetBestFrame(laraItem);
|
||||
|
||||
if (item->Pose.Position.y + GlobalCollisionBounds.Y2 <= laraItem->Pose.Position.y + framePtr->boundingBox.Y1)
|
||||
if ((item->Pose.Position.y + GlobalCollisionBounds.Y2) <= (laraItem->Pose.Position.y + framePtr->boundingBox.Y1))
|
||||
return false;
|
||||
|
||||
if (item->Pose.Position.y + GlobalCollisionBounds.Y1 >= framePtr->boundingBox.Y2)
|
||||
if ((item->Pose.Position.y + GlobalCollisionBounds.Y1) >= framePtr->boundingBox.Y2)
|
||||
return false;
|
||||
|
||||
float sinY = phd_sin(item->Pose.Orientation.y);
|
||||
|
@ -286,10 +286,10 @@ bool TestWithGlobalCollisionBounds(ItemInfo* item, ItemInfo* laraItem, Collision
|
|||
int x = (dx * cosY) - (dz * sinY);
|
||||
int z = (dz * cosY) + (dx * sinY);
|
||||
|
||||
if (x < GlobalCollisionBounds.X1 - coll->Setup.Radius ||
|
||||
x > GlobalCollisionBounds.X2 + coll->Setup.Radius ||
|
||||
z < GlobalCollisionBounds.Z1 - coll->Setup.Radius ||
|
||||
z > GlobalCollisionBounds.Z2 + coll->Setup.Radius)
|
||||
if (x < (GlobalCollisionBounds.X1 - coll->Setup.Radius) ||
|
||||
x > (GlobalCollisionBounds.X2 + coll->Setup.Radius) ||
|
||||
z < (GlobalCollisionBounds.Z1 - coll->Setup.Radius) ||
|
||||
z > (GlobalCollisionBounds.Z2 + coll->Setup.Radius))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -331,7 +331,7 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (phd_Distance(&item->Pose, &item2->Pose) < COLLISION_CHECK_DISTANCE)
|
||||
if (Vector3Int::Distance(item->Pose.Position, item2->Pose.Position) < COLLISION_CHECK_DISTANCE)
|
||||
{
|
||||
auto box = TO_DX_BBOX(item2->Pose, GetBoundsAccurate(item2));
|
||||
float distance;
|
||||
|
@ -346,16 +346,14 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll)
|
|||
itemNumber = item2->NextItem;
|
||||
}
|
||||
|
||||
for (int j = 0; j < g_Level.Rooms[i].mesh.size(); j++)
|
||||
for (auto& mesh : g_Level.Rooms[i].mesh)
|
||||
{
|
||||
auto* mesh = &g_Level.Rooms[i].mesh[j];
|
||||
|
||||
if (!(mesh->flags & StaticMeshFlags::SM_VISIBLE))
|
||||
if (!(mesh.flags & StaticMeshFlags::SM_VISIBLE))
|
||||
continue;
|
||||
|
||||
if (phd_Distance(&item->Pose, &mesh->pos) < COLLISION_CHECK_DISTANCE)
|
||||
if (phd_Distance(&item->Pose, &mesh.pos) < COLLISION_CHECK_DISTANCE)
|
||||
{
|
||||
auto box = TO_DX_BBOX(mesh->pos, GetBoundsAccurate(mesh, false));
|
||||
auto box = TO_DX_BBOX(mesh.pos, GetBoundsAccurate(&mesh, false));
|
||||
float distance;
|
||||
|
||||
if (box.Intersects(origin, direction, distance) && distance < coll->Setup.Radius * 2)
|
||||
|
@ -453,7 +451,7 @@ bool MoveLaraPosition(Vector3Int* offset, ItemInfo* item, ItemInfo* laraItem)
|
|||
{
|
||||
auto* lara = GetLaraInfo(laraItem);
|
||||
|
||||
Matrix matrix = Matrix::CreateFromYawPitchRoll(
|
||||
auto matrix = Matrix::CreateFromYawPitchRoll(
|
||||
TO_RAD(item->Pose.Orientation.y),
|
||||
TO_RAD(item->Pose.Orientation.x),
|
||||
TO_RAD(item->Pose.Orientation.z)
|
||||
|
@ -491,12 +489,12 @@ static bool ItemInRange(int x, int z, int radius)
|
|||
return ((SQUARE(x) + SQUARE(z)) <= SQUARE(radius));
|
||||
}
|
||||
|
||||
bool ItemNearLara(PHD_3DPOS* pos, int radius)
|
||||
bool ItemNearLara(Vector3Int* origin, int radius)
|
||||
{
|
||||
auto target = GameVector(
|
||||
pos->Position.x - LaraItem->Pose.Position.x,
|
||||
pos->Position.y - LaraItem->Pose.Position.y,
|
||||
pos->Position.z - LaraItem->Pose.Position.z
|
||||
origin->x - LaraItem->Pose.Position.x,
|
||||
origin->y - LaraItem->Pose.Position.y,
|
||||
origin->z - LaraItem->Pose.Position.z
|
||||
);
|
||||
|
||||
if (!ItemCollide(target.y, ITEM_RADIUS_YMAX))
|
||||
|
@ -515,9 +513,9 @@ bool ItemNearLara(PHD_3DPOS* pos, int radius)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ItemNearTarget(PHD_3DPOS* origin, ItemInfo* target, int radius)
|
||||
bool ItemNearTarget(Vector3Int* origin, ItemInfo* targetEntity, int radius)
|
||||
{
|
||||
auto pos = origin->Position - target->Pose.Position;
|
||||
auto pos = *origin - targetEntity->Pose.Position;
|
||||
|
||||
if (!ItemCollide(pos.y, ITEM_RADIUS_YMAX))
|
||||
return false;
|
||||
|
@ -528,31 +526,31 @@ bool ItemNearTarget(PHD_3DPOS* origin, ItemInfo* target, int radius)
|
|||
if (!ItemInRange(pos.x, pos.z, radius))
|
||||
return false;
|
||||
|
||||
auto* bounds = GetBoundsAccurate(target);
|
||||
auto* bounds = GetBoundsAccurate(targetEntity);
|
||||
if (pos.y >= bounds->Y1 && pos.y <= bounds->Y2)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Move3DPosTo3DPos(PHD_3DPOS* origin, PHD_3DPOS* target, int velocity, short angleAdd)
|
||||
bool Move3DPosTo3DPos(PHD_3DPOS* fromPose, PHD_3DPOS* toPose, int velocity, short angleAdd)
|
||||
{
|
||||
auto direction = target->Position - origin->Position;
|
||||
int distance = sqrt(SQUARE(direction.x) + SQUARE(direction.y) + SQUARE(direction.z));
|
||||
auto direction = toPose->Position - fromPose->Position;
|
||||
float distance = Vector3Int::Distance(fromPose->Position, toPose->Position);
|
||||
|
||||
if (velocity < distance)
|
||||
origin->Position += direction * velocity / distance;
|
||||
fromPose->Position += direction * (velocity / distance);
|
||||
else
|
||||
origin->Position = target->Position;
|
||||
fromPose->Position = toPose->Position;
|
||||
|
||||
if (!Lara.Control.IsMoving)
|
||||
{
|
||||
bool shouldAnimate = (distance - velocity) > (velocity * ANIMATED_ALIGNMENT_FRAME_COUNT_THRESHOLD);
|
||||
bool shouldAnimate = ((distance - velocity) > (velocity * ANIMATED_ALIGNMENT_FRAME_COUNT_THRESHOLD));
|
||||
|
||||
if (shouldAnimate && Lara.Control.WaterStatus != WaterStatus::Underwater)
|
||||
{
|
||||
int angle = mGetAngle(target->Position.x, target->Position.z, origin->Position.x, origin->Position.z);
|
||||
int direction = (GetQuadrant(angle) - GetQuadrant(target->Orientation.y)) & 3;
|
||||
int angle = mGetAngle(toPose->Position.x, toPose->Position.z, fromPose->Position.x, fromPose->Position.z);
|
||||
int direction = (GetQuadrant(angle) - GetQuadrant(toPose->Orientation.y)) & 3;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
|
@ -583,31 +581,31 @@ bool Move3DPosTo3DPos(PHD_3DPOS* origin, PHD_3DPOS* target, int velocity, short
|
|||
Lara.Control.Count.PositionAdjust = 0;
|
||||
}
|
||||
|
||||
short deltaAngle = target->Orientation.x - origin->Orientation.x;
|
||||
short deltaAngle = toPose->Orientation.x - fromPose->Orientation.x;
|
||||
if (deltaAngle > angleAdd)
|
||||
origin->Orientation.x += angleAdd;
|
||||
fromPose->Orientation.x += angleAdd;
|
||||
else if (deltaAngle < -angleAdd)
|
||||
origin->Orientation.x -= angleAdd;
|
||||
fromPose->Orientation.x -= angleAdd;
|
||||
else
|
||||
origin->Orientation.x = target->Orientation.x;
|
||||
fromPose->Orientation.x = toPose->Orientation.x;
|
||||
|
||||
deltaAngle = target->Orientation.y - origin->Orientation.y;
|
||||
deltaAngle = toPose->Orientation.y - fromPose->Orientation.y;
|
||||
if (deltaAngle > angleAdd)
|
||||
origin->Orientation.y += angleAdd;
|
||||
fromPose->Orientation.y += angleAdd;
|
||||
else if (deltaAngle < -angleAdd)
|
||||
origin->Orientation.y -= angleAdd;
|
||||
fromPose->Orientation.y -= angleAdd;
|
||||
else
|
||||
origin->Orientation.y = target->Orientation.y;
|
||||
fromPose->Orientation.y = toPose->Orientation.y;
|
||||
|
||||
deltaAngle = target->Orientation.z - origin->Orientation.z;
|
||||
deltaAngle = toPose->Orientation.z - fromPose->Orientation.z;
|
||||
if (deltaAngle > angleAdd)
|
||||
origin->Orientation.z += angleAdd;
|
||||
fromPose->Orientation.z += angleAdd;
|
||||
else if (deltaAngle < -angleAdd)
|
||||
origin->Orientation.z -= angleAdd;
|
||||
fromPose->Orientation.z -= angleAdd;
|
||||
else
|
||||
origin->Orientation.z = target->Orientation.z;
|
||||
fromPose->Orientation.z = toPose->Orientation.z;
|
||||
|
||||
return (origin->Position == target->Position && origin->Orientation == target->Orientation);
|
||||
return (fromPose->Position == toPose->Position && fromPose->Orientation == toPose->Orientation);
|
||||
}
|
||||
|
||||
bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius)
|
||||
|
@ -626,13 +624,13 @@ bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius)
|
|||
|
||||
int x = laraItem->Pose.Position.x - item->Pose.Position.x;
|
||||
int z = laraItem->Pose.Position.z - item->Pose.Position.z;
|
||||
int dx = (cosY * x) - (sinY * z);
|
||||
int dz = (cosY * z) + (sinY * x);
|
||||
int dx = (x * cosY) - (z * sinY);
|
||||
int dz = (z * cosY) + (x * sinY);
|
||||
|
||||
if (dx >= bounds->X1 - radius &&
|
||||
dx <= radius + bounds->X2 &&
|
||||
dz >= bounds->Z1 - radius &&
|
||||
dz <= radius + bounds->Z2)
|
||||
if (dx >= (bounds->X1 - radius) &&
|
||||
dx <= (radius + bounds->X2) &&
|
||||
dz >= (bounds->Z1 - radius) &&
|
||||
dz <= (radius + bounds->Z2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -688,11 +686,7 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa
|
|||
int rx = (dx * cosY) - (dz * sinY);
|
||||
int rz = (dz * cosY) + (dx * sinY);
|
||||
|
||||
BOUNDING_BOX* bounds;
|
||||
if (bigPush & 2)
|
||||
bounds = &GlobalCollisionBounds;
|
||||
else
|
||||
bounds = (BOUNDING_BOX*)GetBestFrame(item);
|
||||
auto* bounds = (bigPush & 2) ? &GlobalCollisionBounds : (BOUNDING_BOX*)GetBestFrame(item);
|
||||
|
||||
int minX = bounds->X1;
|
||||
int maxX = bounds->X2;
|
||||
|
@ -729,8 +723,8 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa
|
|||
else
|
||||
rz -= bottom;
|
||||
|
||||
item2->Pose.Position.x = item->Pose.Position.x + cosY * rx + sinY * rz;
|
||||
item2->Pose.Position.z = item->Pose.Position.z + cosY * rz - sinY * rx;
|
||||
item2->Pose.Position.x = item->Pose.Position.x + (rx * cosY) + (rz * sinY);
|
||||
item2->Pose.Position.z = item->Pose.Position.z + (rz * cosY) - (rx * sinY);
|
||||
|
||||
auto* lara = item2->IsLara() ? GetLaraInfo(item2) : nullptr;
|
||||
|
||||
|
@ -755,12 +749,12 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa
|
|||
coll->Setup.LowerCeilingBound = 0;
|
||||
coll->Setup.UpperCeilingBound = MAX_HEIGHT;
|
||||
|
||||
auto facing = coll->Setup.ForwardAngle;
|
||||
auto headingAngle = coll->Setup.ForwardAngle;
|
||||
coll->Setup.ForwardAngle = phd_atan(item2->Pose.Position.z - coll->Setup.OldPosition.z, item2->Pose.Position.x - coll->Setup.OldPosition.x);
|
||||
|
||||
GetCollisionInfo(coll, item2);
|
||||
|
||||
coll->Setup.ForwardAngle = facing;
|
||||
coll->Setup.ForwardAngle = headingAngle;
|
||||
|
||||
if (coll->CollisionType == CT_NONE)
|
||||
{
|
||||
|
@ -778,8 +772,8 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa
|
|||
// If Lara is in the process of aligning to an object, cancel it.
|
||||
if (lara != nullptr && lara->Control.Count.PositionAdjust > (LARA_POSITION_ADJUST_MAX_TIME / 6))
|
||||
{
|
||||
Lara.Control.IsMoving = false;
|
||||
Lara.Control.HandStatus = HandStatus::Free;
|
||||
lara->Control.IsMoving = false;
|
||||
lara->Control.HandStatus = HandStatus::Free;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -868,7 +862,7 @@ void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll)
|
|||
{
|
||||
auto* mesh = &g_Level.Rooms[i].mesh[j];
|
||||
|
||||
// Only process meshes which are visible and solid
|
||||
// Only process meshes which are visible and solid.
|
||||
if ((mesh->flags & StaticMeshFlags::SM_VISIBLE) && (mesh->flags & StaticMeshFlags::SM_SOLID))
|
||||
{
|
||||
if (phd_Distance(&item->Pose, &mesh->pos) < COLLISION_CHECK_DISTANCE)
|
||||
|
@ -882,39 +876,39 @@ void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll)
|
|||
}
|
||||
}
|
||||
|
||||
bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, CollisionInfo* coll)
|
||||
bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pose, CollisionInfo* coll)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
// Get DX static bounds in global coords
|
||||
auto staticBounds = TO_DX_BBOX(pos, box);
|
||||
// Get DX static bounds in global coordinates.
|
||||
auto staticBounds = TO_DX_BBOX(pose, box);
|
||||
|
||||
// Get local TR bounds and DX item bounds in global coords
|
||||
// Get local TR bounds and DX item bounds in global coordinates.
|
||||
auto itemBBox = GetBoundsAccurate(item);
|
||||
auto itemBounds = TO_DX_BBOX(item->Pose, itemBBox);
|
||||
|
||||
// Extend bounds a bit for visual testing
|
||||
itemBounds.Extents = itemBounds.Extents + Vector3(WALL_SIZE);
|
||||
// Extend bounds a bit for visual testing.
|
||||
itemBounds.Extents = itemBounds.Extents + Vector3(SECTOR(1));
|
||||
|
||||
// Filter out any further checks if static isn't nearby
|
||||
// Filter out any further checks if static isn't nearby.
|
||||
if (!staticBounds.Intersects(itemBounds))
|
||||
return false;
|
||||
|
||||
// Bring back extents
|
||||
itemBounds.Extents = itemBounds.Extents - Vector3(WALL_SIZE);
|
||||
// Bring back extents.
|
||||
itemBounds.Extents = itemBounds.Extents - Vector3(SECTOR(1));
|
||||
|
||||
// Draw static bounds
|
||||
// Draw static bounds.
|
||||
g_Renderer.AddDebugBox(staticBounds, Vector4(1, 0.3f, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
||||
|
||||
// Calculate horizontal item coll bounds according to radius
|
||||
// Calculate horizontal item collision bounds according to radius.
|
||||
BOUNDING_BOX collBox;
|
||||
collBox.X1 = -coll->Setup.Radius;
|
||||
collBox.X2 = coll->Setup.Radius;
|
||||
collBox.Z1 = -coll->Setup.Radius;
|
||||
collBox.Z2 = coll->Setup.Radius;
|
||||
|
||||
// Calculate vertical item coll bounds according to either height (land mode) or precise bounds (water mode).
|
||||
// Water mode needs special processing because height calc in original engines is inconsistent in such cases.
|
||||
// Calculate vertical item collision bounds according to either height (land mode) or precise bounds (water mode).
|
||||
// Water mode needs special processing because height calculation in original engines is inconsistent in such cases.
|
||||
if (TestEnvironment(ENV_FLAG_WATER, item))
|
||||
{
|
||||
collBox.Y1 = itemBBox->Y1;
|
||||
|
@ -926,18 +920,18 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis
|
|||
collBox.Y2 = 0;
|
||||
}
|
||||
|
||||
// Get and test DX item coll bounds
|
||||
// Get and test DX item collision bounds.
|
||||
auto collBounds = TO_DX_BBOX(PHD_3DPOS(item->Pose.Position), &collBox);
|
||||
bool intersects = staticBounds.Intersects(collBounds);
|
||||
|
||||
// Check if previous item horizontal position intersects bounds
|
||||
// Check if previous item horizontal position intersects bounds.
|
||||
auto oldCollBounds = TO_DX_BBOX(PHD_3DPOS(coll->Setup.OldPosition.x, item->Pose.Position.y, coll->Setup.OldPosition.z), &collBox);
|
||||
bool oldHorIntersects = staticBounds.Intersects(oldCollBounds);
|
||||
|
||||
// Draw item coll bounds
|
||||
// Draw item coll bounds.
|
||||
g_Renderer.AddDebugBox(collBounds, intersects ? Vector4(1, 0, 0, 1) : Vector4(0, 1, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS);
|
||||
|
||||
// Decompose static bounds into top/bottom plane vertices
|
||||
// Decompose static bounds into top/bottom plane vertices.
|
||||
Vector3 corners[8];
|
||||
staticBounds.GetCorners(corners);
|
||||
Vector3 planeVertices[4][3] =
|
||||
|
@ -948,7 +942,7 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis
|
|||
{ corners[3], corners[6], corners[2] }
|
||||
};
|
||||
|
||||
// Determine collision box vertical dimensions
|
||||
// Determine collision box vertical dimensions.
|
||||
auto height = collBox.Height();
|
||||
auto center = item->Pose.Position.y - height / 2;
|
||||
|
||||
|
@ -960,24 +954,23 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis
|
|||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// Calculate ray direction
|
||||
// Calculate ray direction.
|
||||
auto mxR = Matrix::CreateFromYawPitchRoll(TO_RAD(item->Pose.Orientation.y), TO_RAD(item->Pose.Orientation.x + (ANGLE(90 * i))), 0);
|
||||
auto mxT = Matrix::CreateTranslation(Vector3::UnitY);
|
||||
auto direction = (mxT * mxR).Translation();
|
||||
|
||||
// Make a ray and do ray tests against all decomposed planes
|
||||
// Make a ray and do ray tests against all decomposed planes.
|
||||
auto ray = Ray(collBounds.Center, direction);
|
||||
|
||||
// Determine if top/bottom planes are closest ones or not
|
||||
// Determine if top/bottom planes are closest ones or not.
|
||||
for (int p = 0; p < 4; p++)
|
||||
{
|
||||
// No plane intersection, quickly discard
|
||||
// No plane intersection, quickly discard.
|
||||
float d = 0.0f;
|
||||
if (!ray.Intersects(planeVertices[p][0], planeVertices[p][1], planeVertices[p][2], d))
|
||||
continue;
|
||||
|
||||
// Process plane intersection only if distance is smaller
|
||||
// than already found minimum
|
||||
// Process plane intersection only if distance is smaller than already found minimum.
|
||||
if (d < minDistance)
|
||||
{
|
||||
closestRay = ray;
|
||||
|
@ -987,24 +980,25 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis
|
|||
}
|
||||
}
|
||||
|
||||
if (closestPlane != -1) // Top/bottom plane found
|
||||
// Top/bottom plane found.
|
||||
if (closestPlane != -1)
|
||||
{
|
||||
auto bottom = closestPlane >= 2;
|
||||
auto yPoint = abs((closestRay.direction * minDistance).y);
|
||||
auto distanceToVerticalPlane = height / 2 - yPoint;
|
||||
|
||||
// Correct position according to top/bottom bounds, if collided and plane is nearby
|
||||
// Correct position according to top/bottom bounds, if collided and plane is nearby.
|
||||
if (intersects && oldHorIntersects && minDistance < height)
|
||||
{
|
||||
if (bottom)
|
||||
{
|
||||
// HACK: additionally subtract 2 from bottom plane, or else false positives may occur.
|
||||
// HACK: Additionally subtract 2 from bottom plane, otherwise false positives may occur.
|
||||
item->Pose.Position.y += distanceToVerticalPlane + 2;
|
||||
coll->CollisionType = CT_TOP;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set collision type only if dry room (in water rooms it causes stucking)
|
||||
// Set collision type only if dry room (in water rooms the player can get stuck).
|
||||
item->Pose.Position.y -= distanceToVerticalPlane;
|
||||
coll->CollisionType = (g_Level.Rooms[item->RoomNumber].flags & 1) ? coll->CollisionType : CT_CLAMP;
|
||||
}
|
||||
|
@ -1020,29 +1014,29 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis
|
|||
if (!intersects)
|
||||
return false;
|
||||
|
||||
// Check if bounds still collide after top/bottom position correction
|
||||
// Check if bounds still collide after top/bottom position correction.
|
||||
if (!staticBounds.Intersects(TO_DX_BBOX(PHD_3DPOS(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z), &collBox)))
|
||||
return result;
|
||||
|
||||
// Determine identity rotation/distance
|
||||
auto distance = Vector3(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z) - Vector3(pos.Position.x, pos.Position.y, pos.Position.z);
|
||||
auto sinY = phd_sin(pos.Orientation.y);
|
||||
auto cosY = phd_cos(pos.Orientation.y);
|
||||
// Determine identity orientation/distance.
|
||||
auto distance = (item->Pose.Position - pose.Position).ToVector3();
|
||||
auto sinY = phd_sin(pose.Orientation.y);
|
||||
auto cosY = phd_cos(pose.Orientation.y);
|
||||
|
||||
// Rotate item to collision bounds identity
|
||||
auto x = round(distance.x * cosY - distance.z * sinY) + pos.Position.x;
|
||||
// Rotate item to collision bounds identity.
|
||||
auto x = round((distance.x * cosY) - (distance.z * sinY)) + pose.Position.x;
|
||||
auto y = item->Pose.Position.y;
|
||||
auto z = round(distance.x * sinY + distance.z * cosY) + pos.Position.z;
|
||||
auto z = round((distance.x * sinY) + (distance.z * cosY)) + pose.Position.z;
|
||||
|
||||
// Determine identity static collision bounds
|
||||
auto XMin = pos.Position.x + box->X1;
|
||||
auto XMax = pos.Position.x + box->X2;
|
||||
auto YMin = pos.Position.y + box->Y1;
|
||||
auto YMax = pos.Position.y + box->Y2;
|
||||
auto ZMin = pos.Position.z + box->Z1;
|
||||
auto ZMax = pos.Position.z + box->Z2;
|
||||
// Determine identity static collision bounds.
|
||||
auto XMin = pose.Position.x + box->X1;
|
||||
auto XMax = pose.Position.x + box->X2;
|
||||
auto YMin = pose.Position.y + box->Y1;
|
||||
auto YMax = pose.Position.y + box->Y2;
|
||||
auto ZMin = pose.Position.z + box->Z1;
|
||||
auto ZMax = pose.Position.z + box->Z2;
|
||||
|
||||
// Determine item collision bounds
|
||||
// Determine item collision bounds.
|
||||
auto inXMin = x + collBox.X1;
|
||||
auto inXMax = x + collBox.X2;
|
||||
auto inYMin = y + collBox.Y1;
|
||||
|
@ -1050,15 +1044,15 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis
|
|||
auto inZMin = z + collBox.Z1;
|
||||
auto inZMax = z + collBox.Z2;
|
||||
|
||||
// Don't calculate shifts if not in bounds
|
||||
// Don't calculate shifts if not in bounds.
|
||||
if (inXMax <= XMin || inXMin >= XMax ||
|
||||
inYMax <= YMin || inYMin >= YMax ||
|
||||
inZMax <= ZMin || inZMin >= ZMax)
|
||||
return result;
|
||||
|
||||
// Calculate shifts
|
||||
// Calculate shifts.
|
||||
|
||||
Vector3Int rawShift = {};
|
||||
auto rawShift = Vector3Int::Zero;
|
||||
|
||||
auto shiftLeft = inXMax - XMin;
|
||||
auto shiftRight = XMax - inXMin;
|
||||
|
@ -1076,13 +1070,13 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis
|
|||
else
|
||||
rawShift.z = shiftRight;
|
||||
|
||||
// Rotate previous collision position to identity
|
||||
distance = Vector3(coll->Setup.OldPosition.x, coll->Setup.OldPosition.y, coll->Setup.OldPosition.z) - Vector3(pos.Position.x, pos.Position.y, pos.Position.z);
|
||||
auto ox = round(distance.x * cosY - distance.z * sinY) + pos.Position.x;
|
||||
auto oz = round(distance.x * sinY + distance.z * cosY) + pos.Position.z;
|
||||
// Rotate previous collision position to identity.
|
||||
distance = (coll->Setup.OldPosition - pose.Position).ToVector3();
|
||||
auto ox = round((distance.x * cosY) - (distance.z * sinY)) + pose.Position.x;
|
||||
auto oz = round((distance.x * sinY) + (distance.z * cosY)) + pose.Position.z;
|
||||
|
||||
// Calculate collisison type based on identity rotation
|
||||
switch (GetQuadrant(coll->Setup.ForwardAngle - pos.Orientation.y))
|
||||
// Calculate collisison type based on identity orientation.
|
||||
switch (GetQuadrant(coll->Setup.ForwardAngle - pose.Orientation.y))
|
||||
{
|
||||
case NORTH:
|
||||
if (rawShift.x > coll->Setup.Radius || rawShift.x < -coll->Setup.Radius)
|
||||
|
@ -1173,19 +1167,19 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis
|
|||
break;
|
||||
}
|
||||
|
||||
// Determine final shifts rotation/distance
|
||||
distance = Vector3(x + coll->Shift.x, y, z + coll->Shift.z) - Vector3(pos.Position.x, pos.Position.y, pos.Position.z);
|
||||
sinY = phd_sin(-pos.Orientation.y);
|
||||
cosY = phd_cos(-pos.Orientation.y);
|
||||
// Determine final shifts orientation/distance.
|
||||
distance = Vector3(x + coll->Shift.x, y, z + coll->Shift.z) - pose.Position.ToVector3();
|
||||
sinY = phd_sin(-pose.Orientation.y);
|
||||
cosY = phd_cos(-pose.Orientation.y);
|
||||
|
||||
// Calculate final shifts rotation/distance
|
||||
coll->Shift.x = (round(distance.x * cosY - distance.z * sinY) + pos.Position.x) - item->Pose.Position.x;
|
||||
coll->Shift.z = (round(distance.x * sinY + distance.z * cosY) + pos.Position.z) - item->Pose.Position.z;
|
||||
// Calculate final shifts orientation/distance.
|
||||
coll->Shift.x = (round((distance.x * cosY) - (distance.z * sinY)) + pose.Position.x) - item->Pose.Position.x;
|
||||
coll->Shift.z = (round((distance.x * sinY) + (distance.z * cosY)) + pose.Position.z) - item->Pose.Position.z;
|
||||
|
||||
if (coll->Shift.x == 0 && coll->Shift.z == 0)
|
||||
coll->CollisionType = CT_NONE; // Paranoid
|
||||
coll->CollisionType = CT_NONE; // Paranoid.
|
||||
|
||||
// Set splat state flag if item is Lara and bounds are taller than Lara's headroom
|
||||
// Set splat state flag if item is Lara and bounds are taller than Lara's headroom.
|
||||
if (item == LaraItem && coll->CollisionType == CT_FRONT)
|
||||
coll->HitTallObject = (YMin <= inYMin + LARA_HEADROOM);
|
||||
|
||||
|
@ -1197,39 +1191,39 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
||||
auto prevCollResult = GetCollision(x, y, z, item->RoomNumber);
|
||||
auto collResult = GetCollision(item);
|
||||
auto prevPointProbe = GetCollision(x, y, z, item->RoomNumber);
|
||||
auto pointProbe = GetCollision(item);
|
||||
|
||||
auto* bounds = GetBoundsAccurate(item);
|
||||
int radius = bounds->Height();
|
||||
|
||||
item->Pose.Position.y += radius;
|
||||
|
||||
if (item->Pose.Position.y >= collResult.Position.Floor)
|
||||
if (item->Pose.Position.y >= pointProbe.Position.Floor)
|
||||
{
|
||||
int bs = 0;
|
||||
|
||||
if (collResult.Position.FloorSlope && prevCollResult.Position.Floor < collResult.Position.Floor)
|
||||
if (pointProbe.Position.FloorSlope && prevPointProbe.Position.Floor < pointProbe.Position.Floor)
|
||||
{
|
||||
int yAngle = (long)((unsigned short)item->Pose.Orientation.y);
|
||||
|
||||
if (collResult.FloorTilt.x < 0)
|
||||
if (pointProbe.FloorTilt.x < 0)
|
||||
{
|
||||
if (yAngle >= ANGLE(180.0f))
|
||||
bs = 1;
|
||||
}
|
||||
else if (collResult.FloorTilt.x > 0)
|
||||
else if (pointProbe.FloorTilt.x > 0)
|
||||
{
|
||||
if (yAngle <= ANGLE(180.0f))
|
||||
bs = 1;
|
||||
}
|
||||
|
||||
if (collResult.FloorTilt.y < 0)
|
||||
if (pointProbe.FloorTilt.y < 0)
|
||||
{
|
||||
if (yAngle >= ANGLE(90.0f) && yAngle <= ANGLE(270.0f))
|
||||
bs = 1;
|
||||
}
|
||||
else if (collResult.FloorTilt.y > 0)
|
||||
else if (pointProbe.FloorTilt.y > 0)
|
||||
{
|
||||
if (yAngle <= ANGLE(90.0f) || yAngle >= ANGLE(270.0f))
|
||||
bs = 1;
|
||||
|
@ -1238,7 +1232,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
|
||||
// If last position of item was also below this floor height, we've hit a wall, else we've hit a floor.
|
||||
|
||||
if (y > (collResult.Position.Floor + 32) && bs == 0 &&
|
||||
if (y > (pointProbe.Position.Floor + 32) && bs == 0 &&
|
||||
(((x / SECTOR(1)) != (item->Pose.Position.x / SECTOR(1))) ||
|
||||
((z / SECTOR(1)) != (item->Pose.Position.z / SECTOR(1)))))
|
||||
{
|
||||
|
@ -1246,8 +1240,8 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
|
||||
long xs;
|
||||
|
||||
if ((x & (~(WALL_SIZE - 1))) != (item->Pose.Position.x & (~(WALL_SIZE - 1))) && // X crossed boundary?
|
||||
(z & (~(WALL_SIZE - 1))) != (item->Pose.Position.z & (~(WALL_SIZE - 1)))) // Z crossed boundary as well?
|
||||
if ((x & (~WALL_MASK)) != (item->Pose.Position.x & (~WALL_MASK)) && // X crossed boundary?
|
||||
(z & (~WALL_MASK)) != (item->Pose.Position.z & (~WALL_MASK))) // Z crossed boundary as well?
|
||||
{
|
||||
if (abs(x - item->Pose.Position.x) < abs(z - item->Pose.Position.z))
|
||||
xs = 1; // X has travelled the shortest, so (maybe) hit first. (Seems to work ok).
|
||||
|
@ -1257,7 +1251,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
else
|
||||
xs = 1;
|
||||
|
||||
if ((x & (~(WALL_SIZE - 1))) != (item->Pose.Position.x & (~(WALL_SIZE - 1))) && xs) // X crossed boundary?
|
||||
if ((x & (~WALL_MASK)) != (item->Pose.Position.x & (~WALL_MASK)) && xs) // X crossed boundary?
|
||||
{
|
||||
// Hit angle = ANGLE(270.0f).
|
||||
if (xv <= 0)
|
||||
|
@ -1278,14 +1272,14 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
item->Pose.Position.z = z;
|
||||
}
|
||||
// Hit a steep slope?
|
||||
else if (collResult.Position.FloorSlope)
|
||||
else if (pointProbe.Position.FloorSlope)
|
||||
{
|
||||
// Need to know which direction the slope is.
|
||||
|
||||
item->Animation.Velocity.z -= (item->Animation.Velocity.z / 4);
|
||||
|
||||
// Hit angle = ANGLE(90.0f)
|
||||
if (collResult.FloorTilt.x < 0 && ((abs(collResult.FloorTilt.x)) - (abs(collResult.FloorTilt.y)) >= 2))
|
||||
if (pointProbe.FloorTilt.x < 0 && ((abs(pointProbe.FloorTilt.x)) - (abs(pointProbe.FloorTilt.y)) >= 2))
|
||||
{
|
||||
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(180.0f))
|
||||
{
|
||||
|
@ -1297,7 +1291,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
{
|
||||
if (item->Animation.Velocity.z < 32)
|
||||
{
|
||||
item->Animation.Velocity.z -= collResult.FloorTilt.x * 2;
|
||||
item->Animation.Velocity.z -= pointProbe.FloorTilt.x * 2;
|
||||
if ((unsigned short)item->Pose.Orientation.y > ANGLE(90.0f) && (unsigned short)item->Pose.Orientation.y < ANGLE(270.0f))
|
||||
{
|
||||
item->Pose.Orientation.y -= ANGLE(22.5f);
|
||||
|
@ -1319,7 +1313,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
}
|
||||
}
|
||||
// Hit angle = ANGLE(270.0f)
|
||||
else if (collResult.FloorTilt.x > 0 && ((abs(collResult.FloorTilt.x)) - (abs(collResult.FloorTilt.y)) >= 2))
|
||||
else if (pointProbe.FloorTilt.x > 0 && ((abs(pointProbe.FloorTilt.x)) - (abs(pointProbe.FloorTilt.y)) >= 2))
|
||||
{
|
||||
if (((unsigned short)item->Pose.Orientation.y) < ANGLE(180.0f))
|
||||
{
|
||||
|
@ -1331,7 +1325,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
{
|
||||
if (item->Animation.Velocity.z < 32)
|
||||
{
|
||||
item->Animation.Velocity.z += collResult.FloorTilt.x * 2;
|
||||
item->Animation.Velocity.z += pointProbe.FloorTilt.x * 2;
|
||||
if ((unsigned short)item->Pose.Orientation.y > ANGLE(270.0f) || (unsigned short)item->Pose.Orientation.y < ANGLE(90.0f))
|
||||
{
|
||||
item->Pose.Orientation.y -= ANGLE(22.5f);
|
||||
|
@ -1353,7 +1347,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
}
|
||||
}
|
||||
// Hit angle = 0
|
||||
else if (collResult.FloorTilt.y < 0 && ((abs(collResult.FloorTilt.y)) - (abs(collResult.FloorTilt.x)) >= 2))
|
||||
else if (pointProbe.FloorTilt.y < 0 && ((abs(pointProbe.FloorTilt.y)) - (abs(pointProbe.FloorTilt.x)) >= 2))
|
||||
{
|
||||
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(90.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(270.0f))
|
||||
{
|
||||
|
@ -1365,7 +1359,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
{
|
||||
if (item->Animation.Velocity.z < 32)
|
||||
{
|
||||
item->Animation.Velocity.z -= collResult.FloorTilt.y * 2;
|
||||
item->Animation.Velocity.z -= pointProbe.FloorTilt.y * 2;
|
||||
|
||||
if ((unsigned short)item->Pose.Orientation.y < ANGLE(180.0f))
|
||||
{
|
||||
|
@ -1388,7 +1382,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
}
|
||||
}
|
||||
// Hit angle = ANGLE(180.0f)
|
||||
else if (collResult.FloorTilt.y > 0 && ((abs(collResult.FloorTilt.y)) - (abs(collResult.FloorTilt.x)) >= 2))
|
||||
else if (pointProbe.FloorTilt.y > 0 && ((abs(pointProbe.FloorTilt.y)) - (abs(pointProbe.FloorTilt.x)) >= 2))
|
||||
{
|
||||
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(270.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(90.0f))
|
||||
{
|
||||
|
@ -1400,7 +1394,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
{
|
||||
if (item->Animation.Velocity.z < 32)
|
||||
{
|
||||
item->Animation.Velocity.z += collResult.FloorTilt.y * 2;
|
||||
item->Animation.Velocity.z += pointProbe.FloorTilt.y * 2;
|
||||
|
||||
if ((unsigned short)item->Pose.Orientation.y > ANGLE(180.0f))
|
||||
{
|
||||
|
@ -1422,7 +1416,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
item->Animation.Velocity.y = 0;
|
||||
}
|
||||
}
|
||||
else if (collResult.FloorTilt.x < 0 && collResult.FloorTilt.y < 0) // Hit angle = 0x2000
|
||||
else if (pointProbe.FloorTilt.x < 0 && pointProbe.FloorTilt.y < 0) // Hit angle = 0x2000
|
||||
{
|
||||
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(135.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(315.0f))
|
||||
{
|
||||
|
@ -1434,7 +1428,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
{
|
||||
if (item->Animation.Velocity.z < 32)
|
||||
{
|
||||
item->Animation.Velocity.z += -collResult.FloorTilt.x + -collResult.FloorTilt.y;
|
||||
item->Animation.Velocity.z += -pointProbe.FloorTilt.x + -pointProbe.FloorTilt.y;
|
||||
if ((unsigned short)item->Pose.Orientation.y > ANGLE(45.0f) && (unsigned short)item->Pose.Orientation.y < ANGLE(225.0f))
|
||||
{
|
||||
item->Pose.Orientation.y -= ANGLE(22.5f);
|
||||
|
@ -1456,7 +1450,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
}
|
||||
}
|
||||
// Hit angle = ANGLE(135.0f)
|
||||
else if (collResult.FloorTilt.x < 0 && collResult.FloorTilt.y > 0)
|
||||
else if (pointProbe.FloorTilt.x < 0 && pointProbe.FloorTilt.y > 0)
|
||||
{
|
||||
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(225.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(45.0f))
|
||||
{
|
||||
|
@ -1468,7 +1462,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
{
|
||||
if (item->Animation.Velocity.z < 32)
|
||||
{
|
||||
item->Animation.Velocity.z += (-collResult.FloorTilt.x) + collResult.FloorTilt.y;
|
||||
item->Animation.Velocity.z += (-pointProbe.FloorTilt.x) + pointProbe.FloorTilt.y;
|
||||
if ((unsigned short)item->Pose.Orientation.y < ANGLE(315.0f) && (unsigned short)item->Pose.Orientation.y > ANGLE(135.0f))
|
||||
{
|
||||
item->Pose.Orientation.y -= ANGLE(22.5f);
|
||||
|
@ -1490,7 +1484,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
}
|
||||
}
|
||||
// Hit angle = ANGLE(225.5f)
|
||||
else if (collResult.FloorTilt.x > 0 && collResult.FloorTilt.y > 0)
|
||||
else if (pointProbe.FloorTilt.x > 0 && pointProbe.FloorTilt.y > 0)
|
||||
{
|
||||
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(315.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(135.0f))
|
||||
{
|
||||
|
@ -1502,7 +1496,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
{
|
||||
if (item->Animation.Velocity.z < 32)
|
||||
{
|
||||
item->Animation.Velocity.z += collResult.FloorTilt.x + collResult.FloorTilt.y;
|
||||
item->Animation.Velocity.z += pointProbe.FloorTilt.x + pointProbe.FloorTilt.y;
|
||||
if ((unsigned short)item->Pose.Orientation.y < ANGLE(45.0f) || (unsigned short)item->Pose.Orientation.y > ANGLE(225.5f))
|
||||
{
|
||||
item->Pose.Orientation.y -= ANGLE(22.5f);
|
||||
|
@ -1524,7 +1518,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
}
|
||||
}
|
||||
// Hit angle = ANGLE(315.0f)
|
||||
else if (collResult.FloorTilt.x > 0 && collResult.FloorTilt.y < 0)
|
||||
else if (pointProbe.FloorTilt.x > 0 && pointProbe.FloorTilt.y < 0)
|
||||
{
|
||||
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(45.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(225.5f))
|
||||
{
|
||||
|
@ -1536,7 +1530,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
{
|
||||
if (item->Animation.Velocity.z < 32)
|
||||
{
|
||||
item->Animation.Velocity.z += collResult.FloorTilt.x + (-collResult.FloorTilt.y);
|
||||
item->Animation.Velocity.z += pointProbe.FloorTilt.x + (-pointProbe.FloorTilt.y);
|
||||
if ((unsigned short)item->Pose.Orientation.y < ANGLE(135.0f) || (unsigned short)item->Pose.Orientation.y > ANGLE(315.0f))
|
||||
{
|
||||
item->Pose.Orientation.y -= ANGLE(22.5f);
|
||||
|
@ -1558,7 +1552,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
}
|
||||
}
|
||||
|
||||
// Put item back in its last position.
|
||||
// Move item back to its previous position.
|
||||
item->Pose.Position.x = x;
|
||||
item->Pose.Position.y = y;
|
||||
item->Pose.Position.z = z;
|
||||
|
@ -1597,7 +1591,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
}
|
||||
}
|
||||
|
||||
item->Pose.Position.y = collResult.Position.Floor;
|
||||
item->Pose.Position.y = pointProbe.Position.Floor;
|
||||
}
|
||||
}
|
||||
// Check for on top of object.
|
||||
|
@ -1605,8 +1599,8 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
{
|
||||
if (yv >= 0)
|
||||
{
|
||||
prevCollResult = GetCollision(item->Pose.Position.x, y, item->Pose.Position.z, item->RoomNumber);
|
||||
collResult = GetCollision(item);
|
||||
prevPointProbe = GetCollision(item->Pose.Position.x, y, item->Pose.Position.z, item->RoomNumber);
|
||||
pointProbe = GetCollision(item);
|
||||
|
||||
// Bounce off floor.
|
||||
|
||||
|
@ -1614,7 +1608,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
// was always set to 0 by GetHeight() function which was called before the check.
|
||||
// Possibly a mistake or unfinished feature by Core? -- Lwmte, 27.08.21
|
||||
|
||||
if (item->Pose.Position.y >= prevCollResult.Position.Floor)
|
||||
if (item->Pose.Position.y >= prevPointProbe.Position.Floor)
|
||||
{
|
||||
// Hit the floor; bounce and slow down.
|
||||
if (item->Animation.Velocity.y > 0)
|
||||
|
@ -1648,17 +1642,17 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
}
|
||||
}
|
||||
|
||||
item->Pose.Position.y = prevCollResult.Position.Floor;
|
||||
item->Pose.Position.y = prevPointProbe.Position.Floor;
|
||||
}
|
||||
}
|
||||
// else
|
||||
{
|
||||
// Bounce off ceiling.
|
||||
collResult = GetCollision(item);
|
||||
pointProbe = GetCollision(item);
|
||||
|
||||
if (item->Pose.Position.y < collResult.Position.Ceiling)
|
||||
if (item->Pose.Position.y < pointProbe.Position.Ceiling)
|
||||
{
|
||||
if (y < collResult.Position.Ceiling &&
|
||||
if (y < pointProbe.Position.Ceiling &&
|
||||
(((x / SECTOR(1)) != (item->Pose.Position.x / SECTOR(1))) ||
|
||||
((z / SECTOR(1)) != (item->Pose.Position.z / SECTOR(1)))))
|
||||
{
|
||||
|
@ -1681,13 +1675,13 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
else
|
||||
item->Animation.Velocity.z /= 2;
|
||||
|
||||
// Put item back in its last position.
|
||||
// Move item back to its previous position.
|
||||
item->Pose.Position.x = x;
|
||||
item->Pose.Position.y = y;
|
||||
item->Pose.Position.z = z;
|
||||
}
|
||||
else
|
||||
item->Pose.Position.y = collResult.Position.Ceiling;
|
||||
item->Pose.Position.y = pointProbe.Position.Ceiling;
|
||||
|
||||
if (item->Animation.Velocity.y < 0)
|
||||
item->Animation.Velocity.y = -item->Animation.Velocity.y;
|
||||
|
@ -1695,14 +1689,14 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
|||
}
|
||||
}
|
||||
|
||||
collResult = GetCollision(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber);
|
||||
pointProbe = GetCollision(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber);
|
||||
|
||||
if (collResult.RoomNumber != item->RoomNumber)
|
||||
if (pointProbe.RoomNumber != item->RoomNumber)
|
||||
{
|
||||
if (item->ObjectNumber == ID_GRENADE && TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, collResult.RoomNumber))
|
||||
if (item->ObjectNumber == ID_GRENADE && TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, pointProbe.RoomNumber))
|
||||
Splash(item);
|
||||
|
||||
ItemNewRoom(itemNumber, collResult.RoomNumber);
|
||||
ItemNewRoom(itemNumber, pointProbe.RoomNumber);
|
||||
}
|
||||
|
||||
item->Pose.Position.y -= radius;
|
||||
|
@ -1791,8 +1785,8 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
|
|||
else
|
||||
{
|
||||
DoDamage(item, INT_MAX);
|
||||
|
||||
DoLotsOfBlood(item->Pose.Position.x,
|
||||
DoLotsOfBlood(
|
||||
item->Pose.Position.x,
|
||||
laraItem->Pose.Position.y - CLICK(1),
|
||||
item->Pose.Position.z,
|
||||
laraItem->Animation.Velocity.z,
|
||||
|
@ -1897,7 +1891,7 @@ void CreatureCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll
|
|||
int rx = (frame->boundingBox.X1 + frame->boundingBox.X2) / 2;
|
||||
int rz = (frame->boundingBox.X2 + frame->boundingBox.Z2) / 2;
|
||||
|
||||
if (frame->boundingBox.Height() > STEP_SIZE)
|
||||
if (frame->boundingBox.Height() > CLICK(1))
|
||||
{
|
||||
auto* lara = GetLaraInfo(laraItem);
|
||||
|
||||
|
@ -1905,7 +1899,7 @@ void CreatureCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll
|
|||
|
||||
lara->HitDirection = (short)angle;
|
||||
|
||||
// TODO: check if a second Lara.hitFrame++; is required there !
|
||||
// TODO: Check if a second Lara.hitFrame++ is required. -- TokyoSU
|
||||
lara->HitFrame++;
|
||||
if (lara->HitFrame > 30)
|
||||
lara->HitFrame = 30;
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
#include "Specific/phd_global.h"
|
||||
#include "Specific/trmath.h"
|
||||
|
||||
struct ItemInfo;
|
||||
struct CollisionInfo;
|
||||
class FloorInfo;
|
||||
struct CollisionInfo;
|
||||
struct ItemInfo;
|
||||
struct MESH_INFO;
|
||||
|
||||
constexpr auto MAX_COLLIDED_OBJECTS = 1024;
|
||||
constexpr auto ITEM_RADIUS_YMAX = SECTOR(3);
|
||||
|
||||
constexpr auto VEHICLE_COLLISION_TERMINAL_VELOCITY = 30;
|
||||
constexpr auto VEHICLE_COLLISION_TERMINAL_VELOCITY = 30.0f;
|
||||
|
||||
extern BOUNDING_BOX GlobalCollisionBounds;
|
||||
extern ItemInfo* CollidedItems[MAX_COLLIDED_OBJECTS];
|
||||
|
@ -36,17 +36,17 @@ bool TestLaraPosition(OBJECT_COLLISION_BOUNDS* bounds, ItemInfo* item, ItemInfo*
|
|||
bool AlignLaraPosition(Vector3Int* offset, ItemInfo* item, ItemInfo* laraItem);
|
||||
bool MoveLaraPosition(Vector3Int* pos, ItemInfo* item, ItemInfo* laraItem);
|
||||
|
||||
bool ItemNearLara(PHD_3DPOS* pos, int radius);
|
||||
bool ItemNearTarget(PHD_3DPOS* origin, ItemInfo* target, int radius);
|
||||
bool ItemNearLara(Vector3Int* origin, int radius);
|
||||
bool ItemNearTarget(Vector3Int* origin, ItemInfo* targetEntity, int radius);
|
||||
|
||||
bool Move3DPosTo3DPos(PHD_3DPOS* origin, PHD_3DPOS* target, int velocity, short angleAdd);
|
||||
bool Move3DPosTo3DPos(PHD_3DPOS* fromPose, PHD_3DPOS* toPose, int velocity, short angleAdd);
|
||||
|
||||
bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius);
|
||||
bool TestBoundsCollideStatic(ItemInfo* item, MESH_INFO* mesh, int radius);
|
||||
bool ItemPushItem(ItemInfo* item, ItemInfo* laraItem, CollisionInfo* coll, bool spasmEnabled, char bigPush);
|
||||
bool ItemPushStatic(ItemInfo* laraItem, MESH_INFO* mesh, CollisionInfo* coll);
|
||||
|
||||
bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, CollisionInfo* coll);
|
||||
bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pose, CollisionInfo* coll);
|
||||
void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll);
|
||||
|
||||
void AIPickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||
|
|
|
@ -78,7 +78,7 @@ int TestCollision(ItemInfo* item, ItemInfo* laraItem)
|
|||
int dz = z1 - z2;
|
||||
int r = r1 + r2;
|
||||
|
||||
if ((pow(dx, 2) + pow(dy, 2) + pow(dz, 2)) < pow(r, 2))
|
||||
if ((SQUARE(dx) + SQUARE(dy) + SQUARE(dz)) < SQUARE(r))
|
||||
{
|
||||
item->SetBits(JointBitType::Touch, i);
|
||||
laraItem->SetBits(JointBitType::Touch, j);
|
||||
|
|
|
@ -92,7 +92,7 @@ void DrawNearbyPathfinding(int boxIndex)
|
|||
|
||||
void DropEntityPickups(ItemInfo* item)
|
||||
{
|
||||
ItemInfo* pickup = NULL;
|
||||
ItemInfo* pickup = nullptr;
|
||||
|
||||
for (short pickupNumber = item->CarriedItem; pickupNumber != NO_ITEM; pickupNumber = pickup->CarriedItem)
|
||||
{
|
||||
|
@ -157,7 +157,7 @@ void CreatureYRot2(PHD_3DPOS* srcPos, short angle, short angleAdd)
|
|||
|
||||
bool SameZone(CreatureInfo* creature, ItemInfo* target)
|
||||
{
|
||||
int* zone = g_Level.Zones[creature->LOT.Zone][FlipStatus].data();
|
||||
int* zone = g_Level.Zones[(int)creature->LOT.Zone][FlipStatus].data();
|
||||
auto* item = &g_Level.Items[creature->ItemNumber];
|
||||
|
||||
auto* room = &g_Level.Rooms[item->RoomNumber];
|
||||
|
@ -449,12 +449,13 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
|||
short top;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
if (!item->Data)
|
||||
|
||||
if (!item->IsCreature())
|
||||
return false;
|
||||
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
auto* LOT = &creature->LOT;
|
||||
int* zone = g_Level.Zones[LOT->Zone][FlipStatus].data();
|
||||
int* zone = g_Level.Zones[(int)LOT->Zone][FlipStatus].data();
|
||||
|
||||
int boxHeight;
|
||||
if (item->BoxNumber != NO_BOX)
|
||||
|
@ -462,7 +463,7 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
|||
else
|
||||
boxHeight = item->Floor;
|
||||
|
||||
auto old = item->Pose.Position;
|
||||
auto prevPos = item->Pose.Position;
|
||||
|
||||
AnimateItem(item);
|
||||
|
||||
|
@ -477,7 +478,7 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
|||
int y = item->Pose.Position.y + bounds->Y1;
|
||||
|
||||
short roomNumber = item->RoomNumber;
|
||||
GetFloor(old.x, y, old.z, &roomNumber);
|
||||
GetFloor(prevPos.x, y, prevPos.z, &roomNumber);
|
||||
FloorInfo* floor = GetFloor(item->Pose.Position.x, y, item->Pose.Position.z, &roomNumber);
|
||||
|
||||
// TODO: Check why some blocks have box = -1 assigned to them -- Lwmte, 10.11.21
|
||||
|
@ -506,18 +507,18 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
|||
{
|
||||
xPos = item->Pose.Position.x / SECTOR(1);
|
||||
zPos = item->Pose.Position.z / SECTOR(1);
|
||||
shiftX = old.x / SECTOR(1);
|
||||
shiftZ = old.z / SECTOR(1);
|
||||
shiftX = prevPos.x / SECTOR(1);
|
||||
shiftZ = prevPos.z / SECTOR(1);
|
||||
|
||||
if (xPos < shiftX)
|
||||
item->Pose.Position.x = old.x & (~(SECTOR(1) - 1));
|
||||
item->Pose.Position.x = prevPos.x & (~WALL_MASK);
|
||||
else if (xPos > shiftX)
|
||||
item->Pose.Position.x = old.x | (SECTOR(1) - 1);
|
||||
item->Pose.Position.x = prevPos.x | WALL_MASK;
|
||||
|
||||
if (zPos < shiftZ)
|
||||
item->Pose.Position.z = old.z & (~(SECTOR(1) - 1));
|
||||
item->Pose.Position.z = prevPos.z & (~WALL_MASK);
|
||||
else if (zPos > shiftZ)
|
||||
item->Pose.Position.z = old.z | (SECTOR(1) - 1);
|
||||
item->Pose.Position.z = prevPos.z | (WALL_MASK);
|
||||
|
||||
floor = GetFloor(item->Pose.Position.x, y, item->Pose.Position.z, &roomNumber);
|
||||
height = g_Level.Boxes[floor->Box].height;
|
||||
|
@ -538,8 +539,8 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
|||
|
||||
int x = item->Pose.Position.x;
|
||||
int z = item->Pose.Position.z;
|
||||
xPos = x & (SECTOR(1) - 1);
|
||||
zPos = z & (SECTOR(1) - 1);
|
||||
xPos = x & WALL_MASK;
|
||||
zPos = z & WALL_MASK;
|
||||
short radius = Objects[item->ObjectNumber].radius;
|
||||
shiftX = 0;
|
||||
shiftZ = 0;
|
||||
|
@ -669,8 +670,8 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
|||
{
|
||||
if (item->Pose.Position.y + top < ceiling)
|
||||
{
|
||||
item->Pose.Position.x = old.x;
|
||||
item->Pose.Position.z = old.z;
|
||||
item->Pose.Position.x = prevPos.x;
|
||||
item->Pose.Position.z = prevPos.z;
|
||||
dy = LOT->Fly;
|
||||
}
|
||||
else
|
||||
|
@ -689,13 +690,13 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
|||
}
|
||||
else if (item->Pose.Position.y <= height)
|
||||
{
|
||||
dy = 0;
|
||||
item->Pose.Position.y = height;
|
||||
dy = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->Pose.Position.x = old.x;
|
||||
item->Pose.Position.z = old.z;
|
||||
item->Pose.Position.x = prevPos.x;
|
||||
item->Pose.Position.z = prevPos.z;
|
||||
dy = -LOT->Fly;
|
||||
}
|
||||
|
||||
|
@ -732,11 +733,7 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
|||
if (item->Pose.Position.y > item->Floor)
|
||||
{
|
||||
if (item->Pose.Position.y > (item->Floor + CLICK(1)))
|
||||
{
|
||||
item->Pose.Position.x = old.x;
|
||||
item->Pose.Position.y = old.y;
|
||||
item->Pose.Position.z = old.z;
|
||||
}
|
||||
item->Pose.Position = prevPos;
|
||||
else
|
||||
item->Pose.Position.y = item->Floor;
|
||||
}
|
||||
|
@ -753,11 +750,7 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
|||
top = bounds->Y1; // TODO: check if Y1 or Y2
|
||||
|
||||
if (item->Pose.Position.y + top < ceiling)
|
||||
{
|
||||
item->Pose.Position.x = old.x;
|
||||
item->Pose.Position.z = old.z;
|
||||
item->Pose.Position.y = old.y;
|
||||
}
|
||||
item->Pose.Position = prevPos;
|
||||
|
||||
floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber);
|
||||
item->Floor = GetFloorHeight(floor, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z);
|
||||
|
@ -773,7 +766,6 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
|||
}
|
||||
|
||||
CreatureSwitchRoom(itemNumber);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -892,7 +884,7 @@ int ValidBox(ItemInfo* item, short zoneNumber, short boxNumber)
|
|||
return false;
|
||||
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
int* zone = g_Level.Zones[creature->LOT.Zone][FlipStatus].data();
|
||||
int* zone = g_Level.Zones[(int)creature->LOT.Zone][FlipStatus].data();
|
||||
if (creature->LOT.Fly == NO_FLYING && zone[boxNumber] != zoneNumber)
|
||||
return false;
|
||||
|
||||
|
@ -978,7 +970,7 @@ int UpdateLOT(LOTInfo* LOT, int depth)
|
|||
|
||||
int SearchLOT(LOTInfo* LOT, int depth)
|
||||
{
|
||||
int* zone = g_Level.Zones[LOT->Zone][FlipStatus].data();
|
||||
int* zone = g_Level.Zones[(int)LOT->Zone][FlipStatus].data();
|
||||
int searchZone = zone[LOT->Head];
|
||||
|
||||
if (depth <= 0)
|
||||
|
@ -1092,7 +1084,7 @@ int CreatureActive(short itemNumber)
|
|||
if (item->Flags & IFLAG_KILLED)
|
||||
return false; // Object is already dead
|
||||
|
||||
if (item->Status == ITEM_INVISIBLE || !item->Data.is<CreatureInfo>())
|
||||
if (item->Status == ITEM_INVISIBLE || !item->IsCreature())
|
||||
{
|
||||
if (!EnableEntityAI(itemNumber, 0))
|
||||
return false; // AI couldn't be activated
|
||||
|
@ -1170,7 +1162,7 @@ int CreatureVault(short itemNumber, short angle, int vault, int shift)
|
|||
vault = 0;
|
||||
else if (item->Floor > y + CHECK_CLICK(7))
|
||||
vault = -4;
|
||||
// FIXME: edit assets adding climb down animations for Von Croy and baddys?
|
||||
// FIXME: edit assets adding climb down animations for Von Croy and baddies?
|
||||
else if (item->Floor > y + CHECK_CLICK(5) &&
|
||||
item->ObjectNumber != ID_VON_CROY &&
|
||||
item->ObjectNumber != ID_BADDY1 &&
|
||||
|
@ -1375,7 +1367,7 @@ void FindAITargetObject(CreatureInfo* creature, short objectNumber)
|
|||
|
||||
if (g_Level.AIObjects.size() > 0)
|
||||
{
|
||||
AI_OBJECT* foundObject = NULL;
|
||||
AI_OBJECT* foundObject = nullptr;
|
||||
|
||||
for (int i = 0; i < g_Level.AIObjects.size(); i++)
|
||||
{
|
||||
|
@ -1383,7 +1375,7 @@ void FindAITargetObject(CreatureInfo* creature, short objectNumber)
|
|||
|
||||
if (aiObject->objectNumber == objectNumber && aiObject->triggerFlags == item->ItemFlags[3] && aiObject->roomNumber != NO_ROOM)
|
||||
{
|
||||
int* zone = g_Level.Zones[creature->LOT.Zone][FlipStatus].data();
|
||||
int* zone = g_Level.Zones[(int)creature->LOT.Zone][FlipStatus].data();
|
||||
|
||||
auto* room = &g_Level.Rooms[item->RoomNumber];
|
||||
item->BoxNumber = GetSector(room, item->Pose.Position.x - room->x, item->Pose.Position.z - room->z)->Box;
|
||||
|
@ -1402,7 +1394,7 @@ void FindAITargetObject(CreatureInfo* creature, short objectNumber)
|
|||
}
|
||||
}
|
||||
|
||||
if (foundObject != NULL)
|
||||
if (foundObject != nullptr)
|
||||
{
|
||||
auto* aiItem = creature->AITarget;
|
||||
|
||||
|
@ -1443,7 +1435,7 @@ void CreatureAIInfo(ItemInfo* item, AI_INFO* AI)
|
|||
creature->Enemy = LaraItem;
|
||||
}
|
||||
|
||||
int* zone = g_Level.Zones[creature->LOT.Zone][FlipStatus].data();
|
||||
int* zone = g_Level.Zones[(int)creature->LOT.Zone][FlipStatus].data();
|
||||
|
||||
auto* room = &g_Level.Rooms[item->RoomNumber];
|
||||
item->BoxNumber = NO_BOX;
|
||||
|
@ -1465,8 +1457,8 @@ void CreatureAIInfo(ItemInfo* item, AI_INFO* AI)
|
|||
// This prevents enemies from running to Lara and attacking nothing when she is hanging or shimmying. -- Lwmte, 27.06.22
|
||||
|
||||
bool reachable = false;
|
||||
if (object->zoneType == ZoneType::ZONE_FLYER ||
|
||||
(object->zoneType == ZoneType::ZONE_WATER && TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, item->RoomNumber)))
|
||||
if (object->ZoneType == ZoneType::Flyer ||
|
||||
(object->ZoneType == ZoneType::Water && TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, item->RoomNumber)))
|
||||
{
|
||||
reachable = true; // If NPC is flying or swimming in water, always reach Lara
|
||||
}
|
||||
|
|
|
@ -112,15 +112,15 @@ void DisableEntityAI(short itemNumber)
|
|||
item->Data = nullptr;
|
||||
}
|
||||
|
||||
void InitialiseSlot(short itemNum, short slot, bool makeTarget)
|
||||
void InitialiseSlot(short itemNumber, short slot, bool makeTarget)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNum];
|
||||
auto* obj = &Objects[item->ObjectNumber];
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* object = &Objects[item->ObjectNumber];
|
||||
item->Data = CreatureInfo();
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
InitialiseLOTarray(itemNum);
|
||||
creature->ItemNumber = itemNum;
|
||||
|
||||
InitialiseLOTarray(itemNumber);
|
||||
creature->ItemNumber = itemNumber;
|
||||
creature->Mood = MoodType::Bored;
|
||||
creature->JointRotation[0] = 0;
|
||||
creature->JointRotation[1] = 0;
|
||||
|
@ -136,12 +136,12 @@ void InitialiseSlot(short itemNum, short slot, bool makeTarget)
|
|||
creature->MonkeySwingAhead = false;
|
||||
creature->LOT.CanJump = false;
|
||||
creature->LOT.CanMonkey = false;
|
||||
creature->LOT.IsAmphibious = false; // only the crocodile can go water and land. (default: true)
|
||||
creature->LOT.IsAmphibious = false; // True for crocodile by default as the only the crocodile that can move in water and on land.
|
||||
creature->LOT.IsJumping = false;
|
||||
creature->LOT.IsMonkeying = false;
|
||||
creature->MaxTurn = ANGLE(1);
|
||||
creature->Flags = 0;
|
||||
creature->Enemy = NULL;
|
||||
creature->Enemy = nullptr;
|
||||
creature->LOT.Fly = NO_FLYING;
|
||||
creature->LOT.BlockMask = BLOCKED;
|
||||
|
||||
|
@ -155,117 +155,116 @@ void InitialiseSlot(short itemNum, short slot, bool makeTarget)
|
|||
else
|
||||
creature->AITarget = nullptr;
|
||||
|
||||
switch (obj->zoneType)
|
||||
switch (object->ZoneType)
|
||||
{
|
||||
default:
|
||||
case ZONE_NULL:
|
||||
case ZoneType::None:
|
||||
creature->LOT.Step = CLICK(1);
|
||||
creature->LOT.Drop = -CLICK(1);
|
||||
obj->zoneType = ZONE_BASIC; // only entity that use CreatureActive() will reach InitialiseSlot() !
|
||||
object->ZoneType = ZoneType::Basic; // Only entities that use CreatureActive() will reach InitialiseSlot().
|
||||
break;
|
||||
|
||||
case ZONE_SKELLY:
|
||||
// Can jump
|
||||
// Can jump.
|
||||
case ZoneType::Skeleton:
|
||||
creature->LOT.Step = CLICK(1);
|
||||
creature->LOT.Drop = -CLICK(1);
|
||||
creature->LOT.CanJump = true;
|
||||
creature->LOT.Zone = ZONE_SKELLY;
|
||||
creature->LOT.Zone = ZoneType::Skeleton;
|
||||
break;
|
||||
|
||||
case ZONE_BASIC:
|
||||
case ZoneType::Basic:
|
||||
creature->LOT.Step = CLICK(1);
|
||||
creature->LOT.Drop = -CLICK(1);
|
||||
creature->LOT.Zone = ZONE_BASIC;
|
||||
creature->LOT.Zone = ZoneType::Basic;
|
||||
break;
|
||||
|
||||
case ZONE_FLYER:
|
||||
// Can fly
|
||||
// Can fly.
|
||||
case ZoneType::Flyer:
|
||||
creature->LOT.Step = SECTOR(20);
|
||||
creature->LOT.Drop = -SECTOR(20);
|
||||
creature->LOT.Fly = DEFAULT_FLY_UPDOWN_SPEED;
|
||||
creature->LOT.Zone = ZONE_FLYER;
|
||||
creature->LOT.Zone = ZoneType::Flyer;
|
||||
break;
|
||||
|
||||
case ZONE_WATER:
|
||||
// Can swim
|
||||
// Can swim.
|
||||
case ZoneType::Water:
|
||||
creature->LOT.Step = SECTOR(20);
|
||||
creature->LOT.Drop = -SECTOR(20);
|
||||
creature->LOT.Zone = ZONE_WATER;
|
||||
creature->LOT.Zone = ZoneType::Water;
|
||||
|
||||
if (item->ObjectNumber == ID_CROCODILE)
|
||||
{
|
||||
creature->LOT.Fly = DEFAULT_SWIM_UPDOWN_SPEED / 2; // is more slow than the other underwater entity
|
||||
creature->LOT.IsAmphibious = true; // crocodile can walk and swim.
|
||||
creature->LOT.Fly = DEFAULT_SWIM_UPDOWN_SPEED / 2; // Slower than the other underwater creatures.
|
||||
creature->LOT.IsAmphibious = true; // Can walk and swim.
|
||||
}
|
||||
else if (item->ObjectNumber == ID_BIG_RAT)
|
||||
{
|
||||
creature->LOT.Fly = NO_FLYING; // dont want the bigrat to be able to go in water (just the surface !)
|
||||
creature->LOT.IsAmphibious = true; // bigrat can walk and swim.
|
||||
creature->LOT.Fly = NO_FLYING; // Can't swim underwater, only on the surface.
|
||||
creature->LOT.IsAmphibious = true; // Can walk and swim.
|
||||
}
|
||||
else
|
||||
{
|
||||
creature->LOT.Fly = DEFAULT_SWIM_UPDOWN_SPEED;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ZONE_HUMAN_CLASSIC:
|
||||
// Can climb
|
||||
// Can climb.
|
||||
case ZoneType::HumanClassic:
|
||||
creature->LOT.Step = SECTOR(1);
|
||||
creature->LOT.Drop = -SECTOR(1);
|
||||
creature->LOT.Zone = ZONE_HUMAN_CLASSIC;
|
||||
creature->LOT.Zone = ZoneType::HumanClassic;
|
||||
break;
|
||||
|
||||
case ZONE_HUMAN_JUMP:
|
||||
// Can climb and jump
|
||||
// Can climb and jump.
|
||||
case ZoneType::HumanJump:
|
||||
creature->LOT.Step = SECTOR(1);
|
||||
creature->LOT.Drop = -SECTOR(1);
|
||||
creature->LOT.CanJump = true;
|
||||
creature->LOT.Zone = ZONE_HUMAN_CLASSIC;
|
||||
creature->LOT.Zone = ZoneType::HumanClassic;
|
||||
break;
|
||||
|
||||
case ZONE_HUMAN_JUMP_AND_MONKEY:
|
||||
// Can climb, jump, monkey
|
||||
// Can climb, jump, monkeyswing.
|
||||
case ZoneType::HumanJumpAndMonkey:
|
||||
creature->LOT.Step = SECTOR(1);
|
||||
creature->LOT.Drop = -SECTOR(1);
|
||||
creature->LOT.CanJump = true;
|
||||
creature->LOT.CanMonkey = true;
|
||||
creature->LOT.Zone = ZONE_HUMAN_CLASSIC;
|
||||
creature->LOT.Zone = ZoneType::HumanClassic;
|
||||
break;
|
||||
|
||||
case ZONE_HUMAN_LONGJUMP_AND_MONKEY:
|
||||
// Can climb, jump, monkey, long jump
|
||||
// Can climb, jump, monkey swing, long jump.
|
||||
case ZoneType::HumanLongJumpAndMonkey:
|
||||
creature->LOT.Step = SECTOR(1) + CLICK(3);
|
||||
creature->LOT.Drop = -(SECTOR(1) + CLICK(3));
|
||||
creature->LOT.CanJump = true;
|
||||
creature->LOT.CanMonkey = true;
|
||||
creature->LOT.Zone = ZONE_VON_CROY;
|
||||
creature->LOT.Zone = ZoneType::VonCroy;
|
||||
break;
|
||||
|
||||
case ZONE_SPIDER:
|
||||
case ZoneType::Spider:
|
||||
creature->LOT.Step = SECTOR(1) - CLICK(2);
|
||||
creature->LOT.Drop = -(SECTOR(1) - CLICK(2));
|
||||
creature->LOT.Zone = ZONE_HUMAN_CLASSIC;
|
||||
creature->LOT.Zone = ZoneType::HumanClassic;
|
||||
break;
|
||||
|
||||
case ZONE_BLOCKABLE:
|
||||
case ZoneType::Blockable:
|
||||
creature->LOT.BlockMask = BLOCKABLE;
|
||||
creature->LOT.Zone = ZONE_BASIC;
|
||||
creature->LOT.Zone = ZoneType::Basic;
|
||||
break;
|
||||
|
||||
case ZONE_APE:
|
||||
case ZoneType::Ape:
|
||||
creature->LOT.Step = CLICK(2);
|
||||
creature->LOT.Drop = -SECTOR(1);
|
||||
break;
|
||||
|
||||
case ZONE_SOPHIALEE:
|
||||
case ZoneType::SophiaLee:
|
||||
creature->LOT.Step = SECTOR(1);
|
||||
creature->LOT.Drop = -CLICK(3);
|
||||
creature->LOT.Zone = ZONE_HUMAN_CLASSIC;
|
||||
creature->LOT.Zone = ZoneType::HumanClassic;
|
||||
break;
|
||||
}
|
||||
|
||||
ClearLOT(&creature->LOT);
|
||||
if (itemNum != Lara.ItemNumber)
|
||||
if (itemNumber != Lara.ItemNumber)
|
||||
CreateZone(item);
|
||||
|
||||
SlotsUsed++;
|
||||
|
@ -304,16 +303,16 @@ void ClearLOT(LOTInfo* LOT)
|
|||
void CreateZone(ItemInfo* item)
|
||||
{
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
auto* r = &g_Level.Rooms[item->RoomNumber];
|
||||
auto* room = &g_Level.Rooms[item->RoomNumber];
|
||||
|
||||
item->BoxNumber = GetSector(r, item->Pose.Position.x - r->x, item->Pose.Position.z - r->z)->Box;
|
||||
item->BoxNumber = GetSector(room, item->Pose.Position.x - room->x, item->Pose.Position.z - room->z)->Box;
|
||||
|
||||
if (creature->LOT.Fly)
|
||||
{
|
||||
BOX_NODE* node = creature->LOT.Node.data();
|
||||
auto* node = creature->LOT.Node.data();
|
||||
creature->LOT.ZoneCount = 0;
|
||||
|
||||
for (int i = 0; i < g_Level.Boxes.size(); i++)
|
||||
for (size_t i = 0; i < g_Level.Boxes.size(); i++)
|
||||
{
|
||||
node->boxNumber = i;
|
||||
node++;
|
||||
|
@ -322,8 +321,8 @@ void CreateZone(ItemInfo* item)
|
|||
}
|
||||
else
|
||||
{
|
||||
int* zone = g_Level.Zones[creature->LOT.Zone][0].data();
|
||||
int* flippedZone = g_Level.Zones[creature->LOT.Zone][1].data();
|
||||
int* zone = g_Level.Zones[(int)creature->LOT.Zone][0].data();
|
||||
int* flippedZone = g_Level.Zones[(int)creature->LOT.Zone][1].data();
|
||||
|
||||
int zoneNumber = zone[item->BoxNumber];
|
||||
int flippedZoneNumber = flippedZone[item->BoxNumber];
|
||||
|
@ -331,7 +330,7 @@ void CreateZone(ItemInfo* item)
|
|||
auto* node = creature->LOT.Node.data();
|
||||
creature->LOT.ZoneCount = 0;
|
||||
|
||||
for (int i = 0; i < g_Level.Boxes.size(); i++)
|
||||
for (size_t i = 0; i < g_Level.Boxes.size(); i++)
|
||||
{
|
||||
if (*zone == zoneNumber || *flippedZone == flippedZoneNumber)
|
||||
{
|
||||
|
|
|
@ -56,6 +56,7 @@ int TriggerActive(ItemInfo* item)
|
|||
flag = !flag;
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include "Specific/phd_global.h"
|
||||
|
||||
using std::vector;
|
||||
|
||||
struct ItemInfo;
|
||||
|
||||
struct BOX_NODE
|
||||
|
@ -12,34 +13,37 @@ struct BOX_NODE
|
|||
int boxNumber;
|
||||
};
|
||||
|
||||
enum ZoneType : char
|
||||
enum class ZoneType
|
||||
{
|
||||
ZONE_NULL = -1, // default zone
|
||||
ZONE_SKELLY = 0,
|
||||
ZONE_BASIC,
|
||||
ZONE_FLYER,
|
||||
ZONE_HUMAN_CLASSIC,
|
||||
ZONE_VON_CROY,
|
||||
ZONE_WATER,
|
||||
ZONE_MAX,
|
||||
/// custom zone (using zone above for LOT.zone):
|
||||
ZONE_HUMAN_JUMP_AND_MONKEY,
|
||||
ZONE_HUMAN_JUMP,
|
||||
ZONE_SPIDER,
|
||||
ZONE_BLOCKABLE, // for trex, shiva, etc..
|
||||
ZONE_SOPHIALEE, // dont want sophia to go down again !
|
||||
ZONE_APE, // only 2 click climb
|
||||
ZONE_HUMAN_LONGJUMP_AND_MONKEY,
|
||||
None = -1,
|
||||
Skeleton,
|
||||
Basic,
|
||||
Flyer,
|
||||
HumanClassic,
|
||||
VonCroy,
|
||||
Water,
|
||||
Max,
|
||||
|
||||
// Custom zones (above zones are used for LOT.zone):
|
||||
HumanJumpAndMonkey,
|
||||
HumanJump,
|
||||
Spider,
|
||||
Blockable, // For large creatures such as trex and shiva.
|
||||
SophiaLee, // Prevents Sophia from going to lower levels again.
|
||||
Ape, // Only 0.5 block climb.
|
||||
HumanLongJumpAndMonkey,
|
||||
};
|
||||
|
||||
struct LOTInfo
|
||||
{
|
||||
bool Initialised;
|
||||
|
||||
std::vector<BOX_NODE> Node;
|
||||
vector<BOX_NODE> Node;
|
||||
int Head;
|
||||
int Tail;
|
||||
|
||||
ZoneType Zone = ZoneType::None;
|
||||
Vector3Int Target = Vector3Int::Zero;
|
||||
int SearchNumber;
|
||||
int BlockMask;
|
||||
short Step;
|
||||
|
@ -49,18 +53,16 @@ struct LOTInfo
|
|||
int RequiredBox;
|
||||
short Fly;
|
||||
|
||||
bool CanJump;
|
||||
bool CanMonkey;
|
||||
bool IsJumping;
|
||||
bool IsMonkeying;
|
||||
bool IsAmphibious;
|
||||
|
||||
Vector3Int Target;
|
||||
ZoneType Zone;
|
||||
bool CanJump = false;
|
||||
bool CanMonkey = false;
|
||||
bool IsJumping = false;
|
||||
bool IsMonkeying = false;
|
||||
bool IsAmphibious = false;
|
||||
};
|
||||
|
||||
enum class MoodType
|
||||
{
|
||||
None,
|
||||
Bored,
|
||||
Attack,
|
||||
Escape,
|
||||
|
@ -70,45 +72,43 @@ enum class MoodType
|
|||
enum class CreatureAIPriority
|
||||
{
|
||||
None,
|
||||
High,
|
||||
Low,
|
||||
Medium,
|
||||
Low
|
||||
High
|
||||
};
|
||||
|
||||
struct CreatureInfo
|
||||
{
|
||||
short ItemNumber;
|
||||
short ItemNumber = -1;
|
||||
|
||||
short MaxTurn;
|
||||
short JointRotation[4];
|
||||
bool HeadLeft;
|
||||
bool HeadRight;
|
||||
LOTInfo LOT = {};
|
||||
MoodType Mood = MoodType::None;
|
||||
ItemInfo* Enemy = nullptr;
|
||||
ItemInfo* AITarget = nullptr;
|
||||
short AITargetNumber = -1;
|
||||
Vector3Int Target = Vector3Int::Zero;
|
||||
|
||||
bool Patrol; // Unused?
|
||||
bool Alerted;
|
||||
bool Friendly;
|
||||
bool HurtByLara;
|
||||
bool Poisoned;
|
||||
bool JumpAhead;
|
||||
bool MonkeySwingAhead;
|
||||
bool ReachedGoal;
|
||||
short MaxTurn = 0;
|
||||
short JointRotation[4] = {};
|
||||
bool HeadLeft = false;
|
||||
bool HeadRight = false;
|
||||
|
||||
bool Patrol = false; // Unused?
|
||||
bool Alerted = false;
|
||||
bool Friendly = false;
|
||||
bool HurtByLara = false;
|
||||
bool Poisoned = false;
|
||||
bool JumpAhead = false;
|
||||
bool MonkeySwingAhead = false;
|
||||
bool ReachedGoal = false;
|
||||
|
||||
short FiredWeapon;
|
||||
short Tosspad;
|
||||
short LocationAI;
|
||||
short FiredWeapon;
|
||||
|
||||
LOTInfo LOT;
|
||||
MoodType Mood;
|
||||
ItemInfo* Enemy;
|
||||
short AITargetNumber;
|
||||
ItemInfo* AITarget;
|
||||
short Pad; // Unused?
|
||||
Vector3Int Target;
|
||||
short Flags = 0;
|
||||
|
||||
#ifdef CREATURE_AI_PRIORITY_OPTIMIZATION
|
||||
CreatureAIPriority Priority;
|
||||
size_t FramesSinceLOTUpdate;
|
||||
CreatureAIPriority Priority = CreatureAIPriority::None;
|
||||
size_t FramesSinceLOTUpdate = 0;
|
||||
#endif
|
||||
|
||||
short Flags;
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ template<class... Ts> struct visitor : Ts... { using Ts::operator()...; };
|
|||
template<class... Ts> visitor(Ts...)->visitor<Ts...>; // line not needed in C++20...
|
||||
|
||||
using namespace TEN::Entities::TR4;
|
||||
using namespace TEN::Entities::TR5;
|
||||
using namespace TEN::Entities::Creatures::TR5;
|
||||
using namespace TEN::Entities::Vehicles;
|
||||
|
||||
struct ItemInfo;
|
||||
|
|
|
@ -57,7 +57,7 @@ void ControlMissile(short fxNumber)
|
|||
fx->pos.Position.x += velocity * phd_sin(fx->pos.Orientation.y);
|
||||
|
||||
auto probe = GetCollision(fx->pos.Position.x, fx->pos.Position.y, fx->pos.Position.z, fx->roomNumber);
|
||||
auto hitLara = ItemNearLara(&fx->pos, 200);
|
||||
auto hitLara = ItemNearLara(&fx->pos.Position, 200);
|
||||
|
||||
// Check for hitting something.
|
||||
if (fx->pos.Position.y >= probe.Position.Floor ||
|
||||
|
|
|
@ -17,45 +17,45 @@ bool ShotLara(ItemInfo* item, AI_INFO* AI, BiteInfo gun, short extraRotation, in
|
|||
auto* creature = GetCreatureInfo(item);
|
||||
auto* enemy = creature->Enemy;
|
||||
|
||||
bool hit = false;
|
||||
bool targetable = false;
|
||||
bool hasHit = false;
|
||||
bool isTargetable = false;
|
||||
|
||||
if (AI->distance <= pow(MAX_VISIBILITY_DISTANCE, 2) && Targetable(item, AI))
|
||||
if (AI->distance <= SQUARE(MAX_VISIBILITY_DISTANCE) && Targetable(item, AI))
|
||||
{
|
||||
int distance = phd_sin(AI->enemyFacing) * enemy->Animation.Velocity.z * pow(MAX_VISIBILITY_DISTANCE, 2) / 300;
|
||||
distance = pow(distance, 2) + AI->distance;
|
||||
if (distance <= pow(MAX_VISIBILITY_DISTANCE, 2))
|
||||
distance = SQUARE(distance) + AI->distance;
|
||||
if (distance <= SQUARE(MAX_VISIBILITY_DISTANCE))
|
||||
{
|
||||
int random = (pow(MAX_VISIBILITY_DISTANCE, 2) - AI->distance) / (pow(MAX_VISIBILITY_DISTANCE, 2) / 0x5000) + 8192;
|
||||
hit = GetRandomControl() < random;
|
||||
int random = (SQUARE(MAX_VISIBILITY_DISTANCE) - AI->distance) / (SQUARE(MAX_VISIBILITY_DISTANCE) / 0x5000) + 8192;
|
||||
hasHit = GetRandomControl() < random;
|
||||
}
|
||||
else
|
||||
hit = false;
|
||||
hasHit = false;
|
||||
|
||||
targetable = true;
|
||||
isTargetable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hit = false;
|
||||
targetable = false;
|
||||
hasHit = false;
|
||||
isTargetable = false;
|
||||
}
|
||||
|
||||
if (damage)
|
||||
{
|
||||
if (enemy->IsLara())
|
||||
{
|
||||
if (hit)
|
||||
if (hasHit)
|
||||
{
|
||||
DoDamage(enemy, damage);
|
||||
CreatureEffect(item, gun, &GunHit);
|
||||
}
|
||||
else if (targetable)
|
||||
else if (isTargetable)
|
||||
CreatureEffect(item, gun, &GunMiss);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreatureEffect(item, gun, &GunShot);
|
||||
if (hit)
|
||||
if (hasHit)
|
||||
{
|
||||
enemy->HitStatus = true;
|
||||
enemy->HitPoints += damage / -10;
|
||||
|
@ -74,7 +74,7 @@ bool ShotLara(ItemInfo* item, AI_INFO* AI, BiteInfo gun, short extraRotation, in
|
|||
|
||||
// TODO: smash objects
|
||||
|
||||
return targetable;
|
||||
return isTargetable;
|
||||
}
|
||||
|
||||
short GunMiss(int x, int y, int z, short velocity, short yRot, short roomNumber)
|
||||
|
@ -145,32 +145,32 @@ bool TargetVisible(ItemInfo* item, AI_INFO* AI, float maxAngle)
|
|||
return false;
|
||||
|
||||
// Check just in case.
|
||||
auto* creatureInfo = GetCreatureInfo(item);
|
||||
if (creatureInfo == nullptr)
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
if (creature == nullptr)
|
||||
return false;
|
||||
|
||||
auto* enemy = creatureInfo->Enemy;
|
||||
auto* enemy = creature->Enemy;
|
||||
if (enemy == nullptr || enemy->HitPoints == 0)
|
||||
return false;
|
||||
|
||||
short angle = AI->angle - creatureInfo->JointRotation[2];
|
||||
if (angle > -ANGLE(maxAngle) && angle < ANGLE(maxAngle))
|
||||
short angle = AI->angle - creature->JointRotation[2];
|
||||
if (angle > ANGLE(-maxAngle) && angle < ANGLE(maxAngle))
|
||||
{
|
||||
GameVector start;
|
||||
GameVector target;
|
||||
auto& bounds = GetBestFrame(enemy)->boundingBox;
|
||||
|
||||
start.x = item->Pose.Position.x;
|
||||
start.y = item->Pose.Position.y - CLICK(3);
|
||||
start.z = item->Pose.Position.z;
|
||||
start.roomNumber = item->RoomNumber;
|
||||
|
||||
target.x = enemy->Pose.Position.x;
|
||||
target.y = enemy->Pose.Position.y + ((((bounds.Y1 * 2) + bounds.Y1) + bounds.Y2) / 4);
|
||||
target.z = enemy->Pose.Position.z;
|
||||
target.roomNumber = enemy->RoomNumber; // TODO: Check why this line didn't exist before. -- TokyoSU, 10/8/2022
|
||||
|
||||
return LOS(&start, &target);
|
||||
auto origin = GameVector(
|
||||
item->Pose.Position.x,
|
||||
item->Pose.Position.y - CLICK(3),
|
||||
item->Pose.Position.z,
|
||||
item->RoomNumber
|
||||
);
|
||||
auto target = GameVector(
|
||||
enemy->Pose.Position.x,
|
||||
enemy->Pose.Position.y + ((((bounds.Y1 * 2) + bounds.Y1) + bounds.Y2) / 4),
|
||||
enemy->Pose.Position.z,
|
||||
enemy->RoomNumber // TODO: Check why this line didn't exist before. -- TokyoSU, 10/8/2022
|
||||
);
|
||||
return LOS(&origin, &target);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -259,7 +259,7 @@ namespace TEN::Entities::Effects
|
|||
return;
|
||||
}
|
||||
|
||||
if (ItemNearLara(&fx->pos, 200))
|
||||
if (ItemNearLara(&fx->pos.Position, 200))
|
||||
{
|
||||
LaraItem->HitStatus = true;
|
||||
if (fx->flag1 != 6)
|
||||
|
|
|
@ -154,7 +154,7 @@ namespace TEN::Entities::Effects
|
|||
SoundEffect(SFX_TR4_LOOP_FOR_SMALL_FIRES, &item->Pose);
|
||||
|
||||
if (!Lara.Burn &&
|
||||
ItemNearLara(&item->Pose, 600) &&
|
||||
ItemNearLara(&item->Pose.Position, 600) &&
|
||||
(pow(LaraItem->Pose.Position.x - item->Pose.Position.x, 2) +
|
||||
pow(LaraItem->Pose.Position.z - item->Pose.Position.z, 2) < pow(SECTOR(0.5f), 2)) &&
|
||||
Lara.Control.WaterStatus != WaterStatus::FlyCheat)
|
||||
|
@ -625,8 +625,7 @@ namespace TEN::Entities::Effects
|
|||
|
||||
TriggerDynamicLight(x, item->Pose.Position.y, z, 12, (GetRandomControl() & 0x3F) + 192, ((GetRandomControl() >> 4) & 0x1F) + 96, 0);
|
||||
|
||||
auto pos = PHD_3DPOS(item->Pose.Position);
|
||||
|
||||
auto pos = item->Pose.Position;
|
||||
if (ItemNearLara(&pos, 600))
|
||||
{
|
||||
if ((!Lara.Burn) && Lara.Control.WaterStatus != WaterStatus::FlyCheat)
|
||||
|
|
|
@ -191,7 +191,7 @@ namespace TEN::Entities::TR4
|
|||
locust->pos.Position.y += locust->randomRotation * phd_sin(-locust->pos.Orientation.x);
|
||||
locust->pos.Position.z += locust->randomRotation * phd_cos(locust->pos.Orientation.x) * phd_cos(locust->pos.Orientation.y);
|
||||
|
||||
if (ItemNearTarget(&locust->pos, LaraItem, CLICK(1) / 2))
|
||||
if (ItemNearTarget(&locust->pos.Position, LaraItem, CLICK(1) / 2))
|
||||
{
|
||||
TriggerBlood(locust->pos.Position.x, locust->pos.Position.y, locust->pos.Position.z, 2 * GetRandomControl(), 2);
|
||||
DoDamage(LaraItem, LOCUST_LARA_DAMAGE);
|
||||
|
|
|
@ -15,19 +15,23 @@
|
|||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
constexpr auto APE_ATTACK_DAMAGE = 200;
|
||||
|
||||
constexpr auto APE_ATTACK_RANGE = SQUARE(SECTOR(0.42f));
|
||||
constexpr auto APE_PANIC_RANGE = SQUARE(SECTOR(2));
|
||||
|
||||
constexpr auto APE_JUMP_CHANCE = 0xA0;
|
||||
constexpr auto APE_POUND_CHEST_CHANCE = APE_JUMP_CHANCE + 0xA0;
|
||||
constexpr auto APE_POUND_GROUND_CHANCE = APE_POUND_CHEST_CHANCE + 0xA0;
|
||||
constexpr auto APE_RUN_LEFT_CHANCE = APE_POUND_GROUND_CHANCE + 0xA0;
|
||||
constexpr auto APE_IDLE_JUMP_CHANCE = 1.0f / 6;
|
||||
constexpr auto APE_IDLE_POUND_CHEST_CHANCE = 1.0f / 3;
|
||||
constexpr auto APE_IDLE_POUND_GROUND_CHANCE = 1.0f / 2;
|
||||
constexpr auto APE_IDLE_RUN_LEFT_CHANCE = 1.0f / 2;
|
||||
constexpr auto APE_RUN_JUMP_CHANCE = APE_IDLE_JUMP_CHANCE / 32;
|
||||
constexpr auto APE_RUN_POUND_CHEST_CHANCE = APE_IDLE_POUND_CHEST_CHANCE / 32;
|
||||
constexpr auto APE_RUN_POUND_GROUND_CHANCE = APE_IDLE_POUND_GROUND_CHANCE / 32;
|
||||
constexpr auto APE_RUN_RUN_LEFT_CHANCE = APE_IDLE_RUN_LEFT_CHANCE / 32;
|
||||
|
||||
constexpr auto SHIFT = 75;
|
||||
constexpr auto APE_SHIFT = 75;
|
||||
|
||||
#define APE_RUN_TURN_RATE_MAX ANGLE(5.0f)
|
||||
#define APE_DISPLAY_ANGLE ANGLE(45.0f)
|
||||
|
@ -118,12 +122,12 @@ namespace TEN::Entities::TR1
|
|||
|
||||
if (yy < yFloor)
|
||||
{
|
||||
item->Pose.Position.x = (yFloor * SECTOR(1)) - SHIFT;
|
||||
item->Pose.Position.x = (yFloor * SECTOR(1)) - APE_SHIFT;
|
||||
item->Pose.Orientation.y = ANGLE(90.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
item->Pose.Position.x = (yy * SECTOR(1)) + SHIFT;
|
||||
item->Pose.Position.x = (yy * SECTOR(1)) + APE_SHIFT;
|
||||
item->Pose.Orientation.y = -ANGLE(90.0f);
|
||||
}
|
||||
}
|
||||
|
@ -131,12 +135,12 @@ namespace TEN::Entities::TR1
|
|||
{
|
||||
if (xx < xFloor)
|
||||
{
|
||||
item->Pose.Position.z = (xFloor * SECTOR(1)) - SHIFT;
|
||||
item->Pose.Position.z = (xFloor * SECTOR(1)) - APE_SHIFT;
|
||||
item->Pose.Orientation.y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->Pose.Position.z = (xx * SECTOR(1)) + SHIFT;
|
||||
item->Pose.Position.z = (xx * SECTOR(1)) + APE_SHIFT;
|
||||
item->Pose.Orientation.y = -ANGLE(180.0f);
|
||||
}
|
||||
}
|
||||
|
@ -145,7 +149,7 @@ namespace TEN::Entities::TR1
|
|||
// diagonal
|
||||
}
|
||||
|
||||
if (CreatureVault(itemNumber, angle, 2, SHIFT) == 2)
|
||||
if (CreatureVault(itemNumber, angle, 2, APE_SHIFT) == 2)
|
||||
{
|
||||
item->Pose.Position.y = y;
|
||||
SetAnimation(item, APE_ANIM_VAULT);
|
||||
|
@ -207,14 +211,13 @@ namespace TEN::Entities::TR1
|
|||
else if (!(creatureInfo->Flags & APE_FLAG_ATTACK) &&
|
||||
AI.zoneNumber == AI.enemyZone && AI.ahead)
|
||||
{
|
||||
random = (short)(GetRandomControl() / 32);
|
||||
if (random < APE_JUMP_CHANCE)
|
||||
if (TestProbability(APE_IDLE_JUMP_CHANCE))
|
||||
item->Animation.TargetState = APE_STATE_JUMP;
|
||||
else if (random < APE_POUND_CHEST_CHANCE)
|
||||
else if (TestProbability(APE_IDLE_POUND_CHEST_CHANCE))
|
||||
item->Animation.TargetState = APE_STATE_POUND_CHEST;
|
||||
else if (random < APE_POUND_GROUND_CHANCE)
|
||||
else if (TestProbability(APE_IDLE_POUND_GROUND_CHANCE))
|
||||
item->Animation.TargetState = APE_STATE_POUND_GROUND;
|
||||
else if (random < APE_RUN_LEFT_CHANCE)
|
||||
else if (TestProbability(APE_IDLE_RUN_LEFT_CHANCE))
|
||||
{
|
||||
item->Animation.TargetState = APE_STATE_RUN_LEFT;
|
||||
creatureInfo->MaxTurn = 0;
|
||||
|
@ -246,18 +249,17 @@ namespace TEN::Entities::TR1
|
|||
}
|
||||
else if (creatureInfo->Mood != MoodType::Escape)
|
||||
{
|
||||
random = (short)GetRandomControl();
|
||||
if (random < APE_JUMP_CHANCE)
|
||||
if (TestProbability(APE_RUN_JUMP_CHANCE))
|
||||
{
|
||||
item->Animation.RequiredState = APE_STATE_JUMP;
|
||||
item->Animation.TargetState = APE_STATE_IDLE;
|
||||
}
|
||||
else if (random < APE_POUND_CHEST_CHANCE)
|
||||
else if (TestProbability(APE_RUN_POUND_CHEST_CHANCE))
|
||||
{
|
||||
item->Animation.RequiredState = APE_STATE_POUND_CHEST;
|
||||
item->Animation.TargetState = APE_STATE_IDLE;
|
||||
}
|
||||
else if (random < APE_POUND_GROUND_CHANCE)
|
||||
else if (TestProbability(APE_RUN_POUND_GROUND_CHANCE))
|
||||
{
|
||||
item->Animation.RequiredState = APE_STATE_POUND_GROUND;
|
||||
item->Animation.TargetState = APE_STATE_IDLE;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
void ApeControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -9,11 +9,13 @@
|
|||
#include "Game/Lara/lara.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
constexpr auto BEAR_RUN_DAMAGE = 3;
|
||||
constexpr auto BEAR_ATTACK_DAMAGE = 200;
|
||||
|
@ -24,10 +26,10 @@ namespace TEN::Entities::TR1
|
|||
constexpr auto BEAR_REAR_RANGE = SECTOR(2);
|
||||
constexpr auto BEAR_REAR_SWIPE_ATTACK_RANGE = SECTOR(0.6f);
|
||||
constexpr auto BEAR_EAT_RANGE = CLICK(3);
|
||||
|
||||
constexpr auto BEAR_ROAR_CHANCE = 0x50;
|
||||
constexpr auto BEAR_REAR_CHANCE = 0x300;
|
||||
constexpr auto BEAR_DROP_CHANCE = 0x600;
|
||||
|
||||
constexpr auto BEAR_ROAR_CHANCE = 1.0f / 400;
|
||||
constexpr auto BEAR_REAR_CHANCE = 1.0f / 40;
|
||||
constexpr auto BEAR_DROP_CHANCE = 1.0f / 22;
|
||||
|
||||
#define BEAR_WALK_TURN_RATE_MAX ANGLE(2.0f)
|
||||
#define BEAR_RUN_TURN_RATE_MAX ANGLE(5.0f)
|
||||
|
@ -81,8 +83,8 @@ namespace TEN::Entities::TR1
|
|||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
short head = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
|
@ -117,8 +119,8 @@ namespace TEN::Entities::TR1
|
|||
{
|
||||
if (creature->Flags && item->TestBits(JointBitType::Touch, BearAttackJoints))
|
||||
{
|
||||
creature->Flags = 0;
|
||||
DoDamage(creature->Enemy, BEAR_SLAM_DAMAGE);
|
||||
creature->Flags = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -141,12 +143,12 @@ namespace TEN::Entities::TR1
|
|||
if (item->HitStatus)
|
||||
creature->Flags = 1;
|
||||
|
||||
const bool laraDead = LaraItem->HitPoints <= 0;
|
||||
bool isLaraDead = LaraItem->HitPoints <= 0;
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case BEAR_STATE_IDLE:
|
||||
if (laraDead)
|
||||
if (isLaraDead)
|
||||
{
|
||||
if (AI.bite && AI.distance < pow(BEAR_EAT_RANGE, 2))
|
||||
item->Animation.TargetState = BEAR_STATE_EAT;
|
||||
|
@ -165,7 +167,7 @@ namespace TEN::Entities::TR1
|
|||
case BEAR_STATE_STROLL:
|
||||
creature->MaxTurn = BEAR_WALK_TURN_RATE_MAX;
|
||||
|
||||
if (laraDead && item->TestBits(JointBitType::Touch, BearAttackJoints) && AI.ahead)
|
||||
if (isLaraDead && item->TestBits(JointBitType::Touch, BearAttackJoints) && AI.ahead)
|
||||
item->Animation.TargetState = BEAR_STATE_IDLE;
|
||||
else if (creature->Mood != MoodType::Bored)
|
||||
{
|
||||
|
@ -174,10 +176,10 @@ namespace TEN::Entities::TR1
|
|||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.RequiredState = BEAR_STATE_STROLL;
|
||||
}
|
||||
else if (GetRandomControl() < BEAR_ROAR_CHANCE)
|
||||
else if (TestProbability(BEAR_ROAR_CHANCE))
|
||||
{
|
||||
item->Animation.RequiredState = BEAR_STATE_ROAR;
|
||||
item->Animation.TargetState = BEAR_STATE_IDLE;
|
||||
item->Animation.RequiredState = BEAR_STATE_ROAR;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -186,16 +188,14 @@ namespace TEN::Entities::TR1
|
|||
creature->MaxTurn = BEAR_RUN_TURN_RATE_MAX;
|
||||
|
||||
if (item->TestBits(JointBitType::Touch, BearAttackJoints))
|
||||
{
|
||||
DoDamage(creature->Enemy, BEAR_RUN_DAMAGE);
|
||||
}
|
||||
|
||||
if (creature->Mood == MoodType::Bored || laraDead)
|
||||
if (creature->Mood == MoodType::Bored || isLaraDead)
|
||||
item->Animation.TargetState = BEAR_STATE_IDLE;
|
||||
else if (AI.ahead && !item->Animation.RequiredState)
|
||||
{
|
||||
if (AI.distance < pow(BEAR_REAR_RANGE, 2) &&
|
||||
GetRandomControl() < BEAR_REAR_CHANCE &&
|
||||
TestProbability(BEAR_REAR_CHANCE) &&
|
||||
!creature->Flags)
|
||||
{
|
||||
item->Animation.RequiredState = BEAR_STATE_REAR;
|
||||
|
@ -227,8 +227,8 @@ namespace TEN::Entities::TR1
|
|||
case BEAR_STATE_WALK_FORWARD:
|
||||
if (creature->Flags)
|
||||
{
|
||||
item->Animation.RequiredState = BEAR_STATE_STROLL;
|
||||
item->Animation.TargetState = BEAR_STATE_REAR;
|
||||
item->Animation.RequiredState = BEAR_STATE_STROLL;
|
||||
}
|
||||
else if (AI.ahead && item->TestBits(JointBitType::Touch, BearAttackJoints))
|
||||
item->Animation.TargetState = BEAR_STATE_REAR;
|
||||
|
@ -237,15 +237,15 @@ namespace TEN::Entities::TR1
|
|||
item->Animation.TargetState = BEAR_STATE_REAR;
|
||||
item->Animation.RequiredState = BEAR_STATE_STROLL;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Bored || GetRandomControl() < BEAR_ROAR_CHANCE)
|
||||
else if (creature->Mood == MoodType::Bored || TestProbability(BEAR_ROAR_CHANCE))
|
||||
{
|
||||
item->Animation.TargetState = BEAR_STATE_REAR;
|
||||
item->Animation.RequiredState = BEAR_STATE_ROAR;
|
||||
item->Animation.TargetState = BEAR_STATE_REAR;
|
||||
}
|
||||
else if (AI.distance > pow(BEAR_REAR_RANGE, 2) || GetRandomControl() < BEAR_DROP_CHANCE)
|
||||
else if (AI.distance > pow(BEAR_REAR_RANGE, 2) || TestProbability(BEAR_DROP_CHANCE))
|
||||
{
|
||||
item->Animation.RequiredState = BEAR_STATE_IDLE;
|
||||
item->Animation.TargetState = BEAR_STATE_REAR;
|
||||
item->Animation.RequiredState = BEAR_STATE_IDLE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -264,8 +264,8 @@ namespace TEN::Entities::TR1
|
|||
if (!item->Animation.RequiredState &&
|
||||
item->TestBits(JointBitType::Touch, BearAttackJoints))
|
||||
{
|
||||
CreatureEffect(item, BearBite, DoBloodSplat);
|
||||
DoDamage(creature->Enemy, BEAR_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, BearBite, DoBloodSplat);
|
||||
item->Animation.RequiredState = BEAR_STATE_IDLE;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
void BearControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
constexpr auto BIG_RAT_BITE_ATTACK_DAMAGE = 20;
|
||||
constexpr auto BIG_RAT_POUNCE_ATTACK_DAMAGE = 25;
|
||||
|
@ -27,7 +27,7 @@ namespace TEN::Entities::TR1
|
|||
constexpr auto BIG_RAT_POUNCE_ATTACK_RANGE = SQUARE(SECTOR(0.5f));
|
||||
constexpr auto BIG_RAT_WATER_BITE_ATTACK_RANGE = SQUARE(SECTOR(0.3f));
|
||||
|
||||
constexpr auto BIG_RAT_REAR_POSE_CHANCE = 0.008f;
|
||||
constexpr auto BIG_RAT_REAR_POSE_CHANCE = 1.0f / 128;
|
||||
constexpr auto BIG_RAT_SWIM_UP_DOWN_SPEED = 32;
|
||||
constexpr auto BIG_RAT_WATER_SURFACE_OFFSET = 10;
|
||||
|
||||
|
@ -115,10 +115,11 @@ namespace TEN::Entities::TR1
|
|||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
int waterHeight = GetRatWaterHeight(item);
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
bool isOnWater = waterHeight != NO_HEIGHT;
|
||||
|
||||
short angle = 0;
|
||||
short head = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != BIG_RAT_STATE_LAND_DEATH &&
|
||||
|
@ -183,9 +184,9 @@ namespace TEN::Entities::TR1
|
|||
if (!item->Animation.RequiredState && AI.ahead &&
|
||||
item->TestBits(JointBitType::Touch, BigRatBite.meshNum))
|
||||
{
|
||||
item->Animation.RequiredState = BIG_RAT_STATE_IDLE;
|
||||
DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, BigRatBite, DoBloodSplat);
|
||||
item->Animation.RequiredState = BIG_RAT_STATE_IDLE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -194,9 +195,9 @@ namespace TEN::Entities::TR1
|
|||
if (!item->Animation.RequiredState && AI.ahead &&
|
||||
item->TestBits(JointBitType::Touch, BigRatBite.meshNum))
|
||||
{
|
||||
item->Animation.RequiredState = BIG_RAT_STATE_RUN_FORWARD;
|
||||
DoDamage(creature->Enemy, BIG_RAT_POUNCE_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, BigRatBite, DoBloodSplat);
|
||||
item->Animation.RequiredState = BIG_RAT_STATE_RUN_FORWARD;
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
void InitialiseBigRat(short itemNumber);
|
||||
void BigRatControl(short itemNumber);
|
||||
|
|
|
@ -15,21 +15,23 @@
|
|||
#include "Game/people.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
constexpr auto CENTAUR_REAR_DAMAGE = 200;
|
||||
constexpr auto CENTAUR_REAR_RANGE = SECTOR(1.5f);
|
||||
constexpr auto CENTAUR_REAR_CHANCE = 0x60;
|
||||
constexpr auto CENTAUR_REAR_DAMAGE = 200;
|
||||
constexpr auto CENTAUR_REAR_RANGE = SECTOR(1.5f);
|
||||
constexpr auto CENTAUR_REAR_CHANCE = 1.0f / 340;
|
||||
constexpr auto CENTAUR_BOMB_VELOCITY = 20;
|
||||
|
||||
#define CENTAUR_TURN_RATE_MAX ANGLE(4.0f)
|
||||
|
||||
const auto CentaurRocketBite = BiteInfo(Vector3(11.0f, 415.0f, 41.0f), 13);
|
||||
const auto CentaurRearBite = BiteInfo(Vector3(50.0f, 30.0f, 0.0f), 5);
|
||||
const auto CentaurRearBite = BiteInfo(Vector3(50.0f, 30.0f, 0.0f), 5);
|
||||
const vector<int> CentaurAttackJoints = { 0, 3, 4, 7, 8, 16, 17 };
|
||||
|
||||
enum CentaurState
|
||||
|
@ -56,17 +58,14 @@ namespace TEN::Entities::TR1
|
|||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
short head = 0;
|
||||
|
||||
short angle = 0;
|
||||
short head = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != CENTAUR_STATE_DEATH)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[ID_CENTAUR_MUTANT].animIndex + CENTAUR_ANIM_DEATH;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = CENTAUR_STATE_DEATH;
|
||||
}
|
||||
SetAnimation(item, CENTAUR_ANIM_DEATH);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -98,18 +97,18 @@ namespace TEN::Entities::TR1
|
|||
case CENTAUR_STATE_RUN_FORWARD:
|
||||
if (AI.bite && AI.distance < pow(CENTAUR_REAR_RANGE, 2))
|
||||
{
|
||||
item->Animation.RequiredState = CENTAUR_STATE_WARNING;
|
||||
item->Animation.TargetState = CENTAUR_STATE_IDLE;
|
||||
item->Animation.RequiredState = CENTAUR_STATE_WARNING;
|
||||
}
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
item->Animation.TargetState = CENTAUR_STATE_IDLE;
|
||||
item->Animation.RequiredState = CENTAUR_STATE_AIM;
|
||||
item->Animation.TargetState = CENTAUR_STATE_IDLE;
|
||||
}
|
||||
else if (GetRandomControl() < CENTAUR_REAR_CHANCE)
|
||||
else if (TestProbability(CENTAUR_REAR_CHANCE))
|
||||
{
|
||||
item->Animation.RequiredState = CENTAUR_STATE_WARNING;
|
||||
item->Animation.TargetState = CENTAUR_STATE_IDLE;
|
||||
item->Animation.RequiredState = CENTAUR_STATE_WARNING;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -137,8 +136,8 @@ namespace TEN::Entities::TR1
|
|||
if (!item->Animation.RequiredState &&
|
||||
item->TestBits(JointBitType::Touch, CentaurAttackJoints))
|
||||
{
|
||||
CreatureEffect(item, CentaurRearBite, DoBloodSplat);
|
||||
DoDamage(creature->Enemy, CENTAUR_REAR_DAMAGE);
|
||||
CreatureEffect(item, CentaurRearBite, DoBloodSplat);
|
||||
item->Animation.RequiredState = CENTAUR_STATE_IDLE;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
constexpr auto SHARD_VELOCITY = 250;
|
||||
constexpr auto BOMB_VELOCITY = 220;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
// - Bacon Lara cannot be targeted.
|
||||
// - Bacon Lara cannot move like Lara.
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
// Original:
|
||||
void InitialiseDoppelganger(short itemNumber)
|
||||
|
@ -80,26 +80,24 @@ namespace TEN::Entities::TR1
|
|||
int laraFloorHeight = GetCollision(LaraItem).Position.Floor;
|
||||
|
||||
// Animate bacon Lara, mirroring Lara's position.
|
||||
item->Animation.FrameNumber = LaraItem->Animation.FrameNumber;
|
||||
item->Animation.AnimNumber = LaraItem->Animation.AnimNumber;
|
||||
item->Pose.Position.x = pos.x;
|
||||
item->Pose.Position.y = pos.y;
|
||||
item->Pose.Position.z = pos.z;
|
||||
item->Animation.FrameNumber = LaraItem->Animation.FrameNumber;
|
||||
item->Pose.Position = pos;
|
||||
item->Pose.Orientation.x = LaraItem->Pose.Orientation.x;
|
||||
item->Pose.Orientation.y = LaraItem->Pose.Orientation.y - ANGLE(180.0f);
|
||||
item->Pose.Orientation.z = LaraItem->Pose.Orientation.z;
|
||||
ItemNewRoom(itemNumber, LaraItem->RoomNumber);
|
||||
|
||||
// Compare floor heights.
|
||||
if (item->Floor >= laraFloorHeight + SECTOR(1) + 1 && // Add 1 to avoid bacon Lara dying when exiting water.
|
||||
if (item->Floor >= (laraFloorHeight + SECTOR(1) + 1) && // Add 1 to avoid bacon Lara dying when exiting water.
|
||||
!LaraItem->Animation.IsAirborne)
|
||||
{
|
||||
SetAnimation(item, LA_JUMP_WALL_SMASH_START);
|
||||
item->Animation.Velocity.z = 0;
|
||||
item->Animation.Velocity.y = 0;
|
||||
item->Animation.IsAirborne = true;
|
||||
item->Data = -1;
|
||||
item->Animation.Velocity.y = 0.0f;
|
||||
item->Animation.Velocity.z = 0.0f;
|
||||
item->Pose.Position.y += 50;
|
||||
item->Data = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,8 +112,8 @@ namespace TEN::Entities::TR1
|
|||
item->Pose.Position.y = item->Floor;
|
||||
TestTriggers(item, true);
|
||||
|
||||
item->Animation.Velocity.y = 0;
|
||||
item->Animation.IsAirborne = false;
|
||||
item->Animation.Velocity.y = 0.0f;
|
||||
item->Animation.TargetState = LS_DEATH;
|
||||
item->Animation.RequiredState = LS_DEATH;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
void InitialiseDoppelganger(short itemNumber);
|
||||
void DoppelgangerControl(short itemNumber);
|
||||
|
|
|
@ -12,11 +12,13 @@
|
|||
#include "Game/misc.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
constexpr auto MUTANT_ATTACK_DAMAGE = 500;
|
||||
constexpr auto MUTANT_CONTACT_DAMAGE = 6;
|
||||
|
@ -24,16 +26,17 @@ namespace TEN::Entities::TR1
|
|||
constexpr auto MUTANT_ATTACK_RANGE = SQUARE(SECTOR(2.5f));
|
||||
constexpr auto MUTANT_CLOSE_RANGE = SQUARE(SECTOR(2.2f));
|
||||
|
||||
constexpr auto MUTANT_ATTACK_1_CHANCE = 0x2AF8;
|
||||
constexpr auto MUTANT_ATTACK_2_CHANCE = 0x55F0;
|
||||
// TODO: Unused.
|
||||
constexpr auto MUTANT_ATTACK_1_CHANCE = 1.0f / 3.0f;
|
||||
constexpr auto MUTANT_ATTACK_2_CHANCE = MUTANT_ATTACK_1_CHANCE * 2;
|
||||
|
||||
#define MUTANT_NEED_TURN ANGLE(45.0f)
|
||||
#define MUTANT_TURN ANGLE(3.0f)
|
||||
|
||||
#define LARA_GIANT_MUTANT_DEATH 6 // TODO: Not 13? Check this.
|
||||
|
||||
const vector<int> MutantAttackJoints = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
|
||||
const vector<int> MutantAttackLeftJoints = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
|
||||
const vector<int> MutantAttackJoints = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
|
||||
const vector<int> MutantAttackLeftJoint = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
|
||||
const vector<int> MutantAttackRightJoints = { 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
|
||||
|
||||
enum GiantMutantState
|
||||
|
@ -66,17 +69,13 @@ namespace TEN::Entities::TR1
|
|||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
short head = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != MUTANT_STATE_DEATH)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + MUTANT_ANIM_DEATH;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = MUTANT_STATE_DEATH;
|
||||
}
|
||||
SetAnimation(item, MUTANT_ANIM_DEATH);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -92,9 +91,7 @@ namespace TEN::Entities::TR1
|
|||
angle = (short)phd_atan(creature->Target.z - item->Pose.Position.z, creature->Target.x - item->Pose.Position.x) - item->Pose.Orientation.y;
|
||||
|
||||
if (item->TouchBits)
|
||||
{
|
||||
DoDamage(creature->Enemy, MUTANT_CONTACT_DAMAGE);
|
||||
}
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
|
@ -122,7 +119,7 @@ namespace TEN::Entities::TR1
|
|||
else
|
||||
item->Animation.TargetState = MUTANT_STATE_FORWARD;
|
||||
}
|
||||
else if (GetRandomControl() < 0x4000)
|
||||
else if (TestProbability(0.5f))
|
||||
item->Animation.TargetState = MUTANT_STATE_ATTACK_1;
|
||||
else
|
||||
item->Animation.TargetState = MUTANT_STATE_ATTACK_2;
|
||||
|
@ -178,8 +175,8 @@ namespace TEN::Entities::TR1
|
|||
case MUTANT_STATE_ATTACK_1:
|
||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, MutantAttackRightJoints))
|
||||
{
|
||||
creature->Flags = 1;
|
||||
DoDamage(creature->Enemy, MUTANT_ATTACK_DAMAGE);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -187,8 +184,8 @@ namespace TEN::Entities::TR1
|
|||
case MUTANT_STATE_ATTACK_2:
|
||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, MutantAttackJoints))
|
||||
{
|
||||
creature->Flags = 1;
|
||||
DoDamage(creature->Enemy, MUTANT_ATTACK_DAMAGE);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -202,14 +199,11 @@ namespace TEN::Entities::TR1
|
|||
|
||||
LaraItem->Animation.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + LARA_GIANT_MUTANT_DEATH;
|
||||
LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase;
|
||||
LaraItem->Animation.ActiveState = LaraItem->Animation.TargetState = 46;
|
||||
LaraItem->RoomNumber = item->RoomNumber;
|
||||
LaraItem->Pose.Position.x = item->Pose.Position.x;
|
||||
LaraItem->Pose.Position.y = item->Pose.Position.y;
|
||||
LaraItem->Pose.Position.z = item->Pose.Position.z;
|
||||
LaraItem->Pose.Orientation.y = item->Pose.Orientation.y;
|
||||
LaraItem->Pose.Orientation.x = LaraItem->Pose.Orientation.z = 0;
|
||||
LaraItem->Animation.ActiveState = 46;
|
||||
LaraItem->Animation.TargetState = 46;
|
||||
LaraItem->Animation.IsAirborne = false;
|
||||
LaraItem->Pose = PHD_3DPOS(item->Pose.Position, 0, item->Pose.Orientation.y, 0);
|
||||
LaraItem->RoomNumber = item->RoomNumber;
|
||||
LaraItem->HitPoints = -1;
|
||||
Lara.Air = -1;
|
||||
Lara.Control.HandStatus = HandStatus::Busy;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
void GiantMutantControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -10,9 +10,12 @@
|
|||
#include "Game/people.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/trmath.h"
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
using namespace TEN::Math::Random;
|
||||
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
// TODO: Organise.
|
||||
constexpr auto NATLA_SHOT_DAMAGE = 100;
|
||||
|
@ -20,9 +23,10 @@ namespace TEN::Entities::TR1
|
|||
constexpr auto NATLA_DEATH_TIME = (FPS * 16); // 16 seconds.
|
||||
constexpr auto NATLA_FLYMODE = 0x8000;
|
||||
constexpr auto NATLA_TIMER = 0x7FFF;
|
||||
constexpr auto NATLA_LAND_CHANCE = 0x100;
|
||||
constexpr auto NATLA_GUN_VELOCITY = 400;
|
||||
|
||||
constexpr auto NATLA_LAND_CHANCE = 0.008f;
|
||||
|
||||
#define NATLA_TURN_NEAR_DEATH_SPEED ANGLE(6.0f)
|
||||
#define NATLA_TURN_SPEED ANGLE(5.0f)
|
||||
#define NATLA_FLY_ANGLE_SPEED ANGLE(5.0f)
|
||||
|
@ -183,7 +187,7 @@ namespace TEN::Entities::TR1
|
|||
|
||||
if (item->Animation.ActiveState == NATLA_STATE_FLY && (creature->Flags & NATLA_FLYMODE))
|
||||
{
|
||||
if (creature->Flags & NATLA_FLYMODE && shoot && GetRandomControl() < NATLA_LAND_CHANCE)
|
||||
if (creature->Flags & NATLA_FLYMODE && shoot && TestProbability(NATLA_LAND_CHANCE))
|
||||
creature->Flags -= NATLA_FLYMODE;
|
||||
|
||||
if (!(creature->Flags & NATLA_FLYMODE))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
void NatlaControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -13,11 +13,13 @@
|
|||
#include "Game/people.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/trmath.h"
|
||||
|
||||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
constexpr auto WINGED_MUTANT_IDLE_JUMP_ATTACK_DAMAGE = 150;
|
||||
constexpr auto WINGED_MUTANT_RUN_JUMP_ATTACK_DAMAGE = 100;
|
||||
|
@ -29,8 +31,8 @@ namespace TEN::Entities::TR1
|
|||
constexpr auto WINGED_MUTANT_IDLE_JUMP_ATTACK_RANGE = SQUARE(SECTOR(2.5f));
|
||||
constexpr auto WINGED_MUTANT_ATTACK_RANGE = SQUARE(SECTOR(3.75f));
|
||||
|
||||
constexpr auto WINGED_MUTANT_POSE_CHANCE = 85;
|
||||
constexpr auto WINGED_MUTANT_UNPOSE_CHANCE = 200;
|
||||
constexpr auto WINGED_MUTANT_POSE_CHANCE = 1.0f / 400;
|
||||
constexpr auto WINGED_MUTANT_UNPOSE_CHANCE = 1.0f / 164;
|
||||
|
||||
constexpr auto WINGED_MUTANT_FLY_VELOCITY = CLICK(1) / 8;
|
||||
constexpr auto WINGED_MUTANT_SHARD_VELOCITY = 250;
|
||||
|
@ -337,7 +339,7 @@ namespace TEN::Entities::TR1
|
|||
if (AI.distance < WINGED_MUTANT_WALK_RANGE)
|
||||
{
|
||||
if (AI.zoneNumber == AI.enemyZone ||
|
||||
GetRandomControl() < WINGED_MUTANT_UNPOSE_CHANCE)
|
||||
TestProbability(WINGED_MUTANT_UNPOSE_CHANCE))
|
||||
{
|
||||
item->Animation.TargetState = WMUTANT_STATE_WALK_FORWARD;
|
||||
}
|
||||
|
@ -345,7 +347,7 @@ namespace TEN::Entities::TR1
|
|||
else
|
||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Bored && GetRandomControl() < WINGED_MUTANT_UNPOSE_CHANCE)
|
||||
else if (creature->Mood == MoodType::Bored && TestProbability(WINGED_MUTANT_UNPOSE_CHANCE))
|
||||
item->Animation.TargetState = WMUTANT_STATE_WALK_FORWARD;
|
||||
else if (creature->Mood == MoodType::Attack ||
|
||||
creature->Mood == MoodType::Escape)
|
||||
|
@ -363,7 +365,7 @@ namespace TEN::Entities::TR1
|
|||
else if (creature->Mood == MoodType::Bored ||
|
||||
(creature->Mood == MoodType::Stalk && AI.zoneNumber != AI.enemyZone))
|
||||
{
|
||||
if (GetRandomControl() < WINGED_MUTANT_POSE_CHANCE)
|
||||
if (TestProbability(WINGED_MUTANT_POSE_CHANCE))
|
||||
item->Animation.TargetState = WMUTANT_STATE_POSE;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Stalk &&
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
void InitialiseWingedMutant(short itemNumber);
|
||||
void WingedMutantControl(short itemNumber);
|
||||
|
|
|
@ -9,11 +9,13 @@
|
|||
#include "Game/Lara/lara.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
constexpr auto WOLF_BITE_DAMAGE = 100;
|
||||
constexpr auto WOLF_LUNGE_DAMAGE = 50;
|
||||
|
@ -21,9 +23,9 @@ namespace TEN::Entities::TR1
|
|||
constexpr auto WOLF_ATTACK_RANGE = SQUARE(SECTOR(1.5f));
|
||||
constexpr auto WOLF_STALK_RANGE = SQUARE(SECTOR(2));
|
||||
|
||||
constexpr auto WOLF_WAKE_CHANCE = 0x20;
|
||||
constexpr auto WOLF_SLEEP_CHANCE = 0x20;
|
||||
constexpr auto WOLF_HOWL_CHANCE = 0x180;
|
||||
constexpr auto WOLF_WAKE_CHANCE = 1.0f / 1000;
|
||||
constexpr auto WOLF_SLEEP_CHANCE = 1.0f / 1000;
|
||||
constexpr auto WOLF_HOWL_CHANCE = 1.0f / 85;
|
||||
|
||||
constexpr auto WOLF_SLEEP_FRAME = 96;
|
||||
|
||||
|
@ -110,7 +112,7 @@ namespace TEN::Entities::TR1
|
|||
item->Animation.RequiredState = WOLF_STATE_CROUCH;
|
||||
item->Animation.TargetState = WOLF_STATE_IDLE;
|
||||
}
|
||||
else if (GetRandomControl() < WOLF_WAKE_CHANCE)
|
||||
else if (TestProbability(WOLF_WAKE_CHANCE))
|
||||
{
|
||||
item->Animation.RequiredState = WOLF_STATE_WALK;
|
||||
item->Animation.TargetState = WOLF_STATE_IDLE;
|
||||
|
@ -133,7 +135,7 @@ namespace TEN::Entities::TR1
|
|||
item->Animation.TargetState = WOLF_STATE_STALK;
|
||||
item->Animation.RequiredState = WOLF_STATE_NONE;
|
||||
}
|
||||
else if (GetRandomControl() < WOLF_SLEEP_CHANCE)
|
||||
else if (TestProbability(WOLF_SLEEP_CHANCE))
|
||||
{
|
||||
item->Animation.RequiredState = WOLF_STATE_SLEEP;
|
||||
item->Animation.TargetState = WOLF_STATE_IDLE;
|
||||
|
@ -174,7 +176,7 @@ namespace TEN::Entities::TR1
|
|||
item->Animation.TargetState = WOLF_STATE_RUN;
|
||||
}
|
||||
}
|
||||
else if (GetRandomControl() < WOLF_HOWL_CHANCE)
|
||||
else if (TestProbability(WOLF_HOWL_CHANCE))
|
||||
{
|
||||
item->Animation.RequiredState = WOLF_STATE_HOWL;
|
||||
item->Animation.TargetState = WOLF_STATE_CROUCH;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR1
|
||||
namespace TEN::Entities::Creatures::TR1
|
||||
{
|
||||
void InitialiseWolf(short itemNumber);
|
||||
void WolfControl(short itemNumber);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "framework.h"
|
||||
#include "Objects/TR1/tr1_objects.h"
|
||||
|
||||
/// necessary import
|
||||
#include "Game/control/box.h"
|
||||
#include "Game/collision/collide_item.h"
|
||||
#include "Game/itemdata/creature_info.h"
|
||||
|
@ -9,7 +8,7 @@
|
|||
#include "Specific/setup.h"
|
||||
#include "Specific/level.h"
|
||||
|
||||
/// entities
|
||||
// Creatures
|
||||
#include "Objects/TR1/Entity/tr1_ape.h" // OK
|
||||
#include "Objects/TR1/Entity/tr1_bear.h" // OK
|
||||
#include "Objects/TR1/Entity/tr1_doppelganger.h" // OK
|
||||
|
@ -21,7 +20,7 @@
|
|||
#include "Objects/TR1/Entity/tr1_winged_mutant.h"
|
||||
#include "Objects/Utils/object_helper.h"
|
||||
|
||||
using namespace TEN::Entities::TR1;
|
||||
using namespace TEN::Entities::Creatures::TR1;
|
||||
|
||||
static void StartEntity(ObjectInfo* obj)
|
||||
{
|
||||
|
@ -78,7 +77,7 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->saveHitpoints = true;
|
||||
obj->saveAnim = true;
|
||||
obj->saveFlags = true;
|
||||
obj->zoneType = ZONE_APE;
|
||||
obj->ZoneType = ZoneType::Ape;
|
||||
}
|
||||
|
||||
obj = &Objects[ID_BIG_RAT];
|
||||
|
@ -98,7 +97,7 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->saveAnim = true;
|
||||
obj->saveFlags = true;
|
||||
obj->waterCreature = true;
|
||||
obj->zoneType = ZONE_WATER;
|
||||
obj->ZoneType = ZoneType::Water;
|
||||
obj->SetBoneRotation(1, ROT_Y); // head
|
||||
}
|
||||
|
||||
|
@ -174,7 +173,7 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->saveHitpoints = true;
|
||||
obj->saveAnim = true;
|
||||
obj->saveFlags = true;
|
||||
obj->zoneType = ZONE_BLOCKABLE;
|
||||
obj->ZoneType = ZoneType::Blockable;
|
||||
obj->SetBoneRotation(10, ROT_X | ROT_Y);
|
||||
}
|
||||
|
||||
|
@ -194,7 +193,7 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->saveFlags = true;
|
||||
obj->saveHitpoints = true;
|
||||
obj->savePosition = true;
|
||||
obj->zoneType = ZONE_FLYER;
|
||||
obj->ZoneType = ZoneType::Flyer;
|
||||
obj->SetBoneRotation(1, ROT_Y); // torso
|
||||
obj->SetBoneRotation(2, ROT_Y); // head
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
constexpr auto BARRACUDA_ATTACK_DAMAGE = 100;
|
||||
constexpr auto BARRACUDA_IDLE_ATTACK_RANGE = SQUARE(SECTOR(0.67f));
|
||||
|
@ -23,7 +23,7 @@ namespace TEN::Entities::TR2
|
|||
|
||||
enum BarracudaState
|
||||
{
|
||||
BARRACUDA_STATE_NONE = 0,
|
||||
// No state 0.
|
||||
BARRACUDA_STATE_IDLE = 1,
|
||||
BARRACUDA_STATE_SWIM_SLOW = 2,
|
||||
BARRACUDA_STATE_SWIM_FAST = 3,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void BarracudaControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
constexpr auto BIRD_MONSTER_ATTACK_DAMAGE = 200;
|
||||
constexpr auto BIRD_MONSTER_SLAM_CRUSH_ATTACK_RANGE = SQUARE(SECTOR(1));
|
||||
|
@ -29,7 +29,7 @@ namespace TEN::Entities::TR2
|
|||
|
||||
enum BirdMonsterState
|
||||
{
|
||||
BMONSTER_STATE_NONE = 0,
|
||||
// No state 0.
|
||||
BMONSTER_STATE_IDLE = 1,
|
||||
BMONSTER_STATE_WALK_FORWARD = 2,
|
||||
BMONSTER_STATE_SLAM_ATTACK_START = 3,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void BirdMonsterControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -18,28 +18,23 @@
|
|||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Input;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
const auto DragonMouthBite = BiteInfo(Vector3(35.0f, 171.0f, 1168.0f), 12);
|
||||
|
||||
constexpr auto DRAGON_SWIPE_ATTACK_DAMAGE = 250;
|
||||
constexpr auto DRAGON_TOUCH_DAMAGE = 10;
|
||||
|
||||
constexpr auto DRAGON_CONTACT_DAMAGE = 10;
|
||||
|
||||
const auto DragonMouthBite = BiteInfo(Vector3(35.0f, 171.0f, 1168.0f), 12);
|
||||
const vector<int> DragonSwipeAttackJointsLeft = { 24, 25, 26, 27, 28, 29, 30 };
|
||||
const vector<int> DragonSwipeAttackJointsRight = { 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
// TODO: Organise.
|
||||
#define DRAGON_SWIPE_DAMAGE 250
|
||||
#define DRAGON_TOUCH_DAMAGE 10
|
||||
|
||||
#define DRAGON_LIVE_TIME (30 * 11)
|
||||
#define DRAGON_CLOSE_RANGE pow(SECTOR(3), 2)
|
||||
#define DRAGON_STATE_IDLE_RANGE pow(SECTOR(6), 2)
|
||||
#define DRAGON_FLAME_SPEED 200
|
||||
|
||||
#define DRAGON_TOUCH_R 0x0fe
|
||||
#define DRAGON_TOUCH_L 0x7f000000
|
||||
|
||||
#define DRAGON_ALMOST_LIVE 100
|
||||
#define BOOM_TIME 130
|
||||
#define BOOM_TIME_MIDDLE 140
|
||||
|
@ -58,7 +53,7 @@ namespace TEN::Entities::TR2
|
|||
|
||||
enum DragonState
|
||||
{
|
||||
DRAGON_STATE_NONE = 0,
|
||||
// No state 0.
|
||||
DRAGON_STATE_WALK = 1,
|
||||
DRAGON_STATE_LEFT = 2,
|
||||
DRAGON_STATE_RIGHT = 3,
|
||||
|
@ -255,8 +250,8 @@ namespace TEN::Entities::TR2
|
|||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
short head = 0;
|
||||
|
||||
bool ahead;
|
||||
|
||||
|
@ -318,9 +313,7 @@ namespace TEN::Entities::TR2
|
|||
ahead = (AI.ahead && AI.distance > DRAGON_CLOSE_RANGE && AI.distance < DRAGON_STATE_IDLE_RANGE);
|
||||
|
||||
if (item->TouchBits)
|
||||
{
|
||||
DoDamage(creature->Enemy, DRAGON_TOUCH_DAMAGE);
|
||||
}
|
||||
DoDamage(creature->Enemy, DRAGON_CONTACT_DAMAGE);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
|
@ -350,19 +343,19 @@ namespace TEN::Entities::TR2
|
|||
break;
|
||||
|
||||
case DRAGON_STATE_SWIPE_LEFT:
|
||||
if (item->TouchBits & DRAGON_TOUCH_L)
|
||||
if (item->TestBits(JointBitType::Touch, DragonSwipeAttackJointsLeft))
|
||||
{
|
||||
DoDamage(creature->Enemy, DRAGON_SWIPE_ATTACK_DAMAGE);
|
||||
creature->Flags = 0;
|
||||
DoDamage(creature->Enemy, DRAGON_SWIPE_DAMAGE);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DRAGON_STATE_SWIPE_RIGHT:
|
||||
if (item->TouchBits & DRAGON_TOUCH_R)
|
||||
if (item->TestBits(JointBitType::Touch, DragonSwipeAttackJointsRight))
|
||||
{
|
||||
DoDamage(creature->Enemy, DRAGON_SWIPE_ATTACK_DAMAGE);
|
||||
creature->Flags = 0;
|
||||
DoDamage(creature->Enemy, DRAGON_SWIPE_DAMAGE);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -404,13 +397,11 @@ namespace TEN::Entities::TR2
|
|||
case DRAGON_STATE_TURN_LEFT:
|
||||
item->Pose.Orientation.y += -(ANGLE(1.0f) - angle);
|
||||
creature->Flags = 0;
|
||||
|
||||
break;
|
||||
|
||||
case DRAGON_STATE_TURN_RIGHT:
|
||||
item->Pose.Orientation.y += (ANGLE(1.0f) - angle);
|
||||
creature->Flags = 0;
|
||||
|
||||
break;
|
||||
|
||||
case DRAGON_STATE_AIM_1:
|
||||
|
@ -434,12 +425,11 @@ namespace TEN::Entities::TR2
|
|||
|
||||
case DRAGON_STATE_FIRE_1:
|
||||
item->Pose.Orientation.y -= angle;
|
||||
SoundEffect(SFX_TR2_DRAGON_FIRE, &item->Pose);
|
||||
|
||||
if (AI.ahead)
|
||||
head = -AI.angle;
|
||||
|
||||
SoundEffect(SFX_TR2_DRAGON_FIRE, &item->Pose);
|
||||
|
||||
if (creature->Flags)
|
||||
{
|
||||
if (AI.ahead)
|
||||
|
@ -459,12 +449,7 @@ namespace TEN::Entities::TR2
|
|||
back->Animation.ActiveState = item->Animation.ActiveState;
|
||||
back->Animation.AnimNumber = Objects[ID_DRAGON_BACK].animIndex + (item->Animation.AnimNumber - Objects[ID_DRAGON_FRONT].animIndex);
|
||||
back->Animation.FrameNumber = g_Level.Anims[back->Animation.AnimNumber].frameBase + (item->Animation.FrameNumber - g_Level.Anims[item->Animation.AnimNumber].frameBase);
|
||||
back->Pose.Position.x = item->Pose.Position.x;
|
||||
back->Pose.Position.y = item->Pose.Position.y;
|
||||
back->Pose.Position.z = item->Pose.Position.z;
|
||||
back->Pose.Orientation.x = item->Pose.Orientation.x;
|
||||
back->Pose.Orientation.y = item->Pose.Orientation.y;
|
||||
back->Pose.Orientation.z = item->Pose.Orientation.z;
|
||||
back->Pose = item->Pose;
|
||||
|
||||
if (back->RoomNumber != item->RoomNumber)
|
||||
ItemNewRoom(backItemNumber, item->RoomNumber);
|
||||
|
@ -484,9 +469,7 @@ namespace TEN::Entities::TR2
|
|||
{
|
||||
auto* back = &g_Level.Items[backItem];
|
||||
back->ObjectNumber = ID_DRAGON_BACK;
|
||||
back->Pose.Position.x = item->Pose.Position.x;
|
||||
back->Pose.Position.y = item->Pose.Position.y;
|
||||
back->Pose.Position.z = item->Pose.Position.z;
|
||||
back->Pose.Position = item->Pose.Position;
|
||||
back->Pose.Orientation.y = item->Pose.Orientation.y;
|
||||
back->RoomNumber = item->RoomNumber;
|
||||
back->Status = ITEM_INVISIBLE;
|
||||
|
@ -500,9 +483,7 @@ namespace TEN::Entities::TR2
|
|||
auto* front = &g_Level.Items[frontItem];
|
||||
|
||||
front->ObjectNumber = ID_DRAGON_FRONT;
|
||||
front->Pose.Position.x = item->Pose.Position.x;
|
||||
front->Pose.Position.y = item->Pose.Position.y;
|
||||
front->Pose.Position.z = item->Pose.Position.z;
|
||||
front->Pose.Position = item->Pose.Position;
|
||||
front->Pose.Orientation.y = item->Pose.Orientation.y;
|
||||
front->RoomNumber = item->RoomNumber;
|
||||
front->Status = ITEM_INVISIBLE;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "Game/collision/collide_room.h"
|
||||
#include "Game/items.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void DragonCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||
void DragonControl(short backNumber);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
const auto EagleBite = BiteInfo(Vector3(15.0f, 46.0f, 21.0f), 6);
|
||||
const auto CrowBite = BiteInfo(Vector3(2.0f, 10.0f, 60.0f), 14);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void InitialiseEagle(short itemNumber);
|
||||
void EagleControl(short itemNumber);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
using namespace TEN::Math::Random;
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
constexpr auto KNIFE_PROJECTILE_DAMAGE = 50;
|
||||
|
||||
|
@ -29,7 +29,7 @@ namespace TEN::Entities::TR2
|
|||
|
||||
enum KnifeThrowerState
|
||||
{
|
||||
KTHROWER_STATE_NONE = 0,
|
||||
// No state 0.
|
||||
KTHROWER_STATE_IDLE = 1,
|
||||
KTHROWER_STATE_WALK_FORWARD = 2,
|
||||
KTHROWER_STATE_RUN_FORWARD = 3,
|
||||
|
@ -101,7 +101,7 @@ namespace TEN::Entities::TR2
|
|||
|
||||
fx->pos.Orientation.z += ANGLE(30.0f);
|
||||
|
||||
if (ItemNearLara(&fx->pos, 200))
|
||||
if (ItemNearLara(&fx->pos.Position, 200))
|
||||
{
|
||||
DoDamage(LaraItem, KNIFE_PROJECTILE_DAMAGE);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void KnifeControl(short fxNumber);
|
||||
void KnifeThrowerControl(short itemNumber);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "Specific/setup.h"
|
||||
#include "Specific/trmath.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
const auto MercenaryUziBite = BiteInfo(Vector3(0.0f, 150.0f, 19.0f), 17);
|
||||
const auto MercenaryAutoPistolBite = BiteInfo(Vector3(0.0f, 230.0f, 9.0f), 17);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void MercenaryUziControl(short itemNumber);
|
||||
void MercenaryAutoPistolControl(short itemNumber);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
const auto MonkBite = BiteInfo(Vector3(-23.0f, 16.0f, 265.0f), 14);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void MonkControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
using namespace TEN::Math::Random;
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
constexpr auto RAT_ATTACK_DAMAGE = 20;
|
||||
constexpr auto RAT_ATTACK_RANGE = SQUARE(CLICK(0.7f));
|
||||
|
@ -29,7 +29,7 @@ namespace TEN::Entities::TR2
|
|||
|
||||
enum RatState
|
||||
{
|
||||
RAT_STATE_NONE = 0,
|
||||
// No state 0.
|
||||
RAT_STATE_WALK_FORWARD = 1,
|
||||
RAT_STATE_IDLE = 2,
|
||||
RAT_STATE_SQUEAK = 3,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void RatControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -11,9 +11,14 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
constexpr auto SHARK_BITE_ATTACK_DAMAGE = 400;
|
||||
|
||||
const auto SharkBite = BiteInfo(Vector3(17.0f, -22.0f, 344.0f), 12);
|
||||
const vector<int> SharkBiteAttackJoints = { 10, 12, 13 };
|
||||
|
||||
void SharkControl(short itemNumber)
|
||||
{
|
||||
|
@ -21,7 +26,7 @@ namespace TEN::Entities::TR2
|
|||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* info = GetCreatureInfo(item);
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short head = 0;
|
||||
|
@ -29,11 +34,7 @@ namespace TEN::Entities::TR2
|
|||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != 5)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[ID_SHARK].animIndex + 4;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 5;
|
||||
}
|
||||
SetAnimation(item, 4);
|
||||
|
||||
CreatureFloat(itemNumber);
|
||||
return;
|
||||
|
@ -46,39 +47,40 @@ namespace TEN::Entities::TR2
|
|||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
angle = CreatureTurn(item, info->MaxTurn);
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 0:
|
||||
info->Flags = 0;
|
||||
info->MaxTurn = 0;
|
||||
creature->MaxTurn = 0;
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(0.75f), 2) && AI.zoneNumber == AI.enemyZone)
|
||||
item->Animation.TargetState = 3;
|
||||
else
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
info->MaxTurn = ANGLE(0.5f);
|
||||
creature->MaxTurn = ANGLE(0.5f);
|
||||
|
||||
if (info->Mood == MoodType::Bored)
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
break;
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.75f), 2))
|
||||
item->Animation.TargetState = 0;
|
||||
else if (info->Mood == MoodType::Escape || AI.distance > pow(SECTOR(3), 2) || !AI.ahead)
|
||||
else if (creature->Mood == MoodType::Escape || AI.distance > pow(SECTOR(3), 2) || !AI.ahead)
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
info->MaxTurn = ANGLE(2.0f);
|
||||
info->Flags = 0;
|
||||
creature->MaxTurn = ANGLE(2.0f);
|
||||
creature->Flags = 0;
|
||||
|
||||
if (info->Mood == MoodType::Bored)
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 1;
|
||||
else if (info->Mood == MoodType::Escape)
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
break;
|
||||
else if (AI.ahead && AI.distance < pow(1365, 2) && AI.zoneNumber == AI.enemyZone)
|
||||
{
|
||||
|
@ -95,11 +97,11 @@ namespace TEN::Entities::TR2
|
|||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (!info->Flags && item->TouchBits & 0x3400)
|
||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, SharkBiteAttackJoints))
|
||||
{
|
||||
DoDamage(creature->Enemy, SHARK_BITE_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, SharkBite, DoBloodSplat);
|
||||
DoDamage(info->Enemy, 400);
|
||||
info->Flags = 1;
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void SharkControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -8,22 +8,70 @@
|
|||
#include "Game/misc.h"
|
||||
#include "Game/people.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
using namespace TEN::Math::Random;
|
||||
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
constexpr auto SILENCER_SHOOT_ATTACK_DAMAGE = 50;
|
||||
constexpr auto SILENCER_RUN_RANGE = SQUARE(SECTOR(2));
|
||||
|
||||
const auto SilencerGunBite = BiteInfo(Vector3(3.0f, 331.0f, 56.0f), 10);
|
||||
|
||||
// TODO
|
||||
#define SILENCER_WALK_TURN_RATE_MAX ANGLE(5.0f)
|
||||
#define SILENCER_RUN_TURN_RATE_MAX ANGLE(5.0f)
|
||||
|
||||
enum SilencerState
|
||||
{
|
||||
|
||||
// No state 0.
|
||||
SILENCER_STATE_WALK_FORWARD = 1,
|
||||
SILENCER_STATE_RUN_FORWARD = 2,
|
||||
SILENCER_STATE_IDLE_FRAME = 3,
|
||||
SILENCER_STATE_IDLE = 4,
|
||||
SILENCER_STATE_POSE = 5,
|
||||
SILENCER_STATE_AIM_1 = 6,
|
||||
SILENCER_STATE_SHOOT_1 = 7,
|
||||
// No state 8.
|
||||
SILENCER_STATE_RUN_SHOOT = 9,
|
||||
SILENCER_STATE_AIM_2 = 10,
|
||||
SILENCER_STATE_SHOOT_2 = 11,
|
||||
SILENCER_STATE_DEATH_1 = 12,
|
||||
SILENCER_STATE_DEATH_2 = 13
|
||||
};
|
||||
|
||||
// TODO
|
||||
enum SilencerAnim
|
||||
{
|
||||
|
||||
SILENCER_ANIM_IDLE_FRAME = 0,
|
||||
SILENCER_ANIM_IDLE_TO_WALK_FORWARD = 1,
|
||||
SILENCER_ANIM_WALK_FORWARD = 2,
|
||||
SILENCER_ANIM_WALK_FORWARD_TO_IDLE = 3,
|
||||
SILENCER_ANIM_WALK_FORWARD_TO_POSE = 4,
|
||||
SILENCER_ANIM_POSE = 5,
|
||||
SILENCER_ANIM_WALK_FORWARD_TO_RUN_FORWARD = 6,
|
||||
SILENCER_ANIM_RUN_FORWARD = 7,
|
||||
SILENCER_ANIM_RUN_FORWARD_TO_IDLE = 8,
|
||||
SILENCER_ANIM_IDLE_TO_RUN_FORWARD = 9,
|
||||
SILENCER_ANIM_POSE_TO_IDLE = 10,
|
||||
SILENCER_ANIM_RUN_FORWARD_AIM_LEFT = 11,
|
||||
SILENCER_ANIM_RUN_FORWARD_SHOOT_LEFT = 12,
|
||||
SILENCER_ANIM_RUN_FORWARD_UNAIM_LEFT = 13,
|
||||
SILENCER_ANIM_AIM_1_START = 14,
|
||||
SILENCER_ANIM_AIM_1_CONTINUE = 15,
|
||||
SILENCER_ANIM_SHOOT_1 = 16,
|
||||
SILENCER_ANIM_UNAIM_1 = 17,
|
||||
SILENCER_ANIM_POSE_TO_AIM_1 = 18,
|
||||
SILENCER_ANIM_IDLE = 19,
|
||||
SILENCER_ANIM_DEATH_1 = 20,
|
||||
SILENCER_ANIM_DEATH_2 = 21, // Unused.
|
||||
SILENCER_ANIM_AIM_2_START = 22,
|
||||
SILENCER_ANIM_AIM_2_CONTINUE = 23,
|
||||
SILENCER_ANIM_SHOOT_2 = 24,
|
||||
SILENCER_ANIM_UNAIM_2 = 25,
|
||||
SILENCER_ANIM_RUN_FORWARD_AIM_RIGHT = 26,
|
||||
SILENCER_ANIM_RUN_FORWARD_SHOOT_RIGHT = 27,
|
||||
SILENCER_ANIM_RUN_FORWARD_UNAIM_RIGHT = 28
|
||||
};
|
||||
|
||||
void SilencerControl(short itemNumber)
|
||||
|
@ -32,21 +80,19 @@ namespace TEN::Entities::TR2
|
|||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* info = GetCreatureInfo(item);
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short angle = 0;
|
||||
short torsoX = 0;
|
||||
short torsoY = 0;
|
||||
short head = 0;
|
||||
short tilt = 0;
|
||||
auto extraHeadRot = Vector3Shrt::Zero;
|
||||
auto extraTorsoRot = Vector3Shrt::Zero;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != 12 && item->Animation.ActiveState != 13)
|
||||
if (item->Animation.ActiveState != SILENCER_STATE_DEATH_1 &&
|
||||
item->Animation.ActiveState != SILENCER_STATE_DEATH_2)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 20;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 13;
|
||||
SetAnimation(item, SILENCER_ANIM_DEATH_1);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -57,205 +103,207 @@ namespace TEN::Entities::TR2
|
|||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
angle = CreatureTurn(item, info->MaxTurn);
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 3:
|
||||
case SILENCER_STATE_IDLE_FRAME:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
info->MaxTurn = 0;
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
info->MaxTurn = 0;
|
||||
case SILENCER_STATE_IDLE:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (info->Mood == MoodType::Escape)
|
||||
if (AI.ahead)
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
{
|
||||
item->Animation.RequiredState = 2;
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
item->Animation.RequiredState = SILENCER_STATE_RUN_FORWARD;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Targetable(item, &AI))
|
||||
{
|
||||
item->Animation.RequiredState = (GetRandomControl() >= 0x4000 ? 10 : 6);
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
item->Animation.RequiredState = TestProbability(0.5f) ? SILENCER_STATE_AIM_1 : SILENCER_STATE_AIM_2;
|
||||
}
|
||||
|
||||
if (info->Mood == MoodType::Attack || !AI.ahead)
|
||||
if (creature->Mood == MoodType::Attack || !AI.ahead)
|
||||
{
|
||||
if (AI.distance >= pow(SECTOR(2), 2))
|
||||
if (AI.distance >= SILENCER_RUN_RANGE)
|
||||
{
|
||||
item->Animation.RequiredState = 2;
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
item->Animation.RequiredState = SILENCER_STATE_RUN_FORWARD;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->Animation.RequiredState = 1;
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
item->Animation.RequiredState = SILENCER_STATE_WALK_FORWARD;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (GetRandomControl() >= 1280)
|
||||
if (TestProbability(0.96f))
|
||||
{
|
||||
if (GetRandomControl() < 2560)
|
||||
if (TestProbability(0.08f))
|
||||
{
|
||||
item->Animation.RequiredState = 1;
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
item->Animation.RequiredState = SILENCER_STATE_WALK_FORWARD;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
item->Animation.RequiredState = 5;
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
item->Animation.RequiredState = SILENCER_STATE_POSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case SILENCER_STATE_WALK_FORWARD:
|
||||
creature->MaxTurn = SILENCER_WALK_TURN_RATE_MAX;
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
info->MaxTurn = 910;
|
||||
|
||||
if (info->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 2;
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = SILENCER_STATE_RUN_FORWARD;
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
item->Animation.RequiredState = (GetRandomControl() >= 0x4000 ? 10 : 6);
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
item->Animation.RequiredState = TestProbability(0.5f) ? SILENCER_STATE_AIM_1 : SILENCER_STATE_AIM_2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AI.distance > pow(SECTOR(2), 2) || !AI.ahead)
|
||||
item->Animation.TargetState = 2;
|
||||
if (info->Mood == MoodType::Bored && GetRandomControl() < 0x300)
|
||||
item->Animation.TargetState = 3;
|
||||
if (AI.distance > SILENCER_RUN_RANGE || !AI.ahead)
|
||||
item->Animation.TargetState = SILENCER_STATE_RUN_FORWARD;
|
||||
if (creature->Mood == MoodType::Bored && TestProbability(0.025f))
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
info->MaxTurn = ANGLE(5.0f);
|
||||
info->Flags = 0;
|
||||
case SILENCER_STATE_RUN_FORWARD:
|
||||
creature->MaxTurn = SILENCER_RUN_TURN_RATE_MAX;
|
||||
creature->Flags = 0;
|
||||
tilt = angle / 4;
|
||||
|
||||
if (info->Mood == MoodType::Escape)
|
||||
if (AI.ahead)
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
{
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = 9;
|
||||
item->Animation.TargetState = SILENCER_STATE_RUN_SHOOT;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
{
|
||||
if (AI.distance >= pow(SECTOR(2), 2) && AI.zoneNumber == AI.enemyZone)
|
||||
item->Animation.TargetState = 9;
|
||||
if (AI.distance >= SILENCER_RUN_RANGE && AI.zoneNumber == AI.enemyZone)
|
||||
item->Animation.TargetState = SILENCER_STATE_RUN_SHOOT;
|
||||
|
||||
break;
|
||||
}
|
||||
else if (info->Mood == MoodType::Attack)
|
||||
item->Animation.TargetState = (GetRandomControl() >= 0x4000) ? 3 : 2;
|
||||
else if (creature->Mood == MoodType::Attack)
|
||||
item->Animation.TargetState = TestProbability(0.5f) ? SILENCER_STATE_RUN_FORWARD : SILENCER_STATE_IDLE_FRAME;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
case SILENCER_STATE_POSE:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
info->MaxTurn = 0;
|
||||
if (AI.ahead)
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
{
|
||||
item->Animation.RequiredState = 6;
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
item->Animation.RequiredState = SILENCER_STATE_AIM_1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (info->Mood == MoodType::Attack || GetRandomControl() < 0x100)
|
||||
item->Animation.TargetState = 3;
|
||||
if (creature->Mood == MoodType::Attack || TestProbability(1.0f / 128))
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
|
||||
if (!AI.ahead)
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 6:
|
||||
case 10:
|
||||
info->MaxTurn = 0;
|
||||
info->Flags = 0;
|
||||
case SILENCER_STATE_AIM_1:
|
||||
case SILENCER_STATE_AIM_2:
|
||||
creature->MaxTurn = 0;
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoY = AI.angle;
|
||||
torsoX = AI.xAngle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
else
|
||||
head = AI.angle;
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
if (info->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
else if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = item->Animation.ActiveState != 6 ? 11 : 7;
|
||||
item->Animation.TargetState = (item->Animation.ActiveState != SILENCER_STATE_AIM_1) ? SILENCER_STATE_SHOOT_2 : SILENCER_STATE_SHOOT_1;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||
|
||||
break;
|
||||
|
||||
case 7:
|
||||
case 11:
|
||||
info->MaxTurn = 0;
|
||||
case SILENCER_STATE_SHOOT_1:
|
||||
case SILENCER_STATE_SHOOT_2:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoY = AI.angle;
|
||||
torsoX = AI.xAngle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
else
|
||||
head = AI.angle;
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
if (!info->Flags)
|
||||
if (!creature->Flags)
|
||||
{
|
||||
ShotLara(item, &AI, SilencerGunBite, torsoY, 50);
|
||||
info->Flags = 1;
|
||||
ShotLara(item, &AI, SilencerGunBite, extraTorsoRot.y, SILENCER_SHOOT_ATTACK_DAMAGE);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 9:
|
||||
info->MaxTurn = ANGLE(5.0f);
|
||||
case SILENCER_STATE_RUN_SHOOT:
|
||||
creature->MaxTurn = SILENCER_RUN_TURN_RATE_MAX;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoY = AI.angle;
|
||||
torsoX = AI.xAngle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
else
|
||||
head = AI.angle;
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
if (!item->Animation.RequiredState)
|
||||
{
|
||||
if (!ShotLara(item, &AI, SilencerGunBite, torsoY, 50))
|
||||
item->Animation.TargetState = 2;
|
||||
if (!ShotLara(item, &AI, SilencerGunBite, extraTorsoRot.y, SILENCER_SHOOT_ATTACK_DAMAGE))
|
||||
item->Animation.TargetState = SILENCER_STATE_RUN_FORWARD;
|
||||
|
||||
item->Animation.RequiredState = 9;
|
||||
item->Animation.RequiredState = SILENCER_STATE_RUN_SHOOT;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -263,9 +311,9 @@ namespace TEN::Entities::TR2
|
|||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torsoY);
|
||||
CreatureJoint(item, 1, torsoX);
|
||||
CreatureJoint(item, 2, head);
|
||||
CreatureJoint(item, 0, extraTorsoRot.y);
|
||||
CreatureJoint(item, 1, extraTorsoRot.x);
|
||||
CreatureJoint(item, 2, extraHeadRot.y);
|
||||
CreatureAnimation(itemNumber, angle, tilt);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void SilencerControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
#define SMAN_MIN_TURN (ANGLE(2.0f))
|
||||
#define SMAN_TARGET_ANGLE ANGLE(15.0f)
|
||||
|
@ -28,7 +28,7 @@ namespace TEN::Entities::TR2
|
|||
|
||||
enum SnowmobileManState
|
||||
{
|
||||
SMAN_STATE_NONE = 0,
|
||||
// No state 0.
|
||||
SMAN_STATE_WAIT = 1,
|
||||
SMAN_STATE_MOVING = 2,
|
||||
SMAN_STATE_START_LEFT = 3,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "Game/collision/collide_room.h"
|
||||
#include "Game/items.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void InitialiseSkidooMan(short itemNumber);
|
||||
void SkidooManCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||
|
|
|
@ -10,9 +10,12 @@
|
|||
#include "Game/misc.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
using namespace TEN::Math::Random;
|
||||
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
const auto SpearBiteLeft = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 11);
|
||||
const auto SpearBiteRight = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 18);
|
||||
|
@ -29,7 +32,7 @@ namespace TEN::Entities::TR2
|
|||
|
||||
};
|
||||
|
||||
static void XianDamage(ItemInfo* item, int damage)
|
||||
void XianDamage(ItemInfo* item, int damage)
|
||||
{
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
|
@ -76,7 +79,7 @@ namespace TEN::Entities::TR2
|
|||
short neck = 0;
|
||||
short tilt = 0;
|
||||
|
||||
bool laraAlive = LaraItem->HitPoints > 0;
|
||||
bool isLaraAlive = LaraItem->HitPoints > 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
|
@ -125,10 +128,9 @@ namespace TEN::Entities::TR2
|
|||
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
int random = GetRandomControl();
|
||||
if (random < 0x200)
|
||||
if (TestProbability(1.0f / 64))
|
||||
item->Animation.TargetState = 2;
|
||||
else if (random < 0x400)
|
||||
else if (TestProbability(1.0f / 30))
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||
|
@ -148,10 +150,9 @@ namespace TEN::Entities::TR2
|
|||
item->Animation.TargetState = 3;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
int random = GetRandomControl();
|
||||
if (random < 0x200)
|
||||
if (TestProbability(1.0f / 64))
|
||||
item->Animation.TargetState = 1;
|
||||
else if (random < 0x400)
|
||||
else if (TestProbability(1.0f / 30))
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||
|
@ -171,17 +172,16 @@ namespace TEN::Entities::TR2
|
|||
item->Animation.TargetState = 4;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
int random = GetRandomControl();
|
||||
if (random < 0x200)
|
||||
if (TestProbability(1.0f / 64))
|
||||
item->Animation.TargetState = 1;
|
||||
else if (random < 0x400)
|
||||
else if (TestProbability(1.0f / 30))
|
||||
item->Animation.TargetState = 2;
|
||||
}
|
||||
else if (AI.ahead && AI.distance < pow(SECTOR(2), 2))
|
||||
{
|
||||
if (AI.distance < pow(SECTOR(1.5f), 2))
|
||||
item->Animation.TargetState = 7;
|
||||
else if (GetRandomControl() < 0x4000)
|
||||
else if (TestProbability(0.5f))
|
||||
item->Animation.TargetState = 9;
|
||||
else
|
||||
item->Animation.TargetState = 11;
|
||||
|
@ -201,7 +201,7 @@ namespace TEN::Entities::TR2
|
|||
break;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (GetRandomControl() < 0x4000)
|
||||
if (TestProbability(0.5f))
|
||||
item->Animation.TargetState = 1;
|
||||
else
|
||||
item->Animation.TargetState = 2;
|
||||
|
@ -301,7 +301,7 @@ namespace TEN::Entities::TR2
|
|||
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||
{
|
||||
if (GetRandomControl() < 0x4000)
|
||||
if (TestProbability(0.5f))
|
||||
item->Animation.TargetState = 1;
|
||||
else
|
||||
item->Animation.TargetState = 2;
|
||||
|
@ -332,7 +332,7 @@ namespace TEN::Entities::TR2
|
|||
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||
{
|
||||
if (GetRandomControl() < 0x4000)
|
||||
if (TestProbability(0.5f))
|
||||
item->Animation.TargetState = 1;
|
||||
else
|
||||
item->Animation.TargetState = 2;
|
||||
|
@ -346,7 +346,7 @@ namespace TEN::Entities::TR2
|
|||
}
|
||||
}
|
||||
|
||||
if (laraAlive && LaraItem->HitPoints <= 0)
|
||||
if (isLaraAlive && LaraItem->HitPoints <= 0)
|
||||
{
|
||||
CreatureKill(item, 49, 19, 2);
|
||||
return;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void InitialiseSpearGuardian(short itemNumber);
|
||||
void SpearGuardianControl(short itemNumber);
|
||||
|
|
|
@ -14,11 +14,11 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
const auto SpiderBite = BiteInfo(Vector3(0.0f, 0.0f, 41.0f), 1);
|
||||
|
||||
static void S_SpiderBite(ItemInfo* item)
|
||||
void S_SpiderBite(ItemInfo* item)
|
||||
{
|
||||
auto pos = Vector3Int((int)round(SpiderBite.Position.x), (int)round(SpiderBite.Position.y), (int)round(SpiderBite.Position.z));
|
||||
GetJointAbsPosition(item, &pos, SpiderBite.meshNum);
|
||||
|
@ -26,7 +26,7 @@ namespace TEN::Entities::TR2
|
|||
DoBloodSplat(pos.x, pos.y, pos.z, 10, item->Pose.Position.y, item->RoomNumber);
|
||||
}
|
||||
|
||||
static void SpiderLeap(short itemNumber, ItemInfo* item, short angle)
|
||||
void SpiderLeap(short itemNumber, ItemInfo* item, short angle)
|
||||
{
|
||||
auto vec = GameVector(
|
||||
item->Pose.Position.x,
|
||||
|
@ -40,9 +40,7 @@ namespace TEN::Entities::TR2
|
|||
if (item->Pose.Position.y > (vec.y - CLICK(1.5f)))
|
||||
return;
|
||||
|
||||
item->Pose.Position.x = vec.x;
|
||||
item->Pose.Position.y = vec.y;
|
||||
item->Pose.Position.z = vec.z;
|
||||
item->Pose.Position = Vector3Int(vec.x, vec.y, vec.z);
|
||||
if (item->RoomNumber != vec.roomNumber)
|
||||
ItemNewRoom(item->RoomNumber, vec.roomNumber);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void SmallSpiderControl(short itemNumber);
|
||||
void BigSpiderControl(short itemNumber);
|
||||
|
|
|
@ -11,9 +11,12 @@
|
|||
#include "Game/Lara/lara.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/level.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
using namespace TEN::Math::Random;
|
||||
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
const auto SwordBite = BiteInfo(Vector3(0.0f, 37.0f, 550.0f), 15);
|
||||
|
||||
|
@ -24,7 +27,7 @@ namespace TEN::Entities::TR2
|
|||
ClearItem(itemNumber);
|
||||
}
|
||||
|
||||
static void SwordGuardianFly(ItemInfo* item)
|
||||
void SwordGuardianFly(ItemInfo* item)
|
||||
{
|
||||
Vector3Int pos;
|
||||
pos.x = (GetRandomControl() * 256 / 32768) + item->Pose.Position.x - 128;
|
||||
|
@ -47,7 +50,7 @@ namespace TEN::Entities::TR2
|
|||
short head = 0;
|
||||
short torso = 0;
|
||||
|
||||
bool laraAlive = LaraItem->HitPoints > 0;
|
||||
bool isLaraAlive = LaraItem->HitPoints > 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
|
@ -68,7 +71,7 @@ namespace TEN::Entities::TR2
|
|||
creature->LOT.Step = STEP_SIZE;
|
||||
creature->LOT.Drop = -STEP_SIZE;
|
||||
creature->LOT.Fly = NO_FLYING;
|
||||
creature->LOT.Zone = ZONE_BASIC;
|
||||
creature->LOT.Zone = ZoneType::Basic;
|
||||
|
||||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
@ -80,7 +83,7 @@ namespace TEN::Entities::TR2
|
|||
creature->LOT.Step = WALL_SIZE * 20;
|
||||
creature->LOT.Drop = -WALL_SIZE * 20;
|
||||
creature->LOT.Fly = STEP_SIZE / 4;
|
||||
creature->LOT.Zone = ZONE_FLYER;
|
||||
creature->LOT.Zone = ZoneType::Flyer;
|
||||
CreatureAIInfo(item, &AI);
|
||||
}
|
||||
}
|
||||
|
@ -114,15 +117,10 @@ namespace TEN::Entities::TR2
|
|||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (laraAlive)
|
||||
if (isLaraAlive)
|
||||
{
|
||||
if (AI.bite && AI.distance < pow(SECTOR(1), 2))
|
||||
{
|
||||
if (GetRandomControl() >= 0x4000)
|
||||
item->Animation.TargetState = 5;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
}
|
||||
item->Animation.TargetState = TestProbability(0.5f) ? 3 : 5;
|
||||
else
|
||||
{
|
||||
if (AI.zoneNumber == AI.enemyZone)
|
||||
|
@ -142,7 +140,7 @@ namespace TEN::Entities::TR2
|
|||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (laraAlive)
|
||||
if (isLaraAlive)
|
||||
{
|
||||
if (AI.bite && AI.distance < pow(SECTOR(2), 2))
|
||||
item->Animation.TargetState = 10;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void InitialiseSwordGuardian(short itemNumber);
|
||||
void SwordGuardianControl(short itemNumber);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
const auto WorkerDualGunBiteLeft = BiteInfo(Vector3(-2.0f, 275.0f, 23.0f), 6);
|
||||
const auto WorkerDualGunBiteRight = BiteInfo(Vector3(2.0f, 275.0f, 23.0f), 10);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void WorkerDualGunControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/trmath.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
const auto WorkerFlamethrowerOffset = Vector3Int(0, 140, 0);
|
||||
const auto WorkerFlamethrowerBite = BiteInfo(Vector3(0.0f, 250.0f, 32.0f), 9);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void InitialiseWorkerFlamethrower(short itemNumber);
|
||||
void WorkerFlamethrower(short itemNumber);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
const auto WorkerMachineGunBite = BiteInfo(Vector3(0.0f, 308.0f, 32.0f), 9);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void InitialiseWorkerMachineGun(short itemNumber);
|
||||
void WorkerMachineGunControl(short itemNumber);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
const auto WorkerShotgunBite = BiteInfo(Vector3(0.0f, 281.0f, 40.0f), 9);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void InitialiseWorkerShotgun(short itemNumber);
|
||||
void WorkerShotgunControl(short itemNumber);
|
||||
|
|
|
@ -13,11 +13,14 @@
|
|||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
const auto YetiBiteLeft = BiteInfo(Vector3(12.0f, 101.0f, 19.0f), 13);
|
||||
const auto YetiBiteLeft = BiteInfo(Vector3(12.0f, 101.0f, 19.0f), 13);
|
||||
const auto YetiBiteRight = BiteInfo(Vector3(12.0f, 101.0f, 19.0f), 10);
|
||||
const vector<int> YetiAttackJoints1 = { 10, 12 }; // TODO: Rename.
|
||||
const vector<int> YetiAttackJoints2 = { 8, 9, 10 };
|
||||
|
||||
// TODO
|
||||
enum YetiState
|
||||
|
@ -50,9 +53,9 @@ namespace TEN::Entities::TR2
|
|||
bool isLaraAlive = LaraItem->HitPoints > 0;
|
||||
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
short torso = 0;
|
||||
short head = 0;
|
||||
short tilt = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
|
@ -88,9 +91,9 @@ namespace TEN::Entities::TR2
|
|||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
else if (info->Mood == MoodType::Bored)
|
||||
{
|
||||
if (TestProbability(0.008f) || !isLaraAlive)
|
||||
if (TestProbability(1.0f / 128) || !isLaraAlive)
|
||||
item->Animation.TargetState = 7;
|
||||
else if (TestProbability(0.015f))
|
||||
else if (TestProbability(1.0f / 64))
|
||||
item->Animation.TargetState = 9;
|
||||
else if (TestProbability(0.025f))
|
||||
item->Animation.TargetState = 3;
|
||||
|
@ -116,9 +119,9 @@ namespace TEN::Entities::TR2
|
|||
{
|
||||
if (isLaraAlive)
|
||||
{
|
||||
if (TestProbability(0.008f))
|
||||
if (TestProbability(1.0f / 128))
|
||||
item->Animation.TargetState = 2;
|
||||
else if (TestProbability(0.015f))
|
||||
else if (TestProbability(1.0f / 64))
|
||||
item->Animation.TargetState = 9;
|
||||
else if (TestProbability(0.025f))
|
||||
{
|
||||
|
@ -127,7 +130,7 @@ namespace TEN::Entities::TR2
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (TestProbability(0.015f))
|
||||
else if (TestProbability(1.0f / 64))
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
@ -140,9 +143,9 @@ namespace TEN::Entities::TR2
|
|||
item->Animation.TargetState = 2;
|
||||
else if (info->Mood == MoodType::Bored)
|
||||
{
|
||||
if (TestProbability(0.008f) || !isLaraAlive)
|
||||
if (TestProbability(1.0f / 128) || !isLaraAlive)
|
||||
item->Animation.TargetState = 7;
|
||||
else if (TestProbability(0.015f))
|
||||
else if (TestProbability(1.0f / 64))
|
||||
item->Animation.TargetState = 2;
|
||||
else if (TestProbability(0.025f))
|
||||
{
|
||||
|
@ -150,7 +153,7 @@ namespace TEN::Entities::TR2
|
|||
item->Animation.RequiredState = 3;
|
||||
}
|
||||
}
|
||||
else if (TestProbability(0.015f))
|
||||
else if (TestProbability(1.0f / 64))
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
@ -165,12 +168,12 @@ namespace TEN::Entities::TR2
|
|||
item->Animation.TargetState = 1;
|
||||
else if (info->Mood == MoodType::Bored)
|
||||
{
|
||||
if (TestProbability(0.008f) || !isLaraAlive)
|
||||
if (TestProbability(1.0f / 128) || !isLaraAlive)
|
||||
{
|
||||
item->Animation.TargetState = 2;
|
||||
item->Animation.RequiredState = 7;
|
||||
}
|
||||
else if (TestProbability(0.015f))
|
||||
else if (TestProbability(1.0f / 64))
|
||||
{
|
||||
item->Animation.TargetState = 2;
|
||||
item->Animation.RequiredState = 9;
|
||||
|
@ -213,8 +216,7 @@ namespace TEN::Entities::TR2
|
|||
if (AI.ahead)
|
||||
torso = AI.angle;
|
||||
|
||||
if (!info->Flags &&
|
||||
item->TouchBits & 0x1400)
|
||||
if (!info->Flags && item->TestBits(JointBitType::Touch, YetiAttackJoints1))
|
||||
{
|
||||
CreatureEffect(item, YetiBiteRight, DoBloodSplat);
|
||||
DoDamage(info->Enemy, 100);
|
||||
|
@ -230,11 +232,12 @@ namespace TEN::Entities::TR2
|
|||
torso = AI.angle;
|
||||
|
||||
if (!info->Flags &&
|
||||
item->TouchBits & (0x0700 | 0x1400))
|
||||
(item->TestBits(JointBitType::Touch, YetiAttackJoints1) || item->TestBits(JointBitType::Touch, YetiAttackJoints2)))
|
||||
{
|
||||
if (item->TouchBits & 0x0700)
|
||||
if (item->TestBits(JointBitType::Touch, YetiAttackJoints2))
|
||||
CreatureEffect(item, YetiBiteLeft, DoBloodSplat);
|
||||
if (item->TouchBits & 0x1400)
|
||||
|
||||
if (item->TestBits(JointBitType::Touch, YetiAttackJoints1))
|
||||
CreatureEffect(item, YetiBiteRight, DoBloodSplat);
|
||||
|
||||
DoDamage(info->Enemy, 150);
|
||||
|
@ -248,11 +251,12 @@ namespace TEN::Entities::TR2
|
|||
torso = AI.angle;
|
||||
|
||||
if (!info->Flags &&
|
||||
item->TouchBits & (0x0700 | 0x1400))
|
||||
(item->TestBits(JointBitType::Touch, YetiAttackJoints1) || item->TestBits(JointBitType::Touch, YetiAttackJoints2)))
|
||||
{
|
||||
if (item->TouchBits & 0x0700)
|
||||
if (item->TestBits(JointBitType::Touch, YetiAttackJoints2))
|
||||
CreatureEffect(item, YetiBiteLeft, DoBloodSplat);
|
||||
if (item->TouchBits & 0x1400)
|
||||
|
||||
if (item->TestBits(JointBitType::Touch, YetiAttackJoints1))
|
||||
CreatureEffect(item, YetiBiteRight, DoBloodSplat);
|
||||
|
||||
DoDamage(info->Enemy, 200);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR2
|
||||
namespace TEN::Entities::Creatures::TR2
|
||||
{
|
||||
void InitialiseYeti(short itemNumber);
|
||||
void YetiControl(short itemNumber);
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#include "Objects/TR2/Vehicles/speedboat.h"
|
||||
#include "Objects/TR2/Vehicles/skidoo.h"
|
||||
|
||||
using namespace TEN::Entities::TR2;
|
||||
using namespace TEN::Entities::Creatures::TR2;
|
||||
|
||||
static void StartEntity(ObjectInfo* obj)
|
||||
{
|
||||
|
@ -57,7 +57,7 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->saveHitpoints = true;
|
||||
obj->saveAnim = true;
|
||||
obj->saveFlags = true;
|
||||
obj->zoneType = ZONE_WATER;
|
||||
obj->ZoneType = ZoneType::Water;
|
||||
|
||||
g_Level.Bones[obj->boneIndex + 9 * 4] |= ROT_Y;
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->saveHitpoints = true;
|
||||
obj->saveAnim = true;
|
||||
obj->saveFlags = true;
|
||||
obj->zoneType = ZONE_WATER;
|
||||
obj->ZoneType = ZoneType::Water;
|
||||
|
||||
g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y;
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->saveAnim = true;
|
||||
obj->saveFlags = true;
|
||||
obj->pivotLength = 0;
|
||||
obj->zoneType = ZONE_FLYER;
|
||||
obj->ZoneType = ZoneType::Flyer;
|
||||
}
|
||||
|
||||
obj = &Objects[ID_CROW];
|
||||
|
@ -118,7 +118,7 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->saveAnim = true;
|
||||
obj->saveFlags = true;
|
||||
obj->pivotLength = 0;
|
||||
obj->zoneType = ZONE_FLYER;
|
||||
obj->ZoneType = ZoneType::Flyer;
|
||||
}
|
||||
|
||||
obj = &Objects[ID_RAT];
|
||||
|
@ -154,7 +154,7 @@ static void StartEntity(ObjectInfo* obj)
|
|||
obj->saveFlags = true;
|
||||
obj->saveHitpoints = true;
|
||||
obj->savePosition = true;
|
||||
obj->zoneType = ZONE_HUMAN_CLASSIC;
|
||||
obj->ZoneType = ZoneType::HumanClassic;
|
||||
g_Level.Bones[obj->boneIndex + 6 * 4] |= (ROT_Y);
|
||||
g_Level.Bones[obj->boneIndex + 14 * 4] |= (ROT_Y);
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
constexpr auto CIVVY_ATTACK_DAMAGE = 40;
|
||||
constexpr auto CIVVY_SWIPE_DAMAGE = 50;
|
||||
|
@ -42,7 +42,6 @@ namespace TEN::Entities::TR3
|
|||
// TODO
|
||||
enum CivvyState
|
||||
{
|
||||
CIVVY_STATE_NONE,
|
||||
CIVVY_STATE_IDLE,
|
||||
CIVVY_STATE_WALK_FORWARD,
|
||||
CIVVY_PUNCH2,
|
||||
|
@ -89,11 +88,10 @@ namespace TEN::Entities::TR3
|
|||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short torsoX = 0;
|
||||
short torsoY = 0;
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
auto extraHeadRot = Vector3Shrt::Zero;
|
||||
auto extraTorsoRot = Vector3Shrt::Zero;
|
||||
|
||||
if (item->BoxNumber != NO_BOX && (g_Level.Boxes[item->BoxNumber].flags & BLOCKED))
|
||||
{
|
||||
|
@ -119,18 +117,18 @@ namespace TEN::Entities::TR3
|
|||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
AI_INFO laraAiInfo;
|
||||
AI_INFO laraAI;
|
||||
if (creature->Enemy == LaraItem)
|
||||
{
|
||||
laraAiInfo.angle = AI.angle;
|
||||
laraAiInfo.distance = AI.distance;
|
||||
laraAI.angle = AI.angle;
|
||||
laraAI.distance = AI.distance;
|
||||
}
|
||||
else
|
||||
{
|
||||
int laraDz = LaraItem->Pose.Position.z - item->Pose.Position.z;
|
||||
int laraDx = LaraItem->Pose.Position.x - item->Pose.Position.x;
|
||||
laraAiInfo.angle = phd_atan(laraDz, laraDx) - item->Pose.Orientation.y;
|
||||
laraAiInfo.distance = pow(laraDx, 2) + pow(laraDz, 2);
|
||||
laraAI.angle = phd_atan(laraDz, laraDx) - item->Pose.Orientation.y;
|
||||
laraAI.distance = pow(laraDx, 2) + pow(laraDz, 2);
|
||||
}
|
||||
|
||||
GetCreatureMood(item, &AI, true);
|
||||
|
@ -150,7 +148,7 @@ namespace TEN::Entities::TR3
|
|||
auto* realEnemy = creature->Enemy;
|
||||
creature->Enemy = LaraItem;
|
||||
|
||||
if ((laraAiInfo.distance < CIVVY_AWARE_RANGE || item->HitStatus || TargetVisible(item, &laraAiInfo)) &&
|
||||
if ((laraAI.distance < CIVVY_AWARE_RANGE || item->HitStatus || TargetVisible(item, &laraAI)) &&
|
||||
!(item->AIBits & FOLLOW))
|
||||
{
|
||||
if (!creature->Alerted)
|
||||
|
@ -170,13 +168,13 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
|
||||
case CIVVY_STATE_IDLE:
|
||||
head = laraAiInfo.angle;
|
||||
creature->MaxTurn = 0;
|
||||
creature->Flags = 0;
|
||||
extraHeadRot.y = laraAI.angle;
|
||||
|
||||
if (item->AIBits & GUARD)
|
||||
{
|
||||
head = AIGuard(creature);
|
||||
extraHeadRot.y = AIGuard(creature);
|
||||
if (!(GetRandomControl() & 0xFF))
|
||||
{
|
||||
if (item->Animation.ActiveState == CIVVY_STATE_IDLE)
|
||||
|
@ -199,7 +197,7 @@ namespace TEN::Entities::TR3
|
|||
item->Animation.TargetState = CIVVY_STATE_RUN_FORWARD;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Bored ||
|
||||
(item->AIBits & FOLLOW && (creature->ReachedGoal || laraAiInfo.distance > pow(SECTOR(2), 2))))
|
||||
(item->AIBits & FOLLOW && (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2))))
|
||||
{
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
|
@ -221,11 +219,11 @@ namespace TEN::Entities::TR3
|
|||
|
||||
case CIVVY_STATE_WALK_FORWARD:
|
||||
creature->MaxTurn = CIVVY_WALK_TURN_RATE_MAX;
|
||||
head = laraAiInfo.angle;
|
||||
extraHeadRot.y = laraAI.angle;
|
||||
|
||||
if (item->AIBits & PATROL1)
|
||||
{
|
||||
head = 0;
|
||||
extraHeadRot.y = 0;
|
||||
item->Animation.TargetState = CIVVY_STATE_WALK_FORWARD;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
|
@ -252,7 +250,7 @@ namespace TEN::Entities::TR3
|
|||
tilt = angle / 2;
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
if (item->AIBits & GUARD)
|
||||
item->Animation.TargetState = CIVVY_WAIT;
|
||||
|
@ -262,7 +260,7 @@ namespace TEN::Entities::TR3
|
|||
item->Animation.TargetState = CIVVY_STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
else if ((item->AIBits & FOLLOW) && (creature->ReachedGoal || laraAiInfo.distance > pow(SECTOR(2), 2)))
|
||||
else if ((item->AIBits & FOLLOW) && (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2)))
|
||||
item->Animation.TargetState = CIVVY_STATE_IDLE;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = CIVVY_STATE_WALK_FORWARD;
|
||||
|
@ -273,11 +271,12 @@ namespace TEN::Entities::TR3
|
|||
|
||||
case CIVVY_AIM0:
|
||||
creature->MaxTurn = CIVVY_WALK_TURN_RATE_MAX;
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (AI.bite && AI.distance < CIVVY_ATTACK0_RANGE)
|
||||
|
@ -285,16 +284,16 @@ namespace TEN::Entities::TR3
|
|||
else
|
||||
item->Animation.TargetState = CIVVY_STATE_IDLE;
|
||||
|
||||
creature->Flags = 0;
|
||||
break;
|
||||
|
||||
case CIVVY_AIM1:
|
||||
creature->MaxTurn = CIVVY_WALK_TURN_RATE_MAX;
|
||||
creature->Flags = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (AI.ahead && AI.distance < CIVVY_ATTACK1_RANGE)
|
||||
|
@ -302,7 +301,6 @@ namespace TEN::Entities::TR3
|
|||
else
|
||||
item->Animation.TargetState = CIVVY_STATE_IDLE;
|
||||
|
||||
creature->Flags = 0;
|
||||
break;
|
||||
|
||||
case CIVVY_AIM2:
|
||||
|
@ -311,8 +309,8 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (AI.bite && AI.distance < CIVVY_ATTACK2_RANGE)
|
||||
|
@ -327,8 +325,8 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, CivvyAttackJoints))
|
||||
|
@ -346,8 +344,8 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, CivvyAttackJoints))
|
||||
|
@ -368,8 +366,8 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (creature->Flags != 2 && item->TestBits(JointBitType::Touch, CivvyAttackJoints))
|
||||
|
@ -385,9 +383,9 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torsoY);
|
||||
CreatureJoint(item, 1, torsoX);
|
||||
CreatureJoint(item, 2, head);
|
||||
CreatureJoint(item, 0, extraTorsoRot.y);
|
||||
CreatureJoint(item, 1, extraTorsoRot.x);
|
||||
CreatureJoint(item, 2, extraHeadRot.y);
|
||||
|
||||
if (item->Animation.ActiveState < CIVVY_DEATH)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void InitialiseCivvy(short itemNumber);
|
||||
void CivvyControl(short itemNumber);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
constexpr auto COBRA_BITE_ATTACK_DAMAGE = 80;
|
||||
constexpr auto COBRA_BITE_POISON_POTENCY = 8;
|
||||
|
@ -23,8 +23,7 @@ namespace TEN::Entities::TR3
|
|||
constexpr auto COBRA_AWARE_RANGE = SQUARE(SECTOR(1.5f));
|
||||
constexpr auto COBRA_SLEEP_RANGE = SQUARE(SECTOR(2.5f));
|
||||
|
||||
constexpr auto PLAYER_DISTURB_VELOCITY = 15;
|
||||
|
||||
constexpr auto COBRA_DISTURBANCE_VELOCITY = 15;
|
||||
constexpr auto COBRA_SLEEP_FRAME = 45;
|
||||
|
||||
const auto CobraBite = BiteInfo(Vector3::Zero, 13);
|
||||
|
@ -42,7 +41,7 @@ namespace TEN::Entities::TR3
|
|||
enum CobraAnim
|
||||
{
|
||||
COBRA_ANIM_IDLE = 0,
|
||||
COBRA_ANIM_WAKE_UP = 1,
|
||||
COBRA_ANIM_SLEEP_TO_IDLE = 1,
|
||||
COBRA_ANIM_IDLE_TO_SLEEP = 2,
|
||||
COBRA_ANIM_BITE_ATTACK = 3,
|
||||
COBRA_ANIM_DEATH = 4
|
||||
|
@ -65,9 +64,9 @@ namespace TEN::Entities::TR3
|
|||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
short head = 0;
|
||||
|
||||
if (item->HitPoints <= 0 && item->HitPoints != NOT_TARGETABLE)
|
||||
{
|
||||
|
@ -84,19 +83,23 @@ namespace TEN::Entities::TR3
|
|||
GetCreatureMood(item, &AI, 1);
|
||||
CreatureMood(item, &AI, 1);
|
||||
|
||||
bool enemyMoving = false;
|
||||
bool enemyVisible = false;
|
||||
if (creature->Enemy && (GlobalCounter & 2))
|
||||
{
|
||||
auto src = GameVector(creature->Enemy->Pose.Position, creature->Enemy->RoomNumber);
|
||||
auto dest = GameVector(item->Pose.Position, item->RoomNumber);
|
||||
enemyVisible = LOS(&src, &dest);
|
||||
bool isEnemyMoving = false;
|
||||
bool isEnemyVisible = false;
|
||||
|
||||
enemyMoving = creature->Enemy->Animation.Velocity.z > PLAYER_DISTURB_VELOCITY ||
|
||||
abs(creature->Enemy->Animation.Velocity.y) > PLAYER_DISTURB_VELOCITY;
|
||||
if (creature->Enemy != nullptr && (GlobalCounter & 2))
|
||||
{
|
||||
auto origin = GameVector(creature->Enemy->Pose.Position, creature->Enemy->RoomNumber);
|
||||
auto target = GameVector(item->Pose.Position, item->RoomNumber);
|
||||
isEnemyVisible = LOS(&origin, &target);
|
||||
|
||||
if (creature->Enemy->Animation.Velocity.z > COBRA_DISTURBANCE_VELOCITY ||
|
||||
abs(creature->Enemy->Animation.Velocity.y) > COBRA_DISTURBANCE_VELOCITY)
|
||||
{
|
||||
isEnemyMoving = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (enemyVisible && item->Animation.ActiveState != COBRA_STATE_SLEEP)
|
||||
if (isEnemyVisible && item->Animation.ActiveState != COBRA_STATE_SLEEP)
|
||||
{
|
||||
creature->Target.x = creature->Enemy->Pose.Position.x;
|
||||
creature->Target.z = creature->Enemy->Pose.Position.z;
|
||||
|
@ -119,12 +122,10 @@ namespace TEN::Entities::TR3
|
|||
creature->Flags = 0;
|
||||
|
||||
if (AI.distance > COBRA_SLEEP_RANGE)
|
||||
{
|
||||
item->Animation.TargetState = COBRA_STATE_SLEEP;
|
||||
}
|
||||
else if (creature->Enemy->HitPoints > 0 && enemyVisible &&
|
||||
((AI.ahead && AI.distance < COBRA_ATTACK_RANGE && AI.verticalDistance <= GetBoundsAccurate(item)->Height()) ||
|
||||
item->HitStatus || enemyMoving))
|
||||
else if (creature->Enemy->HitPoints > 0 && isEnemyVisible &&
|
||||
((AI.ahead && AI.distance < COBRA_ATTACK_RANGE && AI.verticalDistance <= GetBoundsAccurate(item)->Height()) ||
|
||||
item->HitStatus || isEnemyMoving))
|
||||
{
|
||||
item->Animation.TargetState = COBRA_STATE_ATTACK;
|
||||
}
|
||||
|
@ -149,12 +150,12 @@ namespace TEN::Entities::TR3
|
|||
break;
|
||||
|
||||
case COBRA_STATE_ATTACK:
|
||||
if (creature->Flags != 1 &&
|
||||
if (!(creature->Flags & 1) && // 1 = is attacking.
|
||||
item->TestBits(JointBitType::Touch, CobraAttackJoints))
|
||||
{
|
||||
DoDamage(creature->Enemy, COBRA_BITE_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, CobraBite, DoBloodSplat);
|
||||
creature->Flags = 1;
|
||||
creature->Flags |= 1; // 1 = is attacking.
|
||||
|
||||
if (creature->Enemy->IsLara())
|
||||
GetLaraInfo(creature->Enemy)->PoisonPotency += COBRA_BITE_POISON_POTENCY;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void InitialiseCobra(short itemNum);
|
||||
void CobraControl(short itemNum);
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "Objects/TR3/fish.h"
|
||||
#include "Specific/level.h"
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
int PirahnaHitWait = false;
|
||||
int CarcassItem = NO_ITEM;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
#include "Game/items.h"
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void SetupShoal(int shoalNumber);
|
||||
void ControlFish(short itemNumber);
|
||||
|
|
|
@ -14,9 +14,12 @@
|
|||
#include "Game/people.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
using namespace TEN::Math::Random;
|
||||
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
const auto FlamethrowerOffset = Vector3Int(0, 340, 0);
|
||||
const auto FlamethrowerBite = BiteInfo(Vector3(0.0f, 340.0f, 64.0f), 7);
|
||||
|
@ -41,34 +44,27 @@ namespace TEN::Entities::TR3
|
|||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short torsoX = 0;
|
||||
short torsoY = 0;
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
short head = 0;
|
||||
auto extraHeadRot = Vector3Shrt::Zero;
|
||||
auto extraTorsoRot = Vector3Shrt::Zero;
|
||||
|
||||
auto pos = Vector3Int(FlamethrowerBite.Position);
|
||||
GetJointAbsPosition(item, &pos, FlamethrowerBite.meshNum);
|
||||
|
||||
int random = GetRandomControl();
|
||||
int randomInt = GetRandomControl();
|
||||
if (item->Animation.ActiveState != 6 && item->Animation.ActiveState != 11)
|
||||
{
|
||||
TriggerDynamicLight(pos.x, pos.y, pos.z, (random & 3) + 6, 24 - ((random / 16) & 3), 16 - ((random / 64) & 3), random & 3);
|
||||
TriggerDynamicLight(pos.x, pos.y, pos.z, (randomInt & 3) + 6, 24 - ((randomInt / 16) & 3), 16 - ((randomInt / 64) & 3), randomInt & 3);
|
||||
TriggerPilotFlame(itemNumber, 9);
|
||||
}
|
||||
else
|
||||
{
|
||||
TriggerDynamicLight(pos.x, pos.y, pos.z, (random & 3) + 10, 31 - ((random / 16) & 3), 24 - ((random / 64) & 3), random & 7);
|
||||
}
|
||||
TriggerDynamicLight(pos.x, pos.y, pos.z, (randomInt & 3) + 10, 31 - ((randomInt / 16) & 3), 24 - ((randomInt / 64) & 3), randomInt & 7);
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != 7)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 19;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 7;
|
||||
}
|
||||
SetAnimation(item, 19);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -83,9 +79,8 @@ namespace TEN::Entities::TR3
|
|||
ItemInfo* target = nullptr;
|
||||
int minDistance = INT_MAX;
|
||||
|
||||
for (int i = 0; i < ActiveCreatures.size(); i++)
|
||||
for (auto& currentCreature : ActiveCreatures)
|
||||
{
|
||||
auto* currentCreature = ActiveCreatures[i];
|
||||
if (currentCreature->ItemNumber == NO_ITEM || currentCreature->ItemNumber == itemNumber)
|
||||
continue;
|
||||
|
||||
|
@ -148,13 +143,13 @@ namespace TEN::Entities::TR3
|
|||
case 1:
|
||||
creature->MaxTurn = 0;
|
||||
creature->Flags = 0;
|
||||
head = laraAI.angle;
|
||||
extraHeadRot.y = laraAI.angle;
|
||||
|
||||
if (item->AIBits & GUARD)
|
||||
{
|
||||
head = AIGuard(creature);
|
||||
extraHeadRot.y = AIGuard(creature);
|
||||
|
||||
if (!(GetRandomControl() & 0xFF))
|
||||
if (TestProbability(1.0f / 128))
|
||||
item->Animation.TargetState = 4;
|
||||
|
||||
break;
|
||||
|
@ -170,21 +165,21 @@ namespace TEN::Entities::TR3
|
|||
else
|
||||
item->Animation.TargetState = 2;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Bored && AI.ahead && !(GetRandomControl() & 0xFF))
|
||||
else if (creature->Mood == MoodType::Bored && AI.ahead && TestProbability(1.0f / 128))
|
||||
item->Animation.TargetState = 4;
|
||||
else if (creature->Mood == MoodType::Attack || !(GetRandomControl() & 0xFF))
|
||||
else if (creature->Mood == MoodType::Attack || TestProbability(1.0f / 128))
|
||||
item->Animation.TargetState = 2;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
head = laraAI.angle;
|
||||
extraHeadRot.y = laraAI.angle;
|
||||
|
||||
if (item->AIBits & GUARD)
|
||||
{
|
||||
head = AIGuard(creature);
|
||||
extraHeadRot.y = AIGuard(creature);
|
||||
|
||||
if (!(GetRandomControl() & 0xFF))
|
||||
if (TestProbability(1.0f / 128))
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
break;
|
||||
|
@ -193,7 +188,7 @@ namespace TEN::Entities::TR3
|
|||
AI.distance < pow(SECTOR(4), 2) &&
|
||||
(realEnemy != LaraItem || creature->HurtByLara) ||
|
||||
creature->Mood != MoodType::Bored ||
|
||||
!(GetRandomControl() & 0xFF)))
|
||||
TestProbability(1.0f / 128)))
|
||||
{
|
||||
item->Animation.TargetState = 1;
|
||||
}
|
||||
|
@ -201,17 +196,12 @@ namespace TEN::Entities::TR3
|
|||
break;
|
||||
|
||||
case 2:
|
||||
creature->Flags = 0;
|
||||
creature->MaxTurn = ANGLE(5.0f);
|
||||
head = laraAI.angle;
|
||||
creature->Flags = 0;
|
||||
extraHeadRot.y = laraAI.angle;
|
||||
|
||||
if (item->AIBits & GUARD)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 12;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 1;
|
||||
item->Animation.TargetState = 1;
|
||||
}
|
||||
SetAnimation(item, 12);
|
||||
else if (item->AIBits & PATROL1)
|
||||
item->Animation.TargetState = 2;
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
|
@ -236,8 +226,8 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI) &&
|
||||
AI.distance < pow(SECTOR(4), 2) &&
|
||||
|
@ -256,8 +246,8 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI) &&
|
||||
AI.distance < pow(SECTOR(4), 2) &&
|
||||
|
@ -277,8 +267,8 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI) &&
|
||||
AI.distance < pow(SECTOR(4), 2) &&
|
||||
|
@ -312,12 +302,12 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI) &&
|
||||
AI.distance < pow(SECTOR(4), 2) &&
|
||||
(realEnemy != LaraItem || creature->HurtByLara))
|
||||
(!realEnemy->IsLara() || creature->HurtByLara))
|
||||
{
|
||||
item->Animation.TargetState = 6;
|
||||
}
|
||||
|
@ -344,9 +334,9 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torsoY);
|
||||
CreatureJoint(item, 1, torsoX);
|
||||
CreatureJoint(item, 2, head);
|
||||
CreatureJoint(item, 0, extraTorsoRot.y);
|
||||
CreatureJoint(item, 1, extraTorsoRot.x);
|
||||
CreatureJoint(item, 2, extraHeadRot.y);
|
||||
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void FlameThrowerControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -10,25 +10,88 @@
|
|||
#include "Game/Lara/lara.h"
|
||||
#include "Game/misc.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
const vector<int> MonkeyAttackJoints = { 10, 13 };
|
||||
// TODO: Work out damage constants.
|
||||
constexpr auto MONKEY_SWIPE_ATTACK_PLAYER_DAMAGE = 40;
|
||||
constexpr auto MONKEY_SWIPE_ATTACK_CREATURE_DAMAGE = 20;
|
||||
|
||||
// TODO: Range constants.
|
||||
|
||||
const auto MonkeyBite = BiteInfo(Vector3(10.0f, 10.0f, 11.0f), 13);
|
||||
const vector<int> MonkeyAttackJoints = { 10, 13 };
|
||||
|
||||
enum MonkeyState
|
||||
{
|
||||
// No states 0-1.
|
||||
MONKEY_STATE_WALK_FORWARD = 2,
|
||||
MONKEY_STATE_IDLE = 3,
|
||||
MONKEY_STATE_RUN_FORWARD = 4,
|
||||
MONKEY_STATE_BITE_ATTACK = 5, // Check.
|
||||
MONKEY_STATE_SIT = 6,
|
||||
MONKEY_STATE_SIT_EAT = 7,
|
||||
MONKEY_STATE_SIT_SCRATCH = 8,
|
||||
MONKEY_STATE_RUN_FORWARD_ROLL = 9,
|
||||
MONKEY_STATE_POUND_GROUND = 10,
|
||||
MONKEY_STATE_DEATH = 11,
|
||||
MONKEY_STATE_SWIPE_ATTACK = 12,
|
||||
MONKEY_STATE_JUMP_ATTACK = 13,
|
||||
MONKEY_STATE_HIGH_JUMP_ATTACK = 14,
|
||||
MONKEY_STATE_VAULT_UP_1_BLOCK = 15,
|
||||
MONKEY_STATE_VAULT_UP_0_POINT_3_BLOCKS = 16,
|
||||
MONKEY_STATE_VAULT_UP_0_POINT_2_BLOCKS = 17,
|
||||
MONKEY_STATE_VAULT_DOWN_1_BLOCK = 18,
|
||||
MONKEY_STATE_VAULT_DOWN_0_POINT_3_BLOCKS = 19,
|
||||
MONKEY_STATE_VAULT_DOWN_0_POINT_2_BLOCKS = 20
|
||||
};
|
||||
|
||||
enum MonkeyAnim
|
||||
{
|
||||
MONKEY_ANIM_WALK_FORWARD = 0,
|
||||
MONKEY_ANIM_WALK_FORWARD_TO_SIT = 1,
|
||||
MONKEY_ANIM_SIT = 2,
|
||||
MONKEY_ANIM_SIT_TO_WALK_FORWARD = 3,
|
||||
MONKEY_ANIM_SIT_EAT = 4,
|
||||
MONKEY_ANIM_SIT_SCRATCH = 5,
|
||||
MONKEY_ANIM_RUN_FORWARD = 6,
|
||||
MONKEY_ANIM_RUN_FORWARD_ROLL = 7,
|
||||
MONKEY_ANIM_IDLE_POUND_GROUND = 8,
|
||||
MONKEY_ANIM_IDLE = 9,
|
||||
MONKEY_ANIM_IDLE_TO_RUN_FORWARD = 10,
|
||||
MONKEY_ANIM_WALK_FORWARD_TO_RUN_FORWARD = 11,
|
||||
MONKEY_ANIM_RUN_FORWARD_TO_IDLE = 12,
|
||||
MONKEY_ANIM_SIT_TO_IDLE = 13,
|
||||
MONKEY_ANIM_DEATH = 14,
|
||||
MONKEY_ANIM_RUN_FORWARD_TO_WALK_FORWARD = 15,
|
||||
MONKEY_ANIM_IDLE_TO_SIT = 16,
|
||||
MONKEY_ANIM_VAULT_UP_1_BLOCK = 17,
|
||||
MONKEY_ANIM_VAULT_UP_0_POINT_3_BLOCKS = 18,
|
||||
MONKEY_ANIM_VAULT_UP_0_POINT_2_BLOCKS = 19,
|
||||
MONKEY_ANIM_VAULT_DOWN_1_BLOCK = 20,
|
||||
MONKEY_ANIM_VAULT_DOWN_0_POINT_3_BLOCKS = 21,
|
||||
MONKEY_ANIM_VAULT_DOWN_0_POINT_2_BLOCKS = 22,
|
||||
MONKEY_ANIM_SWIPE_ATTACK = 23,
|
||||
MONKEY_ANIM_JUMP_ATTACK = 24,
|
||||
MONKEY_ANIM_BITE_ATTACK = 25,
|
||||
MONKEY_ANIM_HIGH_JUMP_ATTACK_START = 26,
|
||||
MONKEY_ANIM_HIGH_JUMP_ATTACK_CONTINUE = 27,
|
||||
MONKEY_ANIM_HIGH_JUMP_ATTACK_END = 28,
|
||||
MONKEY_ANIM_IDLE_TO_WALK_FORWARD = 29,
|
||||
MONKEY_ANIM_WALK_FORWARD_TO_IDLE = 30
|
||||
};
|
||||
|
||||
void InitialiseMonkey(short itemNumber)
|
||||
{
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
||||
ClearItem(itemNumber);
|
||||
|
||||
item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 2;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 6;
|
||||
item->Animation.TargetState = 6;
|
||||
SetAnimation(item, MONKEY_ANIM_SIT);
|
||||
}
|
||||
|
||||
void MonkeyControl(short itemNumber)
|
||||
|
@ -39,20 +102,17 @@ namespace TEN::Entities::TR3
|
|||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short headX = 0;
|
||||
short headY = 0;
|
||||
short torsoY = 0;
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
auto extraHeadRot = Vector3Shrt::Zero;
|
||||
auto extraTorsoRot = Vector3Shrt::Zero;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != 11)
|
||||
if (item->Animation.ActiveState != MONKEY_STATE_DEATH)
|
||||
{
|
||||
SetAnimation(item, MONKEY_ANIM_DEATH);
|
||||
item->MeshBits = ALL_JOINT_BITS;
|
||||
item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 14;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 11;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -63,12 +123,11 @@ namespace TEN::Entities::TR3
|
|||
creature->Enemy = LaraItem;
|
||||
else
|
||||
{
|
||||
int minDistance = 0x7FFFFFFF;
|
||||
creature->Enemy = nullptr;
|
||||
int minDistance = INT_MAX;
|
||||
|
||||
for (int i = 0; i < ActiveCreatures.size(); i++)
|
||||
for (auto& currentCreature : ActiveCreatures)
|
||||
{
|
||||
auto* currentCreature = ActiveCreatures[i];
|
||||
if (currentCreature->ItemNumber == NO_ITEM || currentCreature->ItemNumber == itemNumber)
|
||||
continue;
|
||||
|
||||
|
@ -109,11 +168,11 @@ namespace TEN::Entities::TR3
|
|||
AI_INFO AI;
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
if (!creature->HurtByLara && creature->Enemy == LaraItem)
|
||||
if (!creature->HurtByLara && creature->Enemy->IsLara())
|
||||
creature->Enemy = nullptr;
|
||||
|
||||
AI_INFO laraAI;
|
||||
if (creature->Enemy == LaraItem)
|
||||
if (creature->Enemy->IsLara())
|
||||
{
|
||||
laraAI.angle = AI.angle;
|
||||
laraAI.distance = AI.distance;
|
||||
|
@ -146,144 +205,147 @@ namespace TEN::Entities::TR3
|
|||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 6:
|
||||
creature->Flags = 0;
|
||||
case MONKEY_STATE_SIT:
|
||||
creature->MaxTurn = 0;
|
||||
torsoY = laraAI.angle;
|
||||
creature->Flags = 0;
|
||||
extraTorsoRot.y = laraAI.angle;
|
||||
|
||||
if (item->AIBits & GUARD)
|
||||
{
|
||||
torsoY = AIGuard(creature);
|
||||
if (!(GetRandomControl() & 0xF))
|
||||
extraTorsoRot.y = AIGuard(creature);
|
||||
if (TestProbability(0.06f))
|
||||
{
|
||||
if (GetRandomControl() & 0x1)
|
||||
item->Animation.TargetState = 8;
|
||||
if (TestProbability(0.5f))
|
||||
item->Animation.TargetState = MONKEY_STATE_SIT_EAT;
|
||||
else
|
||||
item->Animation.TargetState = 7;
|
||||
item->Animation.TargetState = MONKEY_STATE_SIT_SCRATCH;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
else if (item->AIBits & PATROL1)
|
||||
item->Animation.TargetState = 2;
|
||||
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
else if (!(GetRandomControl() & 0xF))
|
||||
item->Animation.TargetState = 2;
|
||||
else if (!(GetRandomControl() & 0xF))
|
||||
else if (TestProbability(0.06f))
|
||||
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||
else if (TestProbability(0.06f))
|
||||
{
|
||||
if (GetRandomControl() & 0x1)
|
||||
item->Animation.TargetState = 8;
|
||||
if (TestProbability(0.5f))
|
||||
item->Animation.TargetState = MONKEY_STATE_SIT_EAT;
|
||||
else
|
||||
item->Animation.TargetState = 7;
|
||||
item->Animation.TargetState = MONKEY_STATE_SIT_SCRATCH;
|
||||
}
|
||||
}
|
||||
else if ((item->AIBits & FOLLOW) && (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2)))
|
||||
else if ((item->AIBits & FOLLOW) &&
|
||||
(creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2)))
|
||||
{
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
else if (AI.ahead)
|
||||
item->Animation.TargetState = 6;
|
||||
item->Animation.TargetState = MONKEY_STATE_SIT;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||
}
|
||||
else if (AI.bite && AI.distance < pow(682, 2))
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||
else if (AI.bite && AI.distance < pow(682, 2))
|
||||
item->Animation.TargetState = 2;
|
||||
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case MONKEY_STATE_IDLE:
|
||||
creature->MaxTurn = 0;
|
||||
creature->Flags = 0;
|
||||
torsoY = laraAI.angle;
|
||||
extraTorsoRot.y = laraAI.angle;
|
||||
|
||||
if (item->AIBits & GUARD)
|
||||
{
|
||||
torsoY = AIGuard(creature);
|
||||
extraTorsoRot.y = AIGuard(creature);
|
||||
|
||||
if (!(GetRandomControl() & 15))
|
||||
if (TestProbability(0.06f))
|
||||
{
|
||||
if (GetRandomControl() & 1)
|
||||
item->Animation.TargetState = 10;
|
||||
if (TestProbability(0.5f))
|
||||
item->Animation.TargetState = MONKEY_STATE_POUND_GROUND;
|
||||
else
|
||||
item->Animation.TargetState = 6;
|
||||
item->Animation.TargetState = MONKEY_STATE_SIT;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else if (item->AIBits & PATROL1)
|
||||
item->Animation.TargetState = 2;
|
||||
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
{
|
||||
if (Lara.TargetEntity != item && AI.ahead)
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||
else
|
||||
item->Animation.TargetState = 4;
|
||||
item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
else if (!(GetRandomControl() & 15))
|
||||
item->Animation.TargetState = 2;
|
||||
else if (!(GetRandomControl() & 15))
|
||||
else if (TestProbability(0.06f))
|
||||
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||
else if (TestProbability(0.06f))
|
||||
{
|
||||
if (GetRandomControl() & 1)
|
||||
item->Animation.TargetState = 10;
|
||||
if (TestProbability(0.5f))
|
||||
item->Animation.TargetState = MONKEY_STATE_POUND_GROUND;
|
||||
else
|
||||
item->Animation.TargetState = 6;
|
||||
item->Animation.TargetState = MONKEY_STATE_SIT;
|
||||
}
|
||||
}
|
||||
else if (item->AIBits & FOLLOW && (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2)))
|
||||
else if (item->AIBits & FOLLOW &&
|
||||
(creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2)))
|
||||
{
|
||||
if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
else if (AI.ahead)
|
||||
item->Animation.TargetState = 6;
|
||||
item->Animation.TargetState = MONKEY_STATE_SIT;
|
||||
else
|
||||
item->Animation.TargetState = 4;
|
||||
item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD;
|
||||
}
|
||||
else if (AI.bite && AI.distance < pow(341, 2))
|
||||
{
|
||||
if (LaraItem->Pose.Position.y < item->Pose.Position.y)
|
||||
item->Animation.TargetState = 13;
|
||||
item->Animation.TargetState = MONKEY_STATE_JUMP_ATTACK;
|
||||
else
|
||||
item->Animation.TargetState = 12;
|
||||
item->Animation.TargetState = MONKEY_STATE_SWIPE_ATTACK;
|
||||
}
|
||||
else if (AI.bite && AI.distance < pow(682, 2))
|
||||
item->Animation.TargetState = 14;
|
||||
item->Animation.TargetState = MONKEY_STATE_HIGH_JUMP_ATTACK;
|
||||
else if (AI.bite && AI.distance < pow(682, 2))
|
||||
item->Animation.TargetState = 2;
|
||||
else if (AI.distance < pow(682, 2) && creature->Enemy != LaraItem && creature->Enemy != nullptr &&
|
||||
creature->Enemy->ObjectNumber != ID_AI_PATROL1 && creature->Enemy->ObjectNumber != ID_AI_PATROL2 &&
|
||||
abs(item->Pose.Position.y - creature->Enemy->Pose.Position.y) < 256)
|
||||
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||
else if (AI.distance < pow(682, 2) &&
|
||||
!creature->Enemy->IsLara() && creature->Enemy != nullptr &&
|
||||
creature->Enemy->ObjectNumber != ID_AI_PATROL1 &&
|
||||
creature->Enemy->ObjectNumber != ID_AI_PATROL2 &&
|
||||
abs(item->Pose.Position.y - creature->Enemy->Pose.Position.y) < CLICK(1))
|
||||
{
|
||||
item->Animation.TargetState = 5;
|
||||
item->Animation.TargetState = MONKEY_STATE_BITE_ATTACK;
|
||||
}
|
||||
else if (AI.bite && AI.distance < pow(SECTOR(1), 2))
|
||||
item->Animation.TargetState = 9;
|
||||
item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD_ROLL;
|
||||
else
|
||||
item->Animation.TargetState = 4;
|
||||
item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD;
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
case MONKEY_STATE_BITE_ATTACK:
|
||||
creature->ReachedGoal = true;
|
||||
|
||||
if (creature->Enemy == nullptr)
|
||||
break;
|
||||
else if ((creature->Enemy->ObjectNumber == ID_SMALLMEDI_ITEM ||
|
||||
creature->Enemy->ObjectNumber == ID_KEY_ITEM4) &&
|
||||
item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 12)
|
||||
item->Animation.FrameNumber == (g_Level.Anims[item->Animation.AnimNumber].frameBase + 12))
|
||||
{
|
||||
if (creature->Enemy->RoomNumber == NO_ROOM ||
|
||||
creature->Enemy->Status == ITEM_INVISIBLE ||
|
||||
|
@ -298,9 +360,8 @@ namespace TEN::Entities::TR3
|
|||
creature->Enemy->RoomNumber = NO_ROOM;
|
||||
creature->Enemy->CarriedItem = NO_ITEM;
|
||||
|
||||
for (int i = 0; i < ActiveCreatures.size(); i++)
|
||||
for (auto& currentCreature : ActiveCreatures)
|
||||
{
|
||||
auto* currentCreature = ActiveCreatures[i];
|
||||
if (currentCreature->ItemNumber == NO_ITEM || currentCreature->ItemNumber == itemNumber)
|
||||
continue;
|
||||
|
||||
|
@ -318,15 +379,14 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (creature->Enemy->ObjectNumber == ID_AI_AMBUSH && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 12)
|
||||
else if (creature->Enemy->ObjectNumber == ID_AI_AMBUSH &&
|
||||
item->Animation.FrameNumber == (g_Level.Anims[item->Animation.AnimNumber].frameBase + 12))
|
||||
{
|
||||
item->AIBits = 0;
|
||||
|
||||
auto* carriedItem = &g_Level.Items[item->CarriedItem];
|
||||
|
||||
carriedItem->Pose.Position.x = item->Pose.Position.x;
|
||||
carriedItem->Pose.Position.y = item->Pose.Position.y;
|
||||
carriedItem->Pose.Position.z = item->Pose.Position.z;
|
||||
carriedItem->Pose.Position = item->Pose.Position;
|
||||
|
||||
ItemNewRoom(item->CarriedItem, item->RoomNumber);
|
||||
item->CarriedItem = NO_ITEM;
|
||||
|
@ -348,60 +408,106 @@ namespace TEN::Entities::TR3
|
|||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case MONKEY_STATE_WALK_FORWARD:
|
||||
creature->MaxTurn = ANGLE(7.0f);
|
||||
torsoY = laraAI.angle;
|
||||
extraTorsoRot.y = laraAI.angle;
|
||||
|
||||
if (item->AIBits & PATROL1)
|
||||
{
|
||||
item->Animation.TargetState = 2;
|
||||
torsoY = 0;
|
||||
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||
extraTorsoRot.y = 0;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = 4;
|
||||
item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (GetRandomControl() < 256)
|
||||
item->Animation.TargetState = 6;
|
||||
if (TestProbability(1.0f / 128))
|
||||
item->Animation.TargetState = MONKEY_STATE_SIT;
|
||||
}
|
||||
else if (AI.bite && AI.distance < pow(682, 2))
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case MONKEY_STATE_RUN_FORWARD:
|
||||
creature->MaxTurn = ANGLE(11.0f);
|
||||
tilt = angle / 2;
|
||||
|
||||
if (AI.ahead)
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
|
||||
if (item->AIBits & GUARD)
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||
else if (creature->Mood == MoodType::Escape)
|
||||
{
|
||||
if (Lara.TargetEntity != item && AI.ahead)
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||
|
||||
break;
|
||||
}
|
||||
else if ((item->AIBits & FOLLOW) && (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2)))
|
||||
item->Animation.TargetState = 3;
|
||||
else if ((item->AIBits & FOLLOW) &&
|
||||
(creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2)))
|
||||
{
|
||||
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 9;
|
||||
item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD_ROLL;
|
||||
else if (AI.distance < pow(682, 2))
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||
else if (AI.bite && AI.distance < pow(SECTOR(1), 2))
|
||||
item->Animation.TargetState = 9;
|
||||
item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD_ROLL;
|
||||
|
||||
break;
|
||||
|
||||
case 12:
|
||||
case MONKEY_STATE_SWIPE_ATTACK:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
headY = AI.angle;
|
||||
headX = AI.xAngle;
|
||||
extraHeadRot.x = AI.xAngle;
|
||||
extraHeadRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (abs(AI.angle) < ANGLE(7.0f))
|
||||
item->Pose.Orientation.y += AI.angle;
|
||||
else if (AI.angle < 0)
|
||||
item->Pose.Orientation.y -= ANGLE(7.0f);
|
||||
else
|
||||
item->Pose.Orientation.y += ANGLE(7.0f);
|
||||
|
||||
if (enemy->IsLara())
|
||||
{
|
||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, MonkeyAttackJoints))
|
||||
{
|
||||
DoDamage(enemy, MONKEY_SWIPE_ATTACK_PLAYER_DAMAGE);
|
||||
CreatureEffect(item, MonkeyBite, DoBloodSplat);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!creature->Flags && enemy)
|
||||
{
|
||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < CLICK(1) &&
|
||||
abs(enemy->Pose.Position.y - item->Pose.Position.y) <= CLICK(1) &&
|
||||
abs(enemy->Pose.Position.z - item->Pose.Position.z) < CLICK(1))
|
||||
{
|
||||
DoDamage(enemy, MONKEY_SWIPE_ATTACK_CREATURE_DAMAGE);
|
||||
CreatureEffect(item, MonkeyBite, DoBloodSplat);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MONKEY_STATE_JUMP_ATTACK:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
extraHeadRot.x = AI.xAngle;
|
||||
extraHeadRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (abs(AI.angle) < ANGLE(7.0f))
|
||||
|
@ -422,11 +528,9 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!creature->Flags && enemy)
|
||||
if (!creature->Flags && enemy != nullptr)
|
||||
{
|
||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < CLICK(1) &&
|
||||
abs(enemy->Pose.Position.y - item->Pose.Position.y) <= CLICK(1) &&
|
||||
abs(enemy->Pose.Position.z - item->Pose.Position.z) < CLICK(1))
|
||||
if (Vector3Int::Distance(item->Pose.Position, enemy->Pose.Position) <= CLICK(1))
|
||||
{
|
||||
DoDamage(enemy, 20);
|
||||
CreatureEffect(item, MonkeyBite, DoBloodSplat);
|
||||
|
@ -437,55 +541,13 @@ namespace TEN::Entities::TR3
|
|||
|
||||
break;
|
||||
|
||||
case 13:
|
||||
case MONKEY_STATE_HIGH_JUMP_ATTACK:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
headY = AI.angle;
|
||||
headX = AI.xAngle;
|
||||
}
|
||||
|
||||
if (abs(AI.angle) < ANGLE(7.0f))
|
||||
item->Pose.Orientation.y += AI.angle;
|
||||
else if (AI.angle < 0)
|
||||
item->Pose.Orientation.y -= ANGLE(7.0f);
|
||||
else
|
||||
item->Pose.Orientation.y += ANGLE(7.0f);
|
||||
|
||||
if (enemy->IsLara())
|
||||
{
|
||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, MonkeyAttackJoints))
|
||||
{
|
||||
DoDamage(enemy, 40);
|
||||
CreatureEffect(item, MonkeyBite, DoBloodSplat);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!creature->Flags && enemy)
|
||||
{
|
||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < CLICK(1) &&
|
||||
abs(enemy->Pose.Position.y - item->Pose.Position.y) <= CLICK(1) &&
|
||||
abs(enemy->Pose.Position.z - item->Pose.Position.z) < CLICK(1))
|
||||
{
|
||||
DoDamage(enemy, 20);
|
||||
CreatureEffect(item, MonkeyBite, DoBloodSplat);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 14:
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
headX = AI.xAngle;
|
||||
headY = AI.angle;
|
||||
extraHeadRot.x = AI.xAngle;
|
||||
extraHeadRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (abs(AI.angle) < ANGLE(7.0f))
|
||||
|
@ -508,9 +570,7 @@ namespace TEN::Entities::TR3
|
|||
{
|
||||
if (creature->Flags != 1 && enemy)
|
||||
{
|
||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < CLICK(1) &&
|
||||
abs(enemy->Pose.Position.y - item->Pose.Position.y) <= CLICK(1) &&
|
||||
abs(enemy->Pose.Position.z - item->Pose.Position.z) < CLICK(1))
|
||||
if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= CLICK(1))
|
||||
{
|
||||
DoDamage(enemy, 25);
|
||||
CreatureEffect(item, MonkeyBite, DoBloodSplat);
|
||||
|
@ -524,54 +584,42 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, headY);
|
||||
CreatureJoint(item, 1, headX);
|
||||
CreatureJoint(item, 2, torsoY);
|
||||
CreatureJoint(item, 0, extraHeadRot.y);
|
||||
CreatureJoint(item, 1, extraHeadRot.x);
|
||||
CreatureJoint(item, 2, extraTorsoRot.y);
|
||||
|
||||
if (item->Animation.ActiveState < 15)
|
||||
if (item->Animation.ActiveState < MONKEY_STATE_VAULT_UP_1_BLOCK)
|
||||
{
|
||||
switch (CreatureVault(itemNumber, angle, 2, 128))
|
||||
switch (CreatureVault(itemNumber, angle, 2, CLICK(0.5f)))
|
||||
{
|
||||
case 2:
|
||||
SetAnimation(item, MONKEY_ANIM_VAULT_UP_0_POINT_2_BLOCKS);
|
||||
creature->MaxTurn = 0;
|
||||
item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 19;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 17;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
SetAnimation(item, MONKEY_ANIM_VAULT_UP_0_POINT_3_BLOCKS);
|
||||
creature->MaxTurn = 0;
|
||||
item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 18;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 16;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
SetAnimation(item, MONKEY_ANIM_VAULT_UP_1_BLOCK);
|
||||
creature->MaxTurn = 0;
|
||||
item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 17;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 15;
|
||||
break;
|
||||
|
||||
case -2:
|
||||
SetAnimation(item, MONKEY_ANIM_VAULT_DOWN_0_POINT_2_BLOCKS);
|
||||
creature->MaxTurn = 0;
|
||||
item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 22;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 20;
|
||||
break;
|
||||
|
||||
case -3:
|
||||
SetAnimation(item, MONKEY_ANIM_VAULT_DOWN_0_POINT_3_BLOCKS);
|
||||
creature->MaxTurn = 0;
|
||||
item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 21;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 19;
|
||||
break;
|
||||
|
||||
case -4:
|
||||
SetAnimation(item, MONKEY_ANIM_VAULT_DOWN_1_BLOCK);
|
||||
creature->MaxTurn = 0;
|
||||
item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 20;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 18;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void InitialiseMonkey(short itemNumber);
|
||||
void MonkeyControl(short itemNumber);
|
||||
|
|
|
@ -14,15 +14,17 @@
|
|||
#include "Game/people.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
using namespace TEN::Math::Random;
|
||||
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
const auto MPGunBite = BiteInfo(Vector3(0.0f, 160.0f, 40.0f), 13);
|
||||
|
||||
enum MPGunState
|
||||
{
|
||||
MPGUN_STATE_NONE = 0,
|
||||
MPGUN_STATE_WAIT = 1,
|
||||
MPGUN_STATE_WALK = 2,
|
||||
MPGUN_STATE_RUN = 3,
|
||||
|
@ -59,11 +61,10 @@ namespace TEN::Entities::TR3
|
|||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
short torsoX = 0;
|
||||
short torsoY = 0;
|
||||
short head = 0;
|
||||
auto extraTorsoRot = Vector3Shrt::Zero;
|
||||
|
||||
if (creature->FiredWeapon)
|
||||
{
|
||||
|
@ -76,8 +77,8 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (item->BoxNumber != NO_BOX && (g_Level.Boxes[item->BoxNumber].flags & BLOCKED))
|
||||
{
|
||||
DoLotsOfBlood(item->Pose.Position.x, item->Pose.Position.y - (GetRandomControl() & 255) - 32, item->Pose.Position.z, (GetRandomControl() & 127) + 128, GetRandomControl() * 2, item->RoomNumber, 3);
|
||||
DoDamage(item, 20);
|
||||
DoLotsOfBlood(item->Pose.Position.x, item->Pose.Position.y - (GetRandomControl() & 255) - 32, item->Pose.Position.z, (GetRandomControl() & 127) + 128, GetRandomControl() * 2, item->RoomNumber, 3);
|
||||
}
|
||||
|
||||
AI_INFO AI;
|
||||
|
@ -91,7 +92,7 @@ namespace TEN::Entities::TR3
|
|||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 13;
|
||||
}
|
||||
else if (!(GetRandomControl() & 3) && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 1)
|
||||
else if (TestProbability(0.25f) && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 1)
|
||||
{
|
||||
CreatureAIInfo(item, &AI);
|
||||
|
||||
|
@ -101,8 +102,8 @@ namespace TEN::Entities::TR3
|
|||
AI.angle < ANGLE(45.0f))
|
||||
{
|
||||
head = AI.angle;
|
||||
torsoY = AI.angle;
|
||||
ShotLara(item, &AI, MPGunBite, torsoY, 32);
|
||||
extraTorsoRot.y = AI.angle;
|
||||
ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32);
|
||||
SoundEffect(SFX_TR3_OIL_SMG_FIRE, &item->Pose, SoundEnvironment::Land, 1.0f, 0.7f);
|
||||
creature->FiredWeapon = 1;
|
||||
}
|
||||
|
@ -220,10 +221,9 @@ namespace TEN::Entities::TR3
|
|||
item->Animation.TargetState = MPGUN_STATE_RUN;
|
||||
else if (Targetable(item, &AI))
|
||||
{
|
||||
int random = GetRandomControl();
|
||||
if (random < 0x2000)
|
||||
if (TestProbability(0.25f))
|
||||
item->Animation.TargetState = MPGUN_STATE_SHOOT_1;
|
||||
else if (random < 0x4000)
|
||||
else if (TestProbability(0.5f))
|
||||
item->Animation.TargetState = MPGUN_STATE_SHOOT_2;
|
||||
else
|
||||
item->Animation.TargetState = MPGUN_STATE_AIM_3;
|
||||
|
@ -305,17 +305,18 @@ namespace TEN::Entities::TR3
|
|||
case MPGUN_STATE_AIM_1:
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 12 ||
|
||||
(item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 1 && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 10))
|
||||
(item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 1 &&
|
||||
item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 10))
|
||||
{
|
||||
if (!ShotLara(item, &AI, MPGunBite, torsoY, 32))
|
||||
if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32))
|
||||
item->Animation.RequiredState = MPGUN_STATE_WAIT;
|
||||
}
|
||||
else if (item->HitStatus && !(GetRandomControl() & 0x3) && cover)
|
||||
else if (item->HitStatus && TestProbability(0.25f) && cover)
|
||||
{
|
||||
item->Animation.RequiredState = MPGUN_STATE_CROUCH;
|
||||
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
||||
|
@ -326,8 +327,8 @@ namespace TEN::Entities::TR3
|
|||
case MPGUN_STATE_SHOOT_1:
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (item->Animation.RequiredState == MPGUN_STATE_WAIT)
|
||||
|
@ -338,16 +339,16 @@ namespace TEN::Entities::TR3
|
|||
case MPGUN_STATE_SHOOT_2:
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase)
|
||||
{
|
||||
if (!ShotLara(item, &AI, MPGunBite, torsoY, 32))
|
||||
if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32))
|
||||
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
||||
}
|
||||
else if (item->HitStatus && !(GetRandomControl() & 0x3) && cover)
|
||||
else if (item->HitStatus && TestProbability(0.25f) && cover)
|
||||
{
|
||||
item->Animation.RequiredState = MPGUN_STATE_CROUCH;
|
||||
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
||||
|
@ -359,17 +360,17 @@ namespace TEN::Entities::TR3
|
|||
case MPGUN_STATE_SHOOT_3B:
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase ||
|
||||
item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 11)
|
||||
{
|
||||
if (!ShotLara(item, &AI, MPGunBite, torsoY, 32))
|
||||
if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32))
|
||||
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
||||
}
|
||||
else if (item->HitStatus && !(GetRandomControl() & 0x3) && cover)
|
||||
else if (item->HitStatus && TestProbability(0.25f) && cover)
|
||||
{
|
||||
item->Animation.RequiredState = MPGUN_STATE_CROUCH;
|
||||
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
||||
|
@ -380,17 +381,19 @@ namespace TEN::Entities::TR3
|
|||
case MPGUN_STATE_AIM_4:
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if ((item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 18 && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 17) ||
|
||||
(item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 19 && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 6))
|
||||
if ((item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 18 &&
|
||||
item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 17) ||
|
||||
(item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 19 &&
|
||||
item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 6))
|
||||
{
|
||||
if (!ShotLara(item, &AI, MPGunBite, torsoY, 32))
|
||||
if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32))
|
||||
item->Animation.RequiredState = MPGUN_STATE_WALK;
|
||||
}
|
||||
else if (item->HitStatus && !(GetRandomControl() & 0x3) && cover)
|
||||
else if (item->HitStatus && TestProbability(0.25f) && cover)
|
||||
{
|
||||
item->Animation.RequiredState = MPGUN_STATE_CROUCH;
|
||||
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
||||
|
@ -405,8 +408,8 @@ namespace TEN::Entities::TR3
|
|||
case MPGUN_STATE_SHOOT_4B:
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (item->Animation.RequiredState == MPGUN_STATE_WALK)
|
||||
|
@ -414,7 +417,7 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 16)
|
||||
{
|
||||
if (!ShotLara(item, &AI, MPGunBite, torsoY, 32))
|
||||
if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32))
|
||||
item->Animation.TargetState = MPGUN_STATE_WALK;
|
||||
}
|
||||
|
||||
|
@ -431,7 +434,7 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = MPGUN_STATE_CROUCH_AIM;
|
||||
else if (item->HitStatus || !cover || (AI.ahead && !(GetRandomControl() & 0x1F)))
|
||||
else if (item->HitStatus || !cover || (AI.ahead && TestProbability(1.0f / 30)))
|
||||
item->Animation.TargetState = MPGUN_STATE_STAND;
|
||||
else
|
||||
item->Animation.TargetState = MPGUN_STATE_CROUCH_WALK;
|
||||
|
@ -442,7 +445,7 @@ namespace TEN::Entities::TR3
|
|||
creature->MaxTurn = ANGLE(1.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI))
|
||||
item->Animation.TargetState = MPGUN_STATE_CROUCH_SHOT;
|
||||
|
@ -453,11 +456,11 @@ namespace TEN::Entities::TR3
|
|||
|
||||
case MPGUN_STATE_CROUCH_SHOT:
|
||||
if (AI.ahead)
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
|
||||
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase)
|
||||
{
|
||||
if (!ShotLara(item, &AI, MPGunBite, torsoY, 32) || !(GetRandomControl() & 0x7))
|
||||
if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32) || TestProbability(0.125f))
|
||||
item->Animation.TargetState = MPGUN_STATE_CROUCHED;
|
||||
}
|
||||
|
||||
|
@ -469,7 +472,7 @@ namespace TEN::Entities::TR3
|
|||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
|
||||
if (Targetable(item, &AI) || item->HitStatus || !cover || (AI.ahead && !(GetRandomControl() & 0x1F)))
|
||||
if (Targetable(item, &AI) || item->HitStatus || !cover || (AI.ahead && TestProbability(1.0f / 30)))
|
||||
item->Animation.TargetState = MPGUN_STATE_CROUCHED;
|
||||
|
||||
break;
|
||||
|
@ -477,8 +480,8 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torsoY);
|
||||
CreatureJoint(item, 1, torsoX);
|
||||
CreatureJoint(item, 0, extraTorsoRot.y);
|
||||
CreatureJoint(item, 1, extraTorsoRot.x);
|
||||
CreatureJoint(item, 2, head);
|
||||
CreatureAnimation(itemNumber, angle, tilt);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void MPGunControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -11,20 +11,21 @@
|
|||
#include "Game/people.h"
|
||||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
#include "Specific/prng.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
const auto MPStickBite1 = BiteInfo(Vector3(247.0f, 10.0f, 11.0f), 13);
|
||||
const auto MPStickBite2 = BiteInfo(Vector3(0.0f, 0.0f, 100.0f), 6);
|
||||
const vector<int> MPStickPunchAttackJoints = { 10, 13 };
|
||||
const vector<int> MPStickKickAttackJoints = { 5, 6 };
|
||||
const vector<int> MPStickKickAttackJoints = { 5, 6 };
|
||||
|
||||
enum MPStickState
|
||||
{
|
||||
MPSTICK_STATE_NONE,
|
||||
MPSTICK_STATE_STOP,
|
||||
MPSTICK_STATE_WALK,
|
||||
MPSTICK_STATE_PUNCH2,
|
||||
|
@ -54,10 +55,7 @@ namespace TEN::Entities::TR3
|
|||
auto* item = &g_Level.Items[itemNumber];
|
||||
|
||||
ClearItem(itemNumber);
|
||||
|
||||
item->Animation.AnimNumber = Objects[ID_MP_WITH_STICK].animIndex + 6;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = item->Animation.TargetState = MPSTICK_STATE_STOP;
|
||||
SetAnimation(item, 6);
|
||||
}
|
||||
|
||||
void MPStickControl(short itemNumber)
|
||||
|
@ -68,11 +66,10 @@ namespace TEN::Entities::TR3
|
|||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
short torsoX = 0;
|
||||
short torsoY = 0;
|
||||
short head = 0;
|
||||
auto extraTorsoRot = Vector3Shrt::Zero;
|
||||
|
||||
if (item->BoxNumber != NO_BOX && (g_Level.Boxes[item->BoxNumber].flags & BLOCKED))
|
||||
{
|
||||
|
@ -85,9 +82,7 @@ namespace TEN::Entities::TR3
|
|||
{
|
||||
if (item->Animation.ActiveState != MPSTICK_STATE_DEATH)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[ID_MP_WITH_STICK].animIndex + 26;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = MPSTICK_STATE_DEATH;
|
||||
SetAnimation(item, 26);
|
||||
creature->LOT.Step = 256;
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +98,7 @@ namespace TEN::Entities::TR3
|
|||
int dz = LaraItem->Pose.Position.z - item->Pose.Position.z;
|
||||
laraAI.distance = pow(dx, 2) + pow(dx, 2);
|
||||
|
||||
int bestDistance = 0x7fffffff;
|
||||
int bestDistance = INT_MAX;
|
||||
for (int slot = 0; slot < ActiveCreatures.size(); slot++)
|
||||
{
|
||||
auto* currentCreature = ActiveCreatures[slot];
|
||||
|
@ -184,7 +179,7 @@ namespace TEN::Entities::TR3
|
|||
if (item->AIBits & GUARD)
|
||||
{
|
||||
head = AIGuard(creature);
|
||||
if (!(GetRandomControl() & 0xFF))
|
||||
if (TestProbability(1.0f / 256))
|
||||
{
|
||||
if (item->Animation.ActiveState == MPSTICK_STATE_STOP)
|
||||
item->Animation.TargetState = MPSTICK_STATE_WAIT;
|
||||
|
@ -194,7 +189,6 @@ namespace TEN::Entities::TR3
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
else if (item->AIBits & PATROL1)
|
||||
item->Animation.TargetState = MPSTICK_STATE_WALK;
|
||||
|
||||
|
@ -240,7 +234,7 @@ namespace TEN::Entities::TR3
|
|||
item->Animation.TargetState = MPSTICK_STATE_RUN;
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (GetRandomControl() < 0x100)
|
||||
if (TestProbability(1.0f / 128))
|
||||
{
|
||||
item->Animation.RequiredState = MPSTICK_STATE_WAIT;
|
||||
item->Animation.TargetState = MPSTICK_STATE_STOP;
|
||||
|
@ -288,8 +282,8 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (AI.bite && AI.distance < pow(SECTOR(0.5f), 2))
|
||||
|
@ -305,8 +299,8 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||
|
@ -322,8 +316,8 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (AI.bite && AI.distance < pow(SECTOR(1.25f), 2))
|
||||
|
@ -338,32 +332,30 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (enemy->IsLara())
|
||||
if (creature->Enemy->IsLara())
|
||||
{
|
||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, MPStickPunchAttackJoints))
|
||||
{
|
||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||
DoDamage(enemy, 80);
|
||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!creature->Flags && enemy)
|
||||
if (!creature->Flags && enemy != nullptr)
|
||||
{
|
||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < SECTOR(0.25f) &&
|
||||
abs(enemy->Pose.Position.y - item->Pose.Position.y) <= SECTOR(0.25f) &&
|
||||
abs(enemy->Pose.Position.z - item->Pose.Position.z) < SECTOR(0.25f))
|
||||
if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.25f))
|
||||
{
|
||||
creature->Flags = 1;
|
||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||
DoDamage(enemy, 5);
|
||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -375,32 +367,30 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (enemy->IsLara())
|
||||
if (creature->Enemy->IsLara())
|
||||
{
|
||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, MPStickPunchAttackJoints))
|
||||
{
|
||||
DoDamage(creature->Enemy, 80);
|
||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||
DoDamage(enemy, 80);
|
||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!creature->Flags && enemy)
|
||||
if (!creature->Flags && creature->Enemy != nullptr)
|
||||
{
|
||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < SECTOR(0.25f) &&
|
||||
abs(enemy->Pose.Position.y - item->Pose.Position.y) <= SECTOR(0.25f) &&
|
||||
abs(enemy->Pose.Position.z - item->Pose.Position.z) < SECTOR(0.25f))
|
||||
if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.25f))
|
||||
{
|
||||
creature->Flags = 1;
|
||||
DoDamage(creature->Enemy, 5);
|
||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||
DoDamage(enemy, 5);
|
||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -415,32 +405,30 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (AI.ahead)
|
||||
{
|
||||
torsoX = AI.xAngle;
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
}
|
||||
|
||||
if (enemy->IsLara())
|
||||
if (creature->Enemy->IsLara())
|
||||
{
|
||||
if (creature->Flags != 2 && item->TestBits(JointBitType::Touch, MPStickPunchAttackJoints))
|
||||
{
|
||||
DoDamage(creature->Enemy, 100);
|
||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||
DoDamage(enemy, 100);
|
||||
creature->Flags = 2;
|
||||
SoundEffect(70, &item->Pose);
|
||||
creature->Flags = 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (creature->Flags != 2 && enemy)
|
||||
if (creature->Flags != 2 && creature->Enemy)
|
||||
{
|
||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < SECTOR(0.25f) &&
|
||||
abs(enemy->Pose.Position.y - item->Pose.Position.y) <= SECTOR(0.25f) &&
|
||||
abs(enemy->Pose.Position.z - item->Pose.Position.z) < SECTOR(0.25f))
|
||||
if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.25f))
|
||||
{
|
||||
creature->Flags = 2;
|
||||
DoDamage(creature->Enemy, 6);
|
||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||
DoDamage(enemy, 6);
|
||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||
creature->Flags = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -451,32 +439,30 @@ namespace TEN::Entities::TR3
|
|||
creature->MaxTurn = ANGLE(6.0f);
|
||||
|
||||
if (AI.ahead)
|
||||
torsoY = AI.angle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
|
||||
if (enemy->IsLara())
|
||||
if (creature->Enemy->IsLara())
|
||||
{
|
||||
if (creature->Flags != 1 && item->TestBits(JointBitType::Touch, MPStickKickAttackJoints) &&
|
||||
item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 8)
|
||||
{
|
||||
DoDamage(creature->Enemy, 150);
|
||||
CreatureEffect(item, MPStickBite2, DoBloodSplat);
|
||||
DoDamage(enemy, 150);
|
||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!creature->Flags != 1 && enemy &&
|
||||
if (!creature->Flags != 1 && creature->Enemy &&
|
||||
item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 8)
|
||||
{
|
||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < SECTOR(0.25f) &&
|
||||
abs(enemy->Pose.Position.y - item->Pose.Position.y) <= SECTOR(0.25f) &&
|
||||
abs(enemy->Pose.Position.z - item->Pose.Position.z) < SECTOR(0.25f))
|
||||
if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.25f))
|
||||
{
|
||||
creature->Flags = 1;
|
||||
DoDamage(creature->Enemy, 9);
|
||||
CreatureEffect(item, MPStickBite2, DoBloodSplat);
|
||||
DoDamage(enemy, 9);
|
||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||
creature->Flags = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -486,8 +472,8 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, torsoY);
|
||||
CreatureJoint(item, 1, torsoX);
|
||||
CreatureJoint(item, 0, extraTorsoRot.y);
|
||||
CreatureJoint(item, 1, extraTorsoRot.x);
|
||||
CreatureJoint(item, 2, head);
|
||||
|
||||
if (item->Animation.ActiveState < MPSTICK_STATE_DEATH)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void InitialiseMPStick(short itemNumber);
|
||||
void MPStickControl(short itemNumber);
|
||||
|
|
|
@ -16,10 +16,17 @@
|
|||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
constexpr auto RAPTOR_ATTACK_DAMAGE = 100;
|
||||
|
||||
constexpr auto RAPTOR_BITE_ATTACK_RANGE = SQUARE(585);
|
||||
constexpr auto RAPTOR_JUMP_ATTACK_RANGE = SQUARE(SECTOR(1.5f));
|
||||
constexpr auto RAPTOR_RUN_ATTACK_RANGE = SQUARE(SECTOR(1.5f));
|
||||
|
||||
constexpr auto RAPTOR_ROAR_CHANCE = 1.0f / 256;
|
||||
constexpr auto RAPTOR_SWITCH_TARGET_CHANCE = 1.0f / 128;
|
||||
|
||||
#define RAPTOR_WALK_TURN_RATE_MAX ANGLE(2.0f)
|
||||
#define RAPTOR_RUN_TURN_RATE_MAX ANGLE(2.0f)
|
||||
#define RAPTOR_ATTACK_TURN_RATE_MAX ANGLE(2.0f)
|
||||
|
@ -74,10 +81,10 @@ namespace TEN::Entities::TR3
|
|||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short head = 0;
|
||||
short neck = 0;
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
short head = 0;
|
||||
short neck = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
|
@ -86,14 +93,13 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
else
|
||||
{
|
||||
if (creature->Enemy == nullptr || !(GetRandomControl() & 0x7F)) // TODO: Probability is 0.004f or 0.996f?
|
||||
if (creature->Enemy == nullptr || TestProbability(RAPTOR_SWITCH_TARGET_CHANCE))
|
||||
{
|
||||
ItemInfo* nearestItem = nullptr;
|
||||
int minDistance = INT_MAX;
|
||||
|
||||
for (int i = 0; i < ActiveCreatures.size(); i++)
|
||||
for (auto* currentCreature : ActiveCreatures)
|
||||
{
|
||||
auto* currentCreature = ActiveCreatures[i];
|
||||
if (currentCreature->ItemNumber == NO_ITEM || currentCreature->ItemNumber == itemNumber)
|
||||
{
|
||||
currentCreature++;
|
||||
|
@ -106,7 +112,7 @@ namespace TEN::Entities::TR3
|
|||
int y = (targetItem->Pose.Position.y - item->Pose.Position.y) / 64;
|
||||
int z = (targetItem->Pose.Position.z - item->Pose.Position.z) / 64;
|
||||
|
||||
int distance = pow(x, 2) + pow(y, 2) + pow(z, 2);
|
||||
int distance = SQUARE(x) + SQUARE(y) + SQUARE(z);
|
||||
if (distance < minDistance && item->HitPoints > 0)
|
||||
{
|
||||
nearestItem = targetItem;
|
||||
|
@ -118,7 +124,7 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (nearestItem != nullptr &&
|
||||
(nearestItem->ObjectNumber != ID_RAPTOR ||
|
||||
(TestProbability(0.03f) && minDistance < pow(SECTOR(2), 2))))
|
||||
(TestProbability(1.0f / 30) && minDistance < SQUARE(SECTOR(2)))))
|
||||
{
|
||||
creature->Enemy = nearestItem;
|
||||
}
|
||||
|
@ -127,7 +133,7 @@ namespace TEN::Entities::TR3
|
|||
int y = (LaraItem->Pose.Position.y - item->Pose.Position.y) / 64;
|
||||
int z = (LaraItem->Pose.Position.z - item->Pose.Position.z) / 64;
|
||||
|
||||
int distance = pow(x, 2) + pow(y, 2) + pow(z, 2);
|
||||
int distance = SQUARE(x) + SQUARE(y) + SQUARE(z);
|
||||
if (distance <= minDistance)
|
||||
creature->Enemy = LaraItem;
|
||||
}
|
||||
|
@ -164,11 +170,11 @@ namespace TEN::Entities::TR3
|
|||
item->Animation.TargetState = RAPTOR_STATE_ROAR;
|
||||
}
|
||||
else if (item->TestBits(JointBitType::Touch, RaptorAttackJoints) ||
|
||||
(AI.distance < pow(585, 2) && AI.bite))
|
||||
(AI.distance < RAPTOR_BITE_ATTACK_RANGE && AI.bite))
|
||||
{
|
||||
item->Animation.TargetState = RAPTOR_STATE_BITE_ATTACK;
|
||||
}
|
||||
else if (AI.bite && AI.distance < pow(SECTOR(1.5f), 2))
|
||||
else if (AI.bite && AI.distance < RAPTOR_JUMP_ATTACK_RANGE)
|
||||
item->Animation.TargetState = RAPTOR_STATE_JUMP_ATTACK;
|
||||
else if (creature->Mood == MoodType::Escape &&
|
||||
Lara.TargetEntity != item && AI.ahead && !item->HitStatus)
|
||||
|
@ -188,7 +194,7 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (creature->Mood != MoodType::Bored)
|
||||
item->Animation.TargetState = RAPTOR_STATE_IDLE;
|
||||
else if (AI.ahead && TestProbability(0.004f))
|
||||
else if (AI.ahead && TestProbability(RAPTOR_ROAR_CHANCE))
|
||||
{
|
||||
item->Animation.TargetState = RAPTOR_STATE_IDLE;
|
||||
item->Animation.RequiredState = RAPTOR_STATE_ROAR;
|
||||
|
@ -210,7 +216,7 @@ namespace TEN::Entities::TR3
|
|||
item->Animation.RequiredState = RAPTOR_STATE_ROAR;
|
||||
creature->Flags &= ~2;
|
||||
}
|
||||
else if (AI.bite && AI.distance < pow(SECTOR(1.5f), 2))
|
||||
else if (AI.bite && AI.distance < RAPTOR_RUN_ATTACK_RANGE)
|
||||
{
|
||||
if (item->Animation.TargetState == RAPTOR_STATE_RUN_FORWARD)
|
||||
{
|
||||
|
@ -221,7 +227,7 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
}
|
||||
else if (AI.ahead && creature->Mood != MoodType::Escape &&
|
||||
TestProbability(0.004f))
|
||||
TestProbability(RAPTOR_ROAR_CHANCE))
|
||||
{
|
||||
item->Animation.TargetState = RAPTOR_STATE_IDLE;
|
||||
item->Animation.RequiredState = RAPTOR_STATE_ROAR;
|
||||
|
@ -257,10 +263,7 @@ namespace TEN::Entities::TR3
|
|||
{
|
||||
if (!(creature->Flags & 1) && creature->Enemy != nullptr)
|
||||
{
|
||||
auto direction = creature->Enemy->Pose.Position - item->Pose.Position;
|
||||
if (abs(direction.x) < SECTOR(0.5f) &&
|
||||
abs(direction.y) < SECTOR(0.5f) &&
|
||||
abs(direction.z) < SECTOR(0.5f))
|
||||
if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.5f))
|
||||
{
|
||||
if (creature->Enemy->HitPoints <= 0)
|
||||
creature->Flags |= 2;
|
||||
|
@ -297,10 +300,7 @@ namespace TEN::Entities::TR3
|
|||
{
|
||||
if (!(creature->Flags & 1) && creature->Enemy != nullptr)
|
||||
{
|
||||
auto direction = creature->Enemy->Pose.Position - item->Pose.Position;
|
||||
if (abs(direction.x) < SECTOR(0.5f) &&
|
||||
abs(direction.y) < SECTOR(0.5f) &&
|
||||
abs(direction.z) < SECTOR(0.5f))
|
||||
if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.5f))
|
||||
{
|
||||
if (creature->Enemy->HitPoints <= 0)
|
||||
creature->Flags |= 2;
|
||||
|
@ -336,10 +336,7 @@ namespace TEN::Entities::TR3
|
|||
{
|
||||
if (!(creature->Flags & 1) && creature->Enemy != nullptr)
|
||||
{
|
||||
auto direction = creature->Enemy->Pose.Position - item->Pose.Position;
|
||||
if (abs(direction.x) < SECTOR(0.5f) &&
|
||||
abs(direction.y) < SECTOR(0.5f) &&
|
||||
abs(direction.z) < SECTOR(0.5f))
|
||||
if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.5f))
|
||||
{
|
||||
if (creature->Enemy->HitPoints <= 0)
|
||||
creature->Flags |= 2;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void RaptorControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include "Specific/level.h"
|
||||
#include "Specific/setup.h"
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
constexpr auto SCUBA_DIVER_ATTACK_DAMAGE = 50;
|
||||
|
||||
|
@ -23,7 +23,6 @@ namespace TEN::Entities::TR3
|
|||
|
||||
enum ScubaDiverState
|
||||
{
|
||||
SDIVER_STATE_NONE = 0,
|
||||
SDIVER_STATE_SWIM = 1,
|
||||
SDIVER_STATE_TREAD_WATER_IDLE = 2,
|
||||
SDIVER_STATE_SWIM_SHOOT = 3,
|
||||
|
@ -60,23 +59,23 @@ namespace TEN::Entities::TR3
|
|||
static void ShootHarpoon(ItemInfo* item, Vector3Int pos, short velocity, short yRot, short roomNumber)
|
||||
{
|
||||
short harpoonItemNumber = CreateItem();
|
||||
if (harpoonItemNumber != NO_ITEM)
|
||||
{
|
||||
auto* harpoonItem = &g_Level.Items[harpoonItemNumber];
|
||||
if (harpoonItemNumber == NO_ITEM)
|
||||
return;
|
||||
|
||||
harpoonItem->ObjectNumber = ID_SCUBA_HARPOON;
|
||||
harpoonItem->RoomNumber = item->RoomNumber;
|
||||
harpoonItem->Pose.Position = pos;
|
||||
auto* harpoonItem = &g_Level.Items[harpoonItemNumber];
|
||||
|
||||
InitialiseItem(harpoonItemNumber);
|
||||
harpoonItem->ObjectNumber = ID_SCUBA_HARPOON;
|
||||
harpoonItem->RoomNumber = item->RoomNumber;
|
||||
harpoonItem->Pose.Position = pos;
|
||||
|
||||
harpoonItem->Animation.Velocity.z = 150;
|
||||
harpoonItem->Pose.Orientation.x = 0;
|
||||
harpoonItem->Pose.Orientation.y = yRot;
|
||||
InitialiseItem(harpoonItemNumber);
|
||||
|
||||
AddActiveItem(harpoonItemNumber);
|
||||
harpoonItem->Status = ITEM_ACTIVE;
|
||||
}
|
||||
harpoonItem->Animation.Velocity.z = 150.0f;
|
||||
harpoonItem->Pose.Orientation.x = 0;
|
||||
harpoonItem->Pose.Orientation.y = yRot;
|
||||
|
||||
AddActiveItem(harpoonItemNumber);
|
||||
harpoonItem->Status = ITEM_ACTIVE;
|
||||
}
|
||||
|
||||
void ScubaHarpoonControl(short itemNumber)
|
||||
|
@ -91,14 +90,7 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
else
|
||||
{
|
||||
int ox = item->Pose.Position.x;
|
||||
int oz = item->Pose.Position.z;
|
||||
|
||||
int velocity = item->Animation.Velocity.z * phd_cos(item->Pose.Orientation.x);
|
||||
|
||||
item->Pose.Position.z += velocity * phd_cos(item->Pose.Orientation.y);
|
||||
item->Pose.Position.x += velocity * phd_sin(item->Pose.Orientation.y);
|
||||
item->Pose.Position.y += -item->Animation.Velocity.z * phd_sin(item->Pose.Orientation.x);
|
||||
TranslateItem(item, item->Pose.Orientation, item->Animation.Velocity.z);
|
||||
|
||||
auto probe = GetCollision(item);
|
||||
|
||||
|
@ -123,7 +115,8 @@ namespace TEN::Entities::TR3
|
|||
short head = 0;
|
||||
short neck = 0;
|
||||
|
||||
int waterHeight;
|
||||
int waterHeight = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != SDIVER_STATE_DEATH)
|
||||
|
@ -140,22 +133,23 @@ namespace TEN::Entities::TR3
|
|||
GetCreatureMood(item, &AI, false);
|
||||
CreatureMood(item, &AI, false);
|
||||
|
||||
GameVector origin;
|
||||
GameVector target;
|
||||
bool shoot = false;
|
||||
|
||||
if (Lara.Control.WaterStatus == WaterStatus::Dry)
|
||||
{
|
||||
origin.x = item->Pose.Position.x;
|
||||
origin.y = item->Pose.Position.y - CLICK(1);
|
||||
origin.z = item->Pose.Position.z;
|
||||
origin.roomNumber = item->RoomNumber;
|
||||
|
||||
target.x = LaraItem->Pose.Position.x;
|
||||
target.y = LaraItem->Pose.Position.y - (LARA_HEIGHT - 150);
|
||||
target.z = LaraItem->Pose.Position.z;
|
||||
auto origin = GameVector(
|
||||
item->Pose.Position.x,
|
||||
item->Pose.Position.y - CLICK(1),
|
||||
item->Pose.Position.z,
|
||||
item->RoomNumber
|
||||
);
|
||||
auto target = GameVector(
|
||||
LaraItem->Pose.Position.x,
|
||||
LaraItem->Pose.Position.y - (LARA_HEIGHT - 150),
|
||||
LaraItem->Pose.Position.z
|
||||
);
|
||||
|
||||
shoot = LOS(&origin, &target);
|
||||
|
||||
if (shoot)
|
||||
creature->Target = LaraItem->Pose.Position;
|
||||
|
||||
|
@ -164,19 +158,11 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
else if (AI.angle > -ANGLE(45.0f) && AI.angle < ANGLE(45.0f))
|
||||
{
|
||||
origin.x = item->Pose.Position.x;
|
||||
origin.y = item->Pose.Position.y;
|
||||
origin.z = item->Pose.Position.z;
|
||||
origin.roomNumber = item->RoomNumber;
|
||||
|
||||
target.x = LaraItem->Pose.Position.x;
|
||||
target.y = LaraItem->Pose.Position.y;
|
||||
target.z = LaraItem->Pose.Position.z;
|
||||
|
||||
auto origin = GameVector(item->Pose.Position, item->RoomNumber);
|
||||
auto target = GameVector(LaraItem->Pose.Position);
|
||||
|
||||
shoot = LOS(&origin, &target);
|
||||
}
|
||||
else
|
||||
shoot = false;
|
||||
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
waterHeight = GetWaterSurface(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber) + SECTOR(0.5f);
|
||||
|
@ -202,7 +188,7 @@ namespace TEN::Entities::TR3
|
|||
break;
|
||||
|
||||
case SDIVER_STATE_SWIM_AIM:
|
||||
creature->Flags = NULL;
|
||||
creature->Flags = 0;
|
||||
|
||||
if (shoot)
|
||||
neck = -AI.angle;
|
||||
|
@ -270,7 +256,6 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void ScubaHarpoonControl(short itemNumber);
|
||||
void ScubaControl(short itemNumber);
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
constexpr auto SHIVA_GRAB_ATTACK_DAMAGE = 150;
|
||||
constexpr auto SHIVA_DOWNWARD_ATTACK_DAMAGE = 180;
|
||||
|
@ -29,10 +29,10 @@ namespace TEN::Entities::TR3
|
|||
#define SHIVA_WALK_TURN_RATE_MAX ANGLE(4.0f)
|
||||
#define SHIVA_ATTACK_TURN_RATE_MAX ANGLE(4.0f)
|
||||
|
||||
const vector<int> ShivaAttackLeftJoints = { 10, 13 };
|
||||
const vector<int> ShivaAttackRightJoints = { 22, 25 };
|
||||
const auto ShivaBiteLeft = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 13);
|
||||
const auto ShivaBiteRight = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 22);
|
||||
const vector<int> ShivaAttackLeftJoints = { 10, 13 };
|
||||
const vector<int> ShivaAttackRightJoints = { 22, 25 };
|
||||
|
||||
enum ShivaState
|
||||
{
|
||||
|
@ -81,7 +81,7 @@ namespace TEN::Entities::TR3
|
|||
|
||||
};
|
||||
|
||||
static void TriggerShivaSmoke(long x, long y, long z, long uw)
|
||||
void TriggerShivaSmoke(long x, long y, long z, long uw)
|
||||
{
|
||||
long dx = LaraItem->Pose.Position.x - x;
|
||||
long dz = LaraItem->Pose.Position.z - z;
|
||||
|
@ -169,7 +169,7 @@ namespace TEN::Entities::TR3
|
|||
sptr->dSize = size;
|
||||
}
|
||||
|
||||
static void ShivaDamage(ItemInfo* item, CreatureInfo* creature, int damage)
|
||||
void ShivaDamage(ItemInfo* item, CreatureInfo* creature, int damage)
|
||||
{
|
||||
if (!(creature->Flags) && item->TestBits(JointBitType::Touch, ShivaAttackRightJoints))
|
||||
{
|
||||
|
@ -208,15 +208,15 @@ namespace TEN::Entities::TR3
|
|||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* shiva = GetCreatureInfo(item);
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
auto pos = Vector3Int(0, 0, 256);
|
||||
bool laraAlive = LaraItem->HitPoints > 0;
|
||||
auto pos = Vector3Int(0, 0, CLICK(1));
|
||||
bool isLaraAlive = LaraItem->HitPoints > 0;
|
||||
|
||||
Vector3Shrt extraHeadRot;
|
||||
Vector3Shrt extraTorsoRot;
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
Vector3Shrt extraHeadRot = Vector3Shrt::Zero;
|
||||
Vector3Shrt extraTorsoRot = Vector3Shrt::Zero;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
|
@ -235,13 +235,13 @@ namespace TEN::Entities::TR3
|
|||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
if (shiva->Mood == MoodType::Escape)
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
{
|
||||
shiva->Target.x = LaraItem->Pose.Position.x;
|
||||
shiva->Target.z = LaraItem->Pose.Position.z;
|
||||
creature->Target.x = LaraItem->Pose.Position.x;
|
||||
creature->Target.z = LaraItem->Pose.Position.z;
|
||||
}
|
||||
|
||||
angle = CreatureTurn(item, shiva->MaxTurn);
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
if (item->Animation.ActiveState != SHIVA_STATE_INACTIVE)
|
||||
item->MeshBits = ALL_JOINT_BITS;
|
||||
|
@ -251,15 +251,15 @@ namespace TEN::Entities::TR3
|
|||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case SHIVA_STATE_INACTIVE:
|
||||
shiva->MaxTurn = 0;
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (!shiva->Flags)
|
||||
if (!creature->Flags)
|
||||
{
|
||||
if (!item->MeshBits)
|
||||
effectMesh = 0;
|
||||
|
||||
item->MeshBits = (item->MeshBits * 2) + 1;
|
||||
shiva->Flags = 1;
|
||||
creature->Flags = 1;
|
||||
|
||||
GetJointAbsPosition(item, &pos, effectMesh++);
|
||||
TriggerExplosionSparks(pos.x, pos.y, pos.z, 2, 0, 0, item->RoomNumber);
|
||||
|
@ -267,45 +267,45 @@ namespace TEN::Entities::TR3
|
|||
|
||||
}
|
||||
else
|
||||
shiva->Flags--;
|
||||
creature->Flags--;
|
||||
|
||||
if (item->MeshBits == 0x7FFFFFFF)
|
||||
{
|
||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
||||
shiva->Flags = -45;
|
||||
creature->Flags = -45;
|
||||
effectMesh = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SHIVA_STATE_IDLE:
|
||||
shiva->MaxTurn = 0;
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
if (shiva->Flags < 0)
|
||||
if (creature->Flags < 0)
|
||||
{
|
||||
shiva->Flags++;
|
||||
creature->Flags++;
|
||||
TriggerShivaSmoke(item->Pose.Position.x + (GetRandomControl() & 0x5FF) - 0x300, pos.y - (GetRandomControl() & 0x5FF), item->Pose.Position.z + (GetRandomControl() & 0x5FF) - 0x300, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (shiva->Flags == 1)
|
||||
shiva->Flags = 0;
|
||||
if (creature->Flags == 1)
|
||||
creature->Flags = 0;
|
||||
|
||||
if (shiva->Mood == MoodType::Escape)
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
{
|
||||
int x = item->Pose.Position.x + SECTOR(1) * phd_sin(item->Pose.Orientation.y + ANGLE(180.0f));
|
||||
int z = item->Pose.Position.z + SECTOR(1) * phd_cos(item->Pose.Orientation.y + ANGLE(180.0f));
|
||||
auto box = GetCollision(x, item->Pose.Position.y, z, item->RoomNumber).BottomBlock->Box;
|
||||
|
||||
if (box != NO_BOX && !(g_Level.Boxes[box].flags & BLOCKABLE) && !shiva->Flags)
|
||||
if (box != NO_BOX && !(g_Level.Boxes[box].flags & BLOCKABLE) && !creature->Flags)
|
||||
item->Animation.TargetState = SHIVA_STATE_WALK_BACK;
|
||||
else
|
||||
item->Animation.TargetState = SHIVA_STATE_GUARD_IDLE;
|
||||
}
|
||||
else if (shiva->Mood == MoodType::Bored)
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (TestProbability(0.0325f))
|
||||
item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD;
|
||||
|
@ -313,17 +313,17 @@ namespace TEN::Entities::TR3
|
|||
else if (AI.bite && AI.distance < pow(SECTOR(1.25f), 2))
|
||||
{
|
||||
item->Animation.TargetState = SHIVA_STATE_GRAB_ATTACK;
|
||||
shiva->Flags = 0;
|
||||
creature->Flags = 0;
|
||||
}
|
||||
else if (AI.bite && AI.distance < pow(SECTOR(4) / 3, 2))
|
||||
{
|
||||
item->Animation.TargetState = SHIVA_STATE_DOWNWARD_ATTACK;
|
||||
shiva->Flags = 0;
|
||||
creature->Flags = 0;
|
||||
}
|
||||
else if (item->HitStatus && AI.ahead)
|
||||
{
|
||||
item->Animation.TargetState = SHIVA_STATE_GUARD_IDLE;
|
||||
shiva->Flags = 4;
|
||||
creature->Flags = 4;
|
||||
}
|
||||
else
|
||||
item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD;
|
||||
|
@ -331,103 +331,103 @@ namespace TEN::Entities::TR3
|
|||
break;
|
||||
|
||||
case SHIVA_STATE_GUARD_IDLE:
|
||||
shiva->MaxTurn = 0;
|
||||
creature->MaxTurn = 0;
|
||||
|
||||
if (AI.ahead)
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
if (item->HitStatus || shiva->Mood == MoodType::Escape)
|
||||
shiva->Flags = 4;
|
||||
if (item->HitStatus || creature->Mood == MoodType::Escape)
|
||||
creature->Flags = 4;
|
||||
|
||||
if (AI.bite && AI.distance < pow(SECTOR(4) / 3, 2) ||
|
||||
(item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase &&
|
||||
!shiva->Flags) ||
|
||||
!creature->Flags) ||
|
||||
!AI.ahead)
|
||||
{
|
||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
||||
shiva->Flags = 0;
|
||||
creature->Flags = 0;
|
||||
}
|
||||
else if (shiva->Flags)
|
||||
else if (creature->Flags)
|
||||
item->Animation.TargetState = SHIVA_STATE_GUARD_IDLE;
|
||||
|
||||
|
||||
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase &&
|
||||
shiva->Flags > 1)
|
||||
creature->Flags > 1)
|
||||
{
|
||||
shiva->Flags -= 2;
|
||||
creature->Flags -= 2;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SHIVA_STATE_WALK_FORWARD:
|
||||
shiva->MaxTurn = SHIVA_WALK_TURN_RATE_MAX;
|
||||
creature->MaxTurn = SHIVA_WALK_TURN_RATE_MAX;
|
||||
|
||||
if (AI.ahead)
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
if (shiva->Mood == MoodType::Escape)
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
||||
else if (shiva->Mood == MoodType::Bored)
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
||||
else if (AI.bite && AI.distance < pow(SECTOR(4) / 3, 2))
|
||||
{
|
||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
||||
shiva->Flags = 0;
|
||||
creature->Flags = 0;
|
||||
}
|
||||
else if (item->HitStatus)
|
||||
{
|
||||
item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD_GUARDING;
|
||||
shiva->Flags = 4;
|
||||
creature->Flags = 4;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SHIVA_STATE_WALK_FORWARD_GUARDING:
|
||||
shiva->MaxTurn = SHIVA_WALK_TURN_RATE_MAX;
|
||||
creature->MaxTurn = SHIVA_WALK_TURN_RATE_MAX;
|
||||
|
||||
if (AI.ahead)
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
if (item->HitStatus)
|
||||
shiva->Flags = 4;
|
||||
creature->Flags = 4;
|
||||
|
||||
if (AI.bite && AI.distance < pow(SECTOR(1.25f), 2) ||
|
||||
(item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase &&
|
||||
!shiva->Flags))
|
||||
!creature->Flags))
|
||||
{
|
||||
item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD;
|
||||
shiva->Flags = 0;
|
||||
creature->Flags = 0;
|
||||
}
|
||||
else if (shiva->Flags)
|
||||
else if (creature->Flags)
|
||||
item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD_GUARDING;
|
||||
|
||||
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase)
|
||||
shiva->Flags = 0;
|
||||
creature->Flags = 0;
|
||||
|
||||
break;
|
||||
|
||||
case SHIVA_STATE_WALK_BACK:
|
||||
shiva->MaxTurn = SHIVA_WALK_TURN_RATE_MAX;
|
||||
creature->MaxTurn = SHIVA_WALK_TURN_RATE_MAX;
|
||||
|
||||
if (AI.ahead)
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
if (AI.ahead && AI.distance < pow(SECTOR(4) / 3, 2) ||
|
||||
(item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase &&
|
||||
!shiva->Flags))
|
||||
!creature->Flags))
|
||||
{
|
||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
||||
}
|
||||
else if (item->HitStatus)
|
||||
{
|
||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
||||
shiva->Flags = 4;
|
||||
creature->Flags = 4;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SHIVA_STATE_GRAB_ATTACK:
|
||||
shiva->MaxTurn = SHIVA_ATTACK_TURN_RATE_MAX;
|
||||
creature->MaxTurn = SHIVA_ATTACK_TURN_RATE_MAX;
|
||||
|
||||
if (AI.ahead)
|
||||
{
|
||||
|
@ -435,24 +435,24 @@ namespace TEN::Entities::TR3
|
|||
extraTorsoRot = Vector3Shrt(AI.xAngle, AI.angle, 0);
|
||||
}
|
||||
|
||||
ShivaDamage(item, shiva, SHIVA_GRAB_ATTACK_DAMAGE);
|
||||
ShivaDamage(item, creature, SHIVA_GRAB_ATTACK_DAMAGE);
|
||||
break;
|
||||
|
||||
case SHIVA_STATE_DOWNWARD_ATTACK:
|
||||
shiva->MaxTurn = SHIVA_ATTACK_TURN_RATE_MAX;
|
||||
creature->MaxTurn = SHIVA_ATTACK_TURN_RATE_MAX;
|
||||
extraHeadRot.y = AI.angle;
|
||||
extraTorsoRot.y = AI.angle;
|
||||
|
||||
if (AI.xAngle > 0)
|
||||
extraTorsoRot.x = AI.xAngle;
|
||||
|
||||
ShivaDamage(item, shiva, SHIVA_DOWNWARD_ATTACK_DAMAGE);
|
||||
ShivaDamage(item, creature, SHIVA_DOWNWARD_ATTACK_DAMAGE);
|
||||
break;
|
||||
|
||||
case SHIVA_STATE_KILL:
|
||||
shiva->MaxTurn = 0;
|
||||
extraHeadRot = Vector3Shrt();
|
||||
extraTorsoRot = Vector3Shrt();
|
||||
creature->MaxTurn = 0;
|
||||
extraHeadRot = Vector3Shrt::Zero;
|
||||
extraTorsoRot = Vector3Shrt::Zero;
|
||||
|
||||
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + SHIVA_ANIM_WALK_FORWARD_TO_GUARDED_LEFT_1 ||
|
||||
item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + SHIVA_ANIM_WALK_BACK_RIGHT ||
|
||||
|
@ -466,23 +466,21 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
}
|
||||
|
||||
// Dispatch kill animation
|
||||
if (laraAlive && LaraItem->HitPoints <= 0)
|
||||
// Dispatch kill animation.
|
||||
if (isLaraAlive && LaraItem->HitPoints <= 0)
|
||||
{
|
||||
item->Animation.TargetState = SHIVA_STATE_KILL;
|
||||
|
||||
if (LaraItem->RoomNumber != item->RoomNumber)
|
||||
ItemNewRoom(Lara.ItemNumber, item->RoomNumber);
|
||||
|
||||
LaraItem->Pose.Position = item->Pose.Position;
|
||||
LaraItem->Pose.Orientation = Vector3Shrt(0, item->Pose.Orientation.y, 0);
|
||||
LaraItem->Animation.IsAirborne = false;
|
||||
|
||||
LaraItem->Animation.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + LARA_ANIM_SHIVA_DEATH;
|
||||
LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase;
|
||||
LaraItem->Animation.ActiveState = LS_DEATH;
|
||||
LaraItem->Animation.TargetState = LS_DEATH;
|
||||
|
||||
LaraItem->Animation.IsAirborne = false;
|
||||
LaraItem->Pose.Position = item->Pose.Position;
|
||||
LaraItem->Pose.Orientation = Vector3Shrt(0, item->Pose.Orientation.y, 0);
|
||||
LaraItem->HitPoints = NOT_TARGETABLE;
|
||||
Lara.Air = -1;
|
||||
Lara.Control.HandStatus = HandStatus::Special;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void InitialiseShiva(short itemNumber);
|
||||
void ShivaControl(short itemNumber);
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "Sound/sound.h"
|
||||
#include "Specific/level.h"
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
static BOSS_STRUCT BossData;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
#include "Game/items.h"
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void ControlLaserBolts(short itemNumber);
|
||||
void ControlLondBossPlasmaBall(short fxNumber);
|
||||
|
|
|
@ -15,23 +15,52 @@
|
|||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
constexpr auto TIGER_ATTACK_DAMAGE = 90;
|
||||
|
||||
constexpr auto TIGER_BITE_ATTACK_RANGE = SQUARE(SECTOR(0.33f));
|
||||
constexpr auto TIGER_POUNCE_ATTACK_RANGE = SQUARE(SECTOR(1));
|
||||
constexpr auto TIGER_RUN_ATTACK_RANGE = SQUARE(SECTOR(1.5f));
|
||||
|
||||
constexpr auto TIGER_WALK_CHANCE = 0.035f;
|
||||
constexpr auto TIGER_ROAR_CHANCE = 1.0f / 340;
|
||||
|
||||
const auto TigerBite = BiteInfo(Vector3(19.0f, -13.0f, 3.0f), 26);
|
||||
const vector<int> TigerAttackJoints = { 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26 };
|
||||
|
||||
// TODO
|
||||
#define TIGER_WALK_TURN_RATE_MAX ANGLE(3.0f)
|
||||
#define TIGER_RUN_TURN_RATE_MAX ANGLE(6.0f)
|
||||
#define TIGER_POUNCE_ATTACK_TURN_RATE_MAX ANGLE(3.0f)
|
||||
|
||||
enum TigerState
|
||||
{
|
||||
|
||||
TIGER_STATE_DEATH = 0,
|
||||
TIGER_STATE_IDLE = 1,
|
||||
TIGER_STATE_WALK_FORWARD = 2,
|
||||
TIGER_STATE_RUN_FORWARD = 3,
|
||||
// No state 4.
|
||||
TIGER_STATE_ROAR = 5,
|
||||
TIGER_STATE_BITE_ATTACK = 6,
|
||||
TIGER_STATE_RUN_SWIPE_ATTACK = 7,
|
||||
TIGER_STATE_POUNCE_ATTACK = 8
|
||||
};
|
||||
|
||||
// TODO
|
||||
enum TigerAnim
|
||||
{
|
||||
|
||||
TIGER_ANIM_IDLE_TO_RUN_FORWARD = 0,
|
||||
TIGER_ANIM_BITE_ATTACK = 1,
|
||||
TIGER_ANIM_RUN_SWIPE_ATTACK = 2,
|
||||
TIGER_ANIM_POUNCE_ATTACK_START = 3,
|
||||
TIGER_ANIM_ROAR = 4,
|
||||
TIGER_ANIM_RUN_FORWARD = 5,
|
||||
TIGER_ANIM_RUN_FORWARD_TO_IDLE = 6,
|
||||
TIGER_ANIM_IDLE = 7,
|
||||
TIGER_ANIM_WALK_FORWARD = 8,
|
||||
TIGER_ANIM_IDLE_TO_WALK_FORWARD = 9,
|
||||
TIGER_ANIM_WALK_FORWARD_TO_IDLE = 10,
|
||||
TIGER_ANIM_DEATH = 11,
|
||||
TIGER_ANIM_POUNCE_ATTACK_END = 12
|
||||
};
|
||||
|
||||
void TigerControl(short itemNumber)
|
||||
|
@ -40,20 +69,16 @@ namespace TEN::Entities::TR3
|
|||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* info = GetCreatureInfo(item);
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
short tilt = 0;
|
||||
auto extraHeadRot = Vector3Shrt::Zero;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
if (item->Animation.ActiveState != 9)
|
||||
{
|
||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 11;
|
||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||
item->Animation.ActiveState = 9;
|
||||
}
|
||||
if (item->Animation.ActiveState != TIGER_STATE_DEATH)
|
||||
SetAnimation(item, TIGER_ANIM_DEATH);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -61,99 +86,105 @@ namespace TEN::Entities::TR3
|
|||
CreatureAIInfo(item, &AI);
|
||||
|
||||
if (AI.ahead)
|
||||
head = AI.angle;
|
||||
extraHeadRot.y = AI.angle;
|
||||
|
||||
GetCreatureMood(item, &AI, 1);
|
||||
GetCreatureMood(item, &AI, true);
|
||||
|
||||
if (info->Alerted && AI.zoneNumber != AI.enemyZone)
|
||||
info->Mood = MoodType::Escape;
|
||||
if (creature->Alerted && AI.zoneNumber != AI.enemyZone)
|
||||
creature->Mood = MoodType::Escape;
|
||||
|
||||
CreatureMood(item, &AI, 1);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
angle = CreatureTurn(item, info->MaxTurn);
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
{
|
||||
case 1:
|
||||
info->MaxTurn = 0;
|
||||
info->Flags = 0;
|
||||
case TIGER_STATE_IDLE:
|
||||
creature->MaxTurn = 0;
|
||||
creature->Flags = 0;
|
||||
|
||||
if (info->Mood == MoodType::Escape)
|
||||
if (creature->Mood == MoodType::Escape)
|
||||
{
|
||||
if (Lara.TargetEntity != item && AI.ahead)
|
||||
item->Animation.TargetState = 1;
|
||||
item->Animation.TargetState = TIGER_STATE_IDLE;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = TIGER_STATE_RUN_FORWARD;
|
||||
}
|
||||
else if (info->Mood == MoodType::Bored)
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
{
|
||||
if (TestProbability(0.003f))
|
||||
item->Animation.TargetState = 5;
|
||||
else if (TestProbability(0.035f))
|
||||
item->Animation.TargetState = 2;
|
||||
if (TestProbability(TIGER_ROAR_CHANCE))
|
||||
item->Animation.TargetState = TIGER_STATE_ROAR;
|
||||
else if (TestProbability(TIGER_WALK_CHANCE))
|
||||
item->Animation.TargetState = TIGER_STATE_WALK_FORWARD;
|
||||
}
|
||||
else if (AI.bite && AI.distance < pow(340, 2))
|
||||
item->Animation.TargetState = 6;
|
||||
else if (AI.bite && AI.distance < pow(SECTOR(1), 2))
|
||||
else if (AI.bite && AI.distance < TIGER_BITE_ATTACK_RANGE)
|
||||
item->Animation.TargetState = TIGER_STATE_BITE_ATTACK;
|
||||
else if (AI.bite && AI.distance < TIGER_POUNCE_ATTACK_RANGE)
|
||||
{
|
||||
info->MaxTurn = ANGLE(3.0f);
|
||||
item->Animation.TargetState = 8;
|
||||
item->Animation.TargetState = TIGER_STATE_POUNCE_ATTACK;
|
||||
creature->MaxTurn = TIGER_POUNCE_ATTACK_TURN_RATE_MAX;
|
||||
}
|
||||
else if (item->Animation.RequiredState)
|
||||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
else if (info->Mood != MoodType::Attack && TestProbability(0.003f))
|
||||
item->Animation.TargetState = 5;
|
||||
else if (creature->Mood != MoodType::Attack && TestProbability(TIGER_ROAR_CHANCE))
|
||||
item->Animation.TargetState = TIGER_STATE_ROAR;
|
||||
else
|
||||
item->Animation.TargetState = 3;
|
||||
item->Animation.TargetState = TIGER_STATE_RUN_FORWARD;
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
info->MaxTurn = ANGLE(3.0f);
|
||||
case TIGER_STATE_WALK_FORWARD:
|
||||
creature->MaxTurn = TIGER_WALK_TURN_RATE_MAX;
|
||||
|
||||
if (info->Mood == MoodType::Escape || info->Mood == MoodType::Attack)
|
||||
item->Animation.TargetState = 3;
|
||||
else if (TestProbability(0.003f))
|
||||
if (creature->Mood == MoodType::Escape ||
|
||||
creature->Mood == MoodType::Attack)
|
||||
{
|
||||
item->Animation.TargetState = 1;
|
||||
item->Animation.RequiredState = 5;
|
||||
item->Animation.TargetState = TIGER_STATE_RUN_FORWARD;
|
||||
}
|
||||
else if (TestProbability(TIGER_ROAR_CHANCE))
|
||||
{
|
||||
item->Animation.TargetState = TIGER_STATE_IDLE;
|
||||
item->Animation.RequiredState = TIGER_STATE_ROAR;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
info->MaxTurn = ANGLE(6.0f);
|
||||
case TIGER_STATE_RUN_FORWARD:
|
||||
creature->MaxTurn = TIGER_RUN_TURN_RATE_MAX;
|
||||
|
||||
if (info->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = 1;
|
||||
else if (info->Flags && AI.ahead)
|
||||
item->Animation.TargetState = 1;
|
||||
else if (AI.bite && AI.distance < pow(SECTOR(1.5f), 2))
|
||||
if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = TIGER_STATE_IDLE;
|
||||
else if (creature->Flags && AI.ahead)
|
||||
item->Animation.TargetState = TIGER_STATE_IDLE;
|
||||
else if (AI.bite && AI.distance < TIGER_RUN_ATTACK_RANGE)
|
||||
{
|
||||
if (LaraItem->Animation.Velocity.z == 0)
|
||||
item->Animation.TargetState = 1;
|
||||
if (LaraItem->Animation.Velocity.z == 0.0f)
|
||||
item->Animation.TargetState = TIGER_STATE_IDLE;
|
||||
else
|
||||
item->Animation.TargetState = 7;
|
||||
item->Animation.TargetState = TIGER_STATE_RUN_SWIPE_ATTACK;
|
||||
}
|
||||
else if (info->Mood != MoodType::Attack && TestProbability(0.003f))
|
||||
else if (creature->Mood != MoodType::Attack && TestProbability(TIGER_ROAR_CHANCE))
|
||||
{
|
||||
item->Animation.TargetState = 1;
|
||||
item->Animation.RequiredState = 5;
|
||||
item->Animation.TargetState = TIGER_STATE_IDLE;
|
||||
item->Animation.RequiredState = TIGER_STATE_ROAR;
|
||||
}
|
||||
else if (creature->Mood == MoodType::Escape &&
|
||||
Lara.TargetEntity != item && AI.ahead)
|
||||
{
|
||||
item->Animation.TargetState = TIGER_STATE_IDLE;
|
||||
}
|
||||
else if (info->Mood == MoodType::Escape && Lara.TargetEntity != item && AI.ahead)
|
||||
item->Animation.TargetState = 1;
|
||||
|
||||
info->Flags = 0;
|
||||
creature->Flags = 0;
|
||||
break;
|
||||
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
if (!info->Flags && item->TestBits(JointBitType::Touch, TigerAttackJoints))
|
||||
case TIGER_STATE_BITE_ATTACK:
|
||||
case TIGER_STATE_RUN_SWIPE_ATTACK:
|
||||
case TIGER_STATE_POUNCE_ATTACK:
|
||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, TigerAttackJoints))
|
||||
{
|
||||
DoDamage(info->Enemy, TIGER_ATTACK_DAMAGE);
|
||||
DoDamage(creature->Enemy, TIGER_ATTACK_DAMAGE);
|
||||
CreatureEffect(item, TigerBite, DoBloodSplat);
|
||||
info->Flags = 1;
|
||||
creature->Flags = 1; // 1 = is attacking.
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -161,7 +192,7 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
|
||||
CreatureTilt(item, tilt);
|
||||
CreatureJoint(item, 0, head);
|
||||
CreatureJoint(item, 0, extraHeadRot.y);
|
||||
CreatureAnimation(itemNumber, angle, tilt);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void TigerControl(short itemNumber);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
using namespace TEN::Effects::Lara;
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
static BOSS_STRUCT BossData;
|
||||
|
||||
|
@ -467,7 +467,7 @@ namespace TEN::Entities::TR3
|
|||
|
||||
if (!Lara.Burn)
|
||||
{
|
||||
if (ItemNearLara(&fx->pos, 200))
|
||||
if (ItemNearLara(&fx->pos.Position, 200))
|
||||
{
|
||||
LaraItem->HitStatus = true;
|
||||
KillEffect(fxNumber);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
#include "Game/items.h"
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void InitialiseTony(short itemNumber);
|
||||
void TonyControl(short itemNumber);
|
||||
|
|
|
@ -15,15 +15,20 @@
|
|||
using namespace TEN::Math::Random;
|
||||
using std::vector;
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
constexpr auto TREX_CONTACT_DAMAGE = 1;
|
||||
constexpr auto TREX_RUN_CONTACT_DAMAGE = 10;
|
||||
constexpr auto TREX_ROAR_CHANCE = 0.015f;
|
||||
constexpr auto LARA_ANIM_TREX_DEATH_ANIM = 4;
|
||||
|
||||
const vector<int> TRexAttackJoints = { 12, 13 };
|
||||
|
||||
#define TREX_WALK_TURN_RATE_MAX ANGLE(2.0f)
|
||||
#define TREX_RUN_TURN_RATE_MAX ANGLE(4.0f)
|
||||
|
||||
enum TRexState
|
||||
{
|
||||
TREX_STATE_NONE = 0,
|
||||
TREX_STATE_IDLE = 1,
|
||||
TREX_STATE_WALK_FORWARD = 2,
|
||||
TREX_STATE_RUN_FORWARD = 3,
|
||||
|
@ -57,8 +62,9 @@ namespace TEN::Entities::TR3
|
|||
if (laraItem->RoomNumber != tRexItem->RoomNumber)
|
||||
ItemNewRoom(Lara.ItemNumber, tRexItem->RoomNumber);
|
||||
|
||||
laraItem->Pose.Position = tRexItem->Pose.Position;
|
||||
laraItem->Pose.Orientation = Vector3Shrt(0, tRexItem->Pose.Orientation.y, 0);
|
||||
laraItem->Pose = PHD_3DPOS(
|
||||
tRexItem->Pose.Position,
|
||||
Vector3Shrt(0, tRexItem->Pose.Orientation.y, 0));
|
||||
laraItem->Animation.IsAirborne = false;
|
||||
|
||||
laraItem->Animation.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + LARA_ANIM_TREX_DEATH_ANIM;
|
||||
|
@ -82,10 +88,10 @@ namespace TEN::Entities::TR3
|
|||
return;
|
||||
|
||||
auto* item = &g_Level.Items[itemNumber];
|
||||
auto* info = GetCreatureInfo(item);
|
||||
auto* creature = GetCreatureInfo(item);
|
||||
|
||||
short head = 0;
|
||||
short angle = 0;
|
||||
short head = 0;
|
||||
|
||||
if (item->HitPoints <= 0)
|
||||
{
|
||||
|
@ -105,18 +111,18 @@ namespace TEN::Entities::TR3
|
|||
GetCreatureMood(item, &AI, true);
|
||||
CreatureMood(item, &AI, true);
|
||||
|
||||
angle = CreatureTurn(item, info->MaxTurn);
|
||||
angle = CreatureTurn(item, creature->MaxTurn);
|
||||
|
||||
if (item->TouchBits)
|
||||
DoDamage(LaraItem, (item->Animation.ActiveState == TREX_STATE_RUN_FORWARD) ? 10 : 1);
|
||||
DoDamage(LaraItem, (item->Animation.ActiveState == TREX_STATE_RUN_FORWARD) ? TREX_RUN_CONTACT_DAMAGE : TREX_CONTACT_DAMAGE);
|
||||
|
||||
info->Flags = (info->Mood != MoodType::Escape && !AI.ahead && AI.enemyFacing > -FRONT_ARC && AI.enemyFacing < FRONT_ARC);
|
||||
creature->Flags = (creature->Mood != MoodType::Escape && !AI.ahead && AI.enemyFacing > -FRONT_ARC && AI.enemyFacing < FRONT_ARC);
|
||||
|
||||
if (AI.distance > pow(1500, 2) &&
|
||||
AI.distance < pow(SECTOR(4), 2) &&
|
||||
AI.bite && !info->Flags)
|
||||
AI.bite && !creature->Flags)
|
||||
{
|
||||
info->Flags = 1;
|
||||
creature->Flags = 1;
|
||||
}
|
||||
|
||||
switch (item->Animation.ActiveState)
|
||||
|
@ -126,7 +132,7 @@ namespace TEN::Entities::TR3
|
|||
item->Animation.TargetState = item->Animation.RequiredState;
|
||||
else if (AI.distance < pow(1500, 2) && AI.bite)
|
||||
item->Animation.TargetState = TREX_STATE_ATTACK;
|
||||
else if (info->Mood == MoodType::Bored || info->Flags)
|
||||
else if (creature->Mood == MoodType::Bored || creature->Flags)
|
||||
item->Animation.TargetState = TREX_STATE_WALK_FORWARD;
|
||||
else
|
||||
item->Animation.TargetState = TREX_STATE_RUN_FORWARD;
|
||||
|
@ -134,32 +140,32 @@ namespace TEN::Entities::TR3
|
|||
break;
|
||||
|
||||
case TREX_STATE_WALK_FORWARD:
|
||||
info->MaxTurn = ANGLE(2.0f);
|
||||
creature->MaxTurn = TREX_WALK_TURN_RATE_MAX;
|
||||
|
||||
if (info->Mood != MoodType::Bored || !info->Flags)
|
||||
if (creature->Mood != MoodType::Bored || !creature->Flags)
|
||||
item->Animation.TargetState = TREX_STATE_IDLE;
|
||||
else if (AI.ahead && TestProbability(0.015f))
|
||||
else if (AI.ahead && TestProbability(TREX_ROAR_CHANCE))
|
||||
{
|
||||
item->Animation.RequiredState = TREX_STATE_ROAR;
|
||||
item->Animation.TargetState = TREX_STATE_IDLE;
|
||||
item->Animation.RequiredState = TREX_STATE_ROAR;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TREX_STATE_RUN_FORWARD:
|
||||
info->MaxTurn = ANGLE(4.0f);
|
||||
creature->MaxTurn = TREX_RUN_TURN_RATE_MAX;
|
||||
|
||||
if (AI.distance < pow(SECTOR(5), 2) && AI.bite)
|
||||
item->Animation.TargetState = TREX_STATE_IDLE;
|
||||
else if (info->Flags)
|
||||
else if (creature->Flags)
|
||||
item->Animation.TargetState = TREX_STATE_IDLE;
|
||||
else if (info->Mood != MoodType::Escape &&
|
||||
AI.ahead && TestProbability(0.015f))
|
||||
else if (creature->Mood != MoodType::Escape &&
|
||||
AI.ahead && TestProbability(TREX_ROAR_CHANCE))
|
||||
{
|
||||
item->Animation.RequiredState = TREX_STATE_ROAR;
|
||||
item->Animation.TargetState = TREX_STATE_IDLE;
|
||||
item->Animation.RequiredState = TREX_STATE_ROAR;
|
||||
}
|
||||
else if (info->Mood == MoodType::Bored)
|
||||
else if (creature->Mood == MoodType::Bored)
|
||||
item->Animation.TargetState = TREX_STATE_IDLE;
|
||||
|
||||
break;
|
||||
|
@ -178,8 +184,8 @@ namespace TEN::Entities::TR3
|
|||
}
|
||||
}
|
||||
|
||||
CreatureJoint(item, 0, (short)(head * 2));
|
||||
info->JointRotation[1] = info->JointRotation[0];
|
||||
CreatureJoint(item, 0, head * 2);
|
||||
creature->JointRotation[1] = creature->JointRotation[0];
|
||||
|
||||
CreatureAnimation(itemNumber, angle, 0);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
#include "Game/items.h"
|
||||
|
||||
namespace TEN::Entities::TR3
|
||||
namespace TEN::Entities::Creatures::TR3
|
||||
{
|
||||
void LaraTRexDeath(ItemInfo* tRexItem, ItemInfo* laraItem);
|
||||
void TRexControl(short itemNumber);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue