mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-05-13 05:57:05 +03:00
Merge branch 'master' into math
This commit is contained in:
commit
dbe0713deb
185 changed files with 2928 additions and 2546 deletions
|
@ -21,7 +21,9 @@ Version 1.0.2
|
||||||
* Fix occasional leave event calls when moving closer to volumes.
|
* Fix occasional leave event calls when moving closer to volumes.
|
||||||
* Fix incorrect viewport size in windowed mode.
|
* Fix incorrect viewport size in windowed mode.
|
||||||
* Fix late landing animation dispatch in rare cases.
|
* Fix late landing animation dispatch in rare cases.
|
||||||
* Fix Lara's subway train death so that she no longer slides slowly after the animation has finished.
|
* Fix Lara's subway train death so that she no longer slides slowly after the animation has finished.
|
||||||
|
* Fix horseman's axe attack using his left foot as the damaging joint.
|
||||||
|
* Fix stargate blades needlessly pushing the player around while hardly doing any damage.
|
||||||
|
|
||||||
Lua API changes:
|
Lua API changes:
|
||||||
|
|
||||||
|
|
|
@ -477,7 +477,9 @@ BOUNDING_BOX* GetBoundsAccurate(ItemInfo* item)
|
||||||
InterpolatedBounds.Y2 = framePtr[0]->boundingBox.Y2 + (framePtr[1]->boundingBox.Y2 - framePtr[0]->boundingBox.Y2) * frac / rate;
|
InterpolatedBounds.Y2 = framePtr[0]->boundingBox.Y2 + (framePtr[1]->boundingBox.Y2 - framePtr[0]->boundingBox.Y2) * frac / rate;
|
||||||
InterpolatedBounds.Z1 = framePtr[0]->boundingBox.Z1 + (framePtr[1]->boundingBox.Z1 - framePtr[0]->boundingBox.Z1) * frac / rate;
|
InterpolatedBounds.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;
|
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);
|
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;
|
return false;
|
||||||
|
|
||||||
if (item->Pose.Position.y + GlobalCollisionBounds.Y1 >= framePtr->boundingBox.Y2)
|
if ((item->Pose.Position.y + GlobalCollisionBounds.Y1) >= framePtr->boundingBox.Y2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
float sinY = phd_sin(item->Pose.Orientation.y);
|
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 x = (dx * cosY) - (dz * sinY);
|
||||||
int z = (dz * cosY) + (dx * sinY);
|
int z = (dz * cosY) + (dx * sinY);
|
||||||
|
|
||||||
if (x < GlobalCollisionBounds.X1 - coll->Setup.Radius ||
|
if (x < (GlobalCollisionBounds.X1 - coll->Setup.Radius) ||
|
||||||
x > GlobalCollisionBounds.X2 + coll->Setup.Radius ||
|
x > (GlobalCollisionBounds.X2 + coll->Setup.Radius) ||
|
||||||
z < GlobalCollisionBounds.Z1 - coll->Setup.Radius ||
|
z < (GlobalCollisionBounds.Z1 - coll->Setup.Radius) ||
|
||||||
z > GlobalCollisionBounds.Z2 + coll->Setup.Radius)
|
z > (GlobalCollisionBounds.Z2 + coll->Setup.Radius))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -346,16 +346,14 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll)
|
||||||
itemNumber = item2->NextItem;
|
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;
|
continue;
|
||||||
|
|
||||||
if (Vector3i::Distance(item->Pose.Position, mesh->pos.Position) < COLLISION_CHECK_DISTANCE)
|
if (Vector3i::Distance(item->Pose.Position, mesh.pos.Position) < 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;
|
float distance;
|
||||||
|
|
||||||
if (box.Intersects(origin, direction, distance) && distance < coll->Setup.Radius * 2)
|
if (box.Intersects(origin, direction, distance) && distance < coll->Setup.Radius * 2)
|
||||||
|
@ -476,12 +474,12 @@ static bool ItemInRange(int x, int z, int radius)
|
||||||
return ((SQUARE(x) + SQUARE(z)) <= SQUARE(radius));
|
return ((SQUARE(x) + SQUARE(z)) <= SQUARE(radius));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ItemNearLara(PoseData* pos, int radius)
|
bool ItemNearLara(Vector3i* origin, int radius)
|
||||||
{
|
{
|
||||||
auto target = GameVector(
|
auto target = GameVector(
|
||||||
pos->Position.x - LaraItem->Pose.Position.x,
|
origin->x - LaraItem->Pose.Position.x,
|
||||||
pos->Position.y - LaraItem->Pose.Position.y,
|
origin->y - LaraItem->Pose.Position.y,
|
||||||
pos->Position.z - LaraItem->Pose.Position.z
|
origin->z - LaraItem->Pose.Position.z
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!ItemCollide(target.y, ITEM_RADIUS_YMAX))
|
if (!ItemCollide(target.y, ITEM_RADIUS_YMAX))
|
||||||
|
@ -500,9 +498,9 @@ bool ItemNearLara(PoseData* pos, int radius)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ItemNearTarget(PoseData* origin, ItemInfo* target, int radius)
|
bool ItemNearTarget(Vector3i* 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))
|
if (!ItemCollide(pos.y, ITEM_RADIUS_YMAX))
|
||||||
return false;
|
return false;
|
||||||
|
@ -513,31 +511,31 @@ bool ItemNearTarget(PoseData* origin, ItemInfo* target, int radius)
|
||||||
if (!ItemInRange(pos.x, pos.z, radius))
|
if (!ItemInRange(pos.x, pos.z, radius))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto* bounds = GetBoundsAccurate(target);
|
auto* bounds = GetBoundsAccurate(targetEntity);
|
||||||
if (pos.y >= bounds->Y1 && pos.y <= bounds->Y2)
|
if (pos.y >= bounds->Y1 && pos.y <= bounds->Y2)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Move3DPosTo3DPos(PoseData* origin, PoseData* target, int velocity, short angleAdd)
|
bool Move3DPosTo3DPos(PoseData* fromPose, PoseData* toPose, int velocity, short angleAdd)
|
||||||
{
|
{
|
||||||
auto direction = target->Position - origin->Position;
|
auto direction = toPose->Position - fromPose->Position;
|
||||||
int distance = sqrt(SQUARE(direction.x) + SQUARE(direction.y) + SQUARE(direction.z));
|
float distance = Vector3i::Distance(fromPose->Position, toPose->Position);
|
||||||
|
|
||||||
if (velocity < distance)
|
if (velocity < distance)
|
||||||
origin->Position += direction * velocity / distance;
|
fromPose->Position += direction * (velocity / distance);
|
||||||
else
|
else
|
||||||
origin->Position = target->Position;
|
fromPose->Position = toPose->Position;
|
||||||
|
|
||||||
if (!Lara.Control.IsMoving)
|
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)
|
if (shouldAnimate && Lara.Control.WaterStatus != WaterStatus::Underwater)
|
||||||
{
|
{
|
||||||
int angle = mGetAngle(target->Position.x, target->Position.z, origin->Position.x, origin->Position.z);
|
int angle = mGetAngle(toPose->Position.x, toPose->Position.z, fromPose->Position.x, fromPose->Position.z);
|
||||||
int direction = (GetQuadrant(angle) - GetQuadrant(target->Orientation.y)) & 3;
|
int direction = (GetQuadrant(angle) - GetQuadrant(toPose->Orientation.y)) & 3;
|
||||||
|
|
||||||
switch (direction)
|
switch (direction)
|
||||||
{
|
{
|
||||||
|
@ -568,31 +566,31 @@ bool Move3DPosTo3DPos(PoseData* origin, PoseData* target, int velocity, short an
|
||||||
Lara.Control.Count.PositionAdjust = 0;
|
Lara.Control.Count.PositionAdjust = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
short deltaAngle = target->Orientation.x - origin->Orientation.x;
|
short deltaAngle = toPose->Orientation.x - fromPose->Orientation.x;
|
||||||
if (deltaAngle > angleAdd)
|
if (deltaAngle > angleAdd)
|
||||||
origin->Orientation.x += angleAdd;
|
fromPose->Orientation.x += angleAdd;
|
||||||
else if (deltaAngle < -angleAdd)
|
else if (deltaAngle < -angleAdd)
|
||||||
origin->Orientation.x -= angleAdd;
|
fromPose->Orientation.x -= angleAdd;
|
||||||
else
|
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)
|
if (deltaAngle > angleAdd)
|
||||||
origin->Orientation.y += angleAdd;
|
fromPose->Orientation.y += angleAdd;
|
||||||
else if (deltaAngle < -angleAdd)
|
else if (deltaAngle < -angleAdd)
|
||||||
origin->Orientation.y -= angleAdd;
|
fromPose->Orientation.y -= angleAdd;
|
||||||
else
|
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)
|
if (deltaAngle > angleAdd)
|
||||||
origin->Orientation.z += angleAdd;
|
fromPose->Orientation.z += angleAdd;
|
||||||
else if (deltaAngle < -angleAdd)
|
else if (deltaAngle < -angleAdd)
|
||||||
origin->Orientation.z -= angleAdd;
|
fromPose->Orientation.z -= angleAdd;
|
||||||
else
|
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)
|
bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius)
|
||||||
|
@ -611,13 +609,13 @@ bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius)
|
||||||
|
|
||||||
int x = laraItem->Pose.Position.x - item->Pose.Position.x;
|
int x = laraItem->Pose.Position.x - item->Pose.Position.x;
|
||||||
int z = laraItem->Pose.Position.z - item->Pose.Position.z;
|
int z = laraItem->Pose.Position.z - item->Pose.Position.z;
|
||||||
int dx = (cosY * x) - (sinY * z);
|
int dx = (x * cosY) - (z * sinY);
|
||||||
int dz = (cosY * z) + (sinY * x);
|
int dz = (z * cosY) + (x * sinY);
|
||||||
|
|
||||||
if (dx >= bounds->X1 - radius &&
|
if (dx >= (bounds->X1 - radius) &&
|
||||||
dx <= radius + bounds->X2 &&
|
dx <= (radius + bounds->X2) &&
|
||||||
dz >= bounds->Z1 - radius &&
|
dz >= (bounds->Z1 - radius) &&
|
||||||
dz <= radius + bounds->Z2)
|
dz <= (radius + bounds->Z2))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -673,11 +671,7 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa
|
||||||
int rx = (dx * cosY) - (dz * sinY);
|
int rx = (dx * cosY) - (dz * sinY);
|
||||||
int rz = (dz * cosY) + (dx * sinY);
|
int rz = (dz * cosY) + (dx * sinY);
|
||||||
|
|
||||||
BOUNDING_BOX* bounds;
|
auto* bounds = (bigPush & 2) ? &GlobalCollisionBounds : (BOUNDING_BOX*)GetBestFrame(item);
|
||||||
if (bigPush & 2)
|
|
||||||
bounds = &GlobalCollisionBounds;
|
|
||||||
else
|
|
||||||
bounds = (BOUNDING_BOX*)GetBestFrame(item);
|
|
||||||
|
|
||||||
int minX = bounds->X1;
|
int minX = bounds->X1;
|
||||||
int maxX = bounds->X2;
|
int maxX = bounds->X2;
|
||||||
|
@ -714,8 +708,8 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa
|
||||||
else
|
else
|
||||||
rz -= bottom;
|
rz -= bottom;
|
||||||
|
|
||||||
item2->Pose.Position.x = item->Pose.Position.x + cosY * rx + sinY * rz;
|
item2->Pose.Position.x = item->Pose.Position.x + (rx * cosY) + (rz * sinY);
|
||||||
item2->Pose.Position.z = item->Pose.Position.z + cosY * rz - sinY * rx;
|
item2->Pose.Position.z = item->Pose.Position.z + (rz * cosY) - (rx * sinY);
|
||||||
|
|
||||||
auto* lara = item2->IsLara() ? GetLaraInfo(item2) : nullptr;
|
auto* lara = item2->IsLara() ? GetLaraInfo(item2) : nullptr;
|
||||||
|
|
||||||
|
@ -740,12 +734,12 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa
|
||||||
coll->Setup.LowerCeilingBound = 0;
|
coll->Setup.LowerCeilingBound = 0;
|
||||||
coll->Setup.UpperCeilingBound = MAX_HEIGHT;
|
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);
|
coll->Setup.ForwardAngle = phd_atan(item2->Pose.Position.z - coll->Setup.OldPosition.z, item2->Pose.Position.x - coll->Setup.OldPosition.x);
|
||||||
|
|
||||||
GetCollisionInfo(coll, item2);
|
GetCollisionInfo(coll, item2);
|
||||||
|
|
||||||
coll->Setup.ForwardAngle = facing;
|
coll->Setup.ForwardAngle = headingAngle;
|
||||||
|
|
||||||
if (coll->CollisionType == CT_NONE)
|
if (coll->CollisionType == CT_NONE)
|
||||||
{
|
{
|
||||||
|
@ -763,8 +757,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 is in the process of aligning to an object, cancel it.
|
||||||
if (lara != nullptr && lara->Control.Count.PositionAdjust > (LARA_POSITION_ADJUST_MAX_TIME / 6))
|
if (lara != nullptr && lara->Control.Count.PositionAdjust > (LARA_POSITION_ADJUST_MAX_TIME / 6))
|
||||||
{
|
{
|
||||||
Lara.Control.IsMoving = false;
|
lara->Control.IsMoving = false;
|
||||||
Lara.Control.HandStatus = HandStatus::Free;
|
lara->Control.HandStatus = HandStatus::Free;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -853,7 +847,7 @@ void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll)
|
||||||
{
|
{
|
||||||
auto* mesh = &g_Level.Rooms[i].mesh[j];
|
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 ((mesh->flags & StaticMeshFlags::SM_VISIBLE) && (mesh->flags & StaticMeshFlags::SM_SOLID))
|
||||||
{
|
{
|
||||||
if (Vector3i::Distance(item->Pose.Position, mesh->pos.Position) < COLLISION_CHECK_DISTANCE)
|
if (Vector3i::Distance(item->Pose.Position, mesh->pos.Position) < COLLISION_CHECK_DISTANCE)
|
||||||
|
@ -867,39 +861,39 @@ void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PoseData pos, CollisionInfo* coll)
|
bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PoseData pose, CollisionInfo* coll)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
// Get DX static bounds in global coords
|
// Get DX static bounds in global coordinates.
|
||||||
auto staticBounds = TO_DX_BBOX(pos, box);
|
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 itemBBox = GetBoundsAccurate(item);
|
||||||
auto itemBounds = TO_DX_BBOX(item->Pose, itemBBox);
|
auto itemBounds = TO_DX_BBOX(item->Pose, itemBBox);
|
||||||
|
|
||||||
// Extend bounds a bit for visual testing
|
// Extend bounds a bit for visual testing.
|
||||||
itemBounds.Extents = itemBounds.Extents + Vector3(WALL_SIZE);
|
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))
|
if (!staticBounds.Intersects(itemBounds))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Bring back extents
|
// Bring back extents.
|
||||||
itemBounds.Extents = itemBounds.Extents - Vector3(WALL_SIZE);
|
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);
|
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;
|
BOUNDING_BOX collBox;
|
||||||
collBox.X1 = -coll->Setup.Radius;
|
collBox.X1 = -coll->Setup.Radius;
|
||||||
collBox.X2 = coll->Setup.Radius;
|
collBox.X2 = coll->Setup.Radius;
|
||||||
collBox.Z1 = -coll->Setup.Radius;
|
collBox.Z1 = -coll->Setup.Radius;
|
||||||
collBox.Z2 = coll->Setup.Radius;
|
collBox.Z2 = coll->Setup.Radius;
|
||||||
|
|
||||||
// Calculate vertical item coll bounds according to either height (land mode) or precise bounds (water mode).
|
// Calculate vertical item collision 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.
|
// Water mode needs special processing because height calculation in original engines is inconsistent in such cases.
|
||||||
if (TestEnvironment(ENV_FLAG_WATER, item))
|
if (TestEnvironment(ENV_FLAG_WATER, item))
|
||||||
{
|
{
|
||||||
collBox.Y1 = itemBBox->Y1;
|
collBox.Y1 = itemBBox->Y1;
|
||||||
|
@ -911,18 +905,18 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PoseData pos, Collisi
|
||||||
collBox.Y2 = 0;
|
collBox.Y2 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get and test DX item coll bounds
|
// Get and test DX item collision bounds.
|
||||||
auto collBounds = TO_DX_BBOX(PoseData(item->Pose.Position), &collBox);
|
auto collBounds = TO_DX_BBOX(PoseData(item->Pose.Position), &collBox);
|
||||||
bool intersects = staticBounds.Intersects(collBounds);
|
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(PoseData(coll->Setup.OldPosition.x, item->Pose.Position.y, coll->Setup.OldPosition.z), &collBox);
|
auto oldCollBounds = TO_DX_BBOX(PoseData(coll->Setup.OldPosition.x, item->Pose.Position.y, coll->Setup.OldPosition.z), &collBox);
|
||||||
bool oldHorIntersects = staticBounds.Intersects(oldCollBounds);
|
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);
|
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];
|
Vector3 corners[8];
|
||||||
staticBounds.GetCorners(corners);
|
staticBounds.GetCorners(corners);
|
||||||
Vector3 planeVertices[4][3] =
|
Vector3 planeVertices[4][3] =
|
||||||
|
@ -933,7 +927,7 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PoseData pos, Collisi
|
||||||
{ corners[3], corners[6], corners[2] }
|
{ corners[3], corners[6], corners[2] }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Determine collision box vertical dimensions
|
// Determine collision box vertical dimensions.
|
||||||
auto height = collBox.Height();
|
auto height = collBox.Height();
|
||||||
auto center = item->Pose.Position.y - height / 2;
|
auto center = item->Pose.Position.y - height / 2;
|
||||||
|
|
||||||
|
@ -945,24 +939,23 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PoseData pos, Collisi
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
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.0f);
|
auto mxR = Matrix::CreateFromYawPitchRoll(TO_RAD(item->Pose.Orientation.y), TO_RAD(item->Pose.Orientation.x + (ANGLE(90 * i))), 0.0f);
|
||||||
auto mxT = Matrix::CreateTranslation(Vector3::UnitY);
|
auto mxT = Matrix::CreateTranslation(Vector3::UnitY);
|
||||||
auto direction = (mxT * mxR).Translation();
|
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);
|
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++)
|
for (int p = 0; p < 4; p++)
|
||||||
{
|
{
|
||||||
// No plane intersection, quickly discard
|
// No plane intersection, quickly discard.
|
||||||
float d = 0.0f;
|
float d = 0.0f;
|
||||||
if (!ray.Intersects(planeVertices[p][0], planeVertices[p][1], planeVertices[p][2], d))
|
if (!ray.Intersects(planeVertices[p][0], planeVertices[p][1], planeVertices[p][2], d))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Process plane intersection only if distance is smaller
|
// Process plane intersection only if distance is smaller than already found minimum.
|
||||||
// than already found minimum
|
|
||||||
if (d < minDistance)
|
if (d < minDistance)
|
||||||
{
|
{
|
||||||
closestRay = ray;
|
closestRay = ray;
|
||||||
|
@ -972,24 +965,25 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PoseData pos, Collisi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closestPlane != -1) // Top/bottom plane found
|
// Top/bottom plane found.
|
||||||
|
if (closestPlane != -1)
|
||||||
{
|
{
|
||||||
auto bottom = closestPlane >= 2;
|
auto bottom = closestPlane >= 2;
|
||||||
auto yPoint = abs((closestRay.direction * minDistance).y);
|
auto yPoint = abs((closestRay.direction * minDistance).y);
|
||||||
auto distanceToVerticalPlane = height / 2 - yPoint;
|
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 (intersects && oldHorIntersects && minDistance < height)
|
||||||
{
|
{
|
||||||
if (bottom)
|
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;
|
item->Pose.Position.y += distanceToVerticalPlane + 2;
|
||||||
coll->CollisionType = CT_TOP;
|
coll->CollisionType = CT_TOP;
|
||||||
}
|
}
|
||||||
else
|
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;
|
item->Pose.Position.y -= distanceToVerticalPlane;
|
||||||
coll->CollisionType = (g_Level.Rooms[item->RoomNumber].flags & 1) ? coll->CollisionType : CT_CLAMP;
|
coll->CollisionType = (g_Level.Rooms[item->RoomNumber].flags & 1) ? coll->CollisionType : CT_CLAMP;
|
||||||
}
|
}
|
||||||
|
@ -1005,29 +999,29 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PoseData pos, Collisi
|
||||||
if (!intersects)
|
if (!intersects)
|
||||||
return false;
|
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(PoseData(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z), &collBox)))
|
if (!staticBounds.Intersects(TO_DX_BBOX(PoseData(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z), &collBox)))
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
// Determine identity rotation/distance
|
// Determine identity orientation/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 distance = (item->Pose.Position - pose.Position).ToVector3();
|
||||||
auto sinY = phd_sin(pos.Orientation.y);
|
auto sinY = phd_sin(pose.Orientation.y);
|
||||||
auto cosY = phd_cos(pos.Orientation.y);
|
auto cosY = phd_cos(pose.Orientation.y);
|
||||||
|
|
||||||
// Rotate item to collision bounds identity
|
// Rotate item to collision bounds identity.
|
||||||
auto x = round(distance.x * cosY - distance.z * sinY) + pos.Position.x;
|
auto x = round((distance.x * cosY) - (distance.z * sinY)) + pose.Position.x;
|
||||||
auto y = item->Pose.Position.y;
|
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
|
// Determine identity static collision bounds.
|
||||||
auto XMin = pos.Position.x + box->X1;
|
auto XMin = pose.Position.x + box->X1;
|
||||||
auto XMax = pos.Position.x + box->X2;
|
auto XMax = pose.Position.x + box->X2;
|
||||||
auto YMin = pos.Position.y + box->Y1;
|
auto YMin = pose.Position.y + box->Y1;
|
||||||
auto YMax = pos.Position.y + box->Y2;
|
auto YMax = pose.Position.y + box->Y2;
|
||||||
auto ZMin = pos.Position.z + box->Z1;
|
auto ZMin = pose.Position.z + box->Z1;
|
||||||
auto ZMax = pos.Position.z + box->Z2;
|
auto ZMax = pose.Position.z + box->Z2;
|
||||||
|
|
||||||
// Determine item collision bounds
|
// Determine item collision bounds.
|
||||||
auto inXMin = x + collBox.X1;
|
auto inXMin = x + collBox.X1;
|
||||||
auto inXMax = x + collBox.X2;
|
auto inXMax = x + collBox.X2;
|
||||||
auto inYMin = y + collBox.Y1;
|
auto inYMin = y + collBox.Y1;
|
||||||
|
@ -1035,16 +1029,15 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PoseData pos, Collisi
|
||||||
auto inZMin = z + collBox.Z1;
|
auto inZMin = z + collBox.Z1;
|
||||||
auto inZMax = z + collBox.Z2;
|
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 ||
|
if (inXMax <= XMin || inXMin >= XMax ||
|
||||||
inYMax <= YMin || inYMin >= YMax ||
|
inYMax <= YMin || inYMin >= YMax ||
|
||||||
inZMax <= ZMin || inZMin >= ZMax)
|
inZMax <= ZMin || inZMin >= ZMax)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
// Calculate shifts
|
// Calculate shifts.
|
||||||
|
|
||||||
Vector3i rawShift = {};
|
|
||||||
|
|
||||||
|
auto rawShift = Vector3i::Zero;
|
||||||
auto shiftLeft = inXMax - XMin;
|
auto shiftLeft = inXMax - XMin;
|
||||||
auto shiftRight = XMax - inXMin;
|
auto shiftRight = XMax - inXMin;
|
||||||
|
|
||||||
|
@ -1061,13 +1054,13 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PoseData pos, Collisi
|
||||||
else
|
else
|
||||||
rawShift.z = shiftRight;
|
rawShift.z = shiftRight;
|
||||||
|
|
||||||
// Rotate previous collision position to identity
|
// 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);
|
distance = (coll->Setup.OldPosition - pose.Position).ToVector3();
|
||||||
auto ox = round(distance.x * cosY - distance.z * sinY) + pos.Position.x;
|
auto ox = round((distance.x * cosY) - (distance.z * sinY)) + pose.Position.x;
|
||||||
auto oz = round(distance.x * sinY + distance.z * cosY) + pos.Position.z;
|
auto oz = round((distance.x * sinY) + (distance.z * cosY)) + pose.Position.z;
|
||||||
|
|
||||||
// Calculate collisison type based on identity rotation
|
// Calculate collisison type based on identity orientation.
|
||||||
switch (GetQuadrant(coll->Setup.ForwardAngle - pos.Orientation.y))
|
switch (GetQuadrant(coll->Setup.ForwardAngle - pose.Orientation.y))
|
||||||
{
|
{
|
||||||
case NORTH:
|
case NORTH:
|
||||||
if (rawShift.x > coll->Setup.Radius || rawShift.x < -coll->Setup.Radius)
|
if (rawShift.x > coll->Setup.Radius || rawShift.x < -coll->Setup.Radius)
|
||||||
|
@ -1158,19 +1151,19 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PoseData pos, Collisi
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine final shifts rotation/distance
|
// Determine final shifts orientation/distance.
|
||||||
distance = Vector3(x + coll->Shift.x, y, z + coll->Shift.z) - Vector3(pos.Position.x, pos.Position.y, pos.Position.z);
|
distance = Vector3(x + coll->Shift.x, y, z + coll->Shift.z) - pose.Position.ToVector3();
|
||||||
sinY = phd_sin(-pos.Orientation.y);
|
sinY = phd_sin(-pose.Orientation.y);
|
||||||
cosY = phd_cos(-pos.Orientation.y);
|
cosY = phd_cos(-pose.Orientation.y);
|
||||||
|
|
||||||
// Calculate final shifts rotation/distance
|
// Calculate final shifts orientation/distance.
|
||||||
coll->Shift.x = (round(distance.x * cosY - distance.z * sinY) + pos.Position.x) - item->Pose.Position.x;
|
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) + pos.Position.z) - item->Pose.Position.z;
|
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)
|
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)
|
if (item == LaraItem && coll->CollisionType == CT_FRONT)
|
||||||
coll->HitTallObject = (YMin <= inYMin + LARA_HEADROOM);
|
coll->HitTallObject = (YMin <= inYMin + LARA_HEADROOM);
|
||||||
|
|
||||||
|
@ -1182,39 +1175,39 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
{
|
{
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
|
|
||||||
auto prevCollResult = GetCollision(x, y, z, item->RoomNumber);
|
auto prevPointProbe = GetCollision(x, y, z, item->RoomNumber);
|
||||||
auto collResult = GetCollision(item);
|
auto pointProbe = GetCollision(item);
|
||||||
|
|
||||||
auto* bounds = GetBoundsAccurate(item);
|
auto* bounds = GetBoundsAccurate(item);
|
||||||
int radius = bounds->Height();
|
int radius = bounds->Height();
|
||||||
|
|
||||||
item->Pose.Position.y += radius;
|
item->Pose.Position.y += radius;
|
||||||
|
|
||||||
if (item->Pose.Position.y >= collResult.Position.Floor)
|
if (item->Pose.Position.y >= pointProbe.Position.Floor)
|
||||||
{
|
{
|
||||||
int bs = 0;
|
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);
|
int yAngle = (long)((unsigned short)item->Pose.Orientation.y);
|
||||||
|
|
||||||
if (collResult.FloorTilt.x < 0)
|
if (pointProbe.FloorTilt.x < 0)
|
||||||
{
|
{
|
||||||
if (yAngle >= ANGLE(180.0f))
|
if (yAngle >= ANGLE(180.0f))
|
||||||
bs = 1;
|
bs = 1;
|
||||||
}
|
}
|
||||||
else if (collResult.FloorTilt.x > 0)
|
else if (pointProbe.FloorTilt.x > 0)
|
||||||
{
|
{
|
||||||
if (yAngle <= ANGLE(180.0f))
|
if (yAngle <= ANGLE(180.0f))
|
||||||
bs = 1;
|
bs = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (collResult.FloorTilt.y < 0)
|
if (pointProbe.FloorTilt.y < 0)
|
||||||
{
|
{
|
||||||
if (yAngle >= ANGLE(90.0f) && yAngle <= ANGLE(270.0f))
|
if (yAngle >= ANGLE(90.0f) && yAngle <= ANGLE(270.0f))
|
||||||
bs = 1;
|
bs = 1;
|
||||||
}
|
}
|
||||||
else if (collResult.FloorTilt.y > 0)
|
else if (pointProbe.FloorTilt.y > 0)
|
||||||
{
|
{
|
||||||
if (yAngle <= ANGLE(90.0f) || yAngle >= ANGLE(270.0f))
|
if (yAngle <= ANGLE(90.0f) || yAngle >= ANGLE(270.0f))
|
||||||
bs = 1;
|
bs = 1;
|
||||||
|
@ -1223,7 +1216,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 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))) ||
|
(((x / SECTOR(1)) != (item->Pose.Position.x / SECTOR(1))) ||
|
||||||
((z / SECTOR(1)) != (item->Pose.Position.z / SECTOR(1)))))
|
((z / SECTOR(1)) != (item->Pose.Position.z / SECTOR(1)))))
|
||||||
{
|
{
|
||||||
|
@ -1231,8 +1224,8 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
|
|
||||||
long xs;
|
long xs;
|
||||||
|
|
||||||
if ((x & (~(WALL_SIZE - 1))) != (item->Pose.Position.x & (~(WALL_SIZE - 1))) && // X crossed boundary?
|
if ((x & (~WALL_MASK)) != (item->Pose.Position.x & (~WALL_MASK)) && // X crossed boundary?
|
||||||
(z & (~(WALL_SIZE - 1))) != (item->Pose.Position.z & (~(WALL_SIZE - 1)))) // Z crossed boundary as well?
|
(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))
|
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).
|
xs = 1; // X has travelled the shortest, so (maybe) hit first. (Seems to work ok).
|
||||||
|
@ -1242,7 +1235,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
else
|
else
|
||||||
xs = 1;
|
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).
|
// Hit angle = ANGLE(270.0f).
|
||||||
if (xv <= 0)
|
if (xv <= 0)
|
||||||
|
@ -1263,14 +1256,14 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
item->Pose.Position.z = z;
|
item->Pose.Position.z = z;
|
||||||
}
|
}
|
||||||
// Hit a steep slope?
|
// Hit a steep slope?
|
||||||
else if (collResult.Position.FloorSlope)
|
else if (pointProbe.Position.FloorSlope)
|
||||||
{
|
{
|
||||||
// Need to know which direction the slope is.
|
// Need to know which direction the slope is.
|
||||||
|
|
||||||
item->Animation.Velocity.z -= (item->Animation.Velocity.z / 4);
|
item->Animation.Velocity.z -= (item->Animation.Velocity.z / 4);
|
||||||
|
|
||||||
// Hit angle = ANGLE(90.0f)
|
// 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))
|
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(180.0f))
|
||||||
{
|
{
|
||||||
|
@ -1282,7 +1275,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
{
|
{
|
||||||
if (item->Animation.Velocity.z < 32)
|
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))
|
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);
|
item->Pose.Orientation.y -= ANGLE(22.5f);
|
||||||
|
@ -1304,7 +1297,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Hit angle = ANGLE(270.0f)
|
// 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))
|
if (((unsigned short)item->Pose.Orientation.y) < ANGLE(180.0f))
|
||||||
{
|
{
|
||||||
|
@ -1316,7 +1309,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
{
|
{
|
||||||
if (item->Animation.Velocity.z < 32)
|
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))
|
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);
|
item->Pose.Orientation.y -= ANGLE(22.5f);
|
||||||
|
@ -1338,7 +1331,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Hit angle = 0
|
// 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))
|
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(90.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(270.0f))
|
||||||
{
|
{
|
||||||
|
@ -1350,7 +1343,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
{
|
{
|
||||||
if (item->Animation.Velocity.z < 32)
|
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))
|
if ((unsigned short)item->Pose.Orientation.y < ANGLE(180.0f))
|
||||||
{
|
{
|
||||||
|
@ -1373,7 +1366,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Hit angle = ANGLE(180.0f)
|
// 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))
|
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(270.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(90.0f))
|
||||||
{
|
{
|
||||||
|
@ -1385,7 +1378,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
{
|
{
|
||||||
if (item->Animation.Velocity.z < 32)
|
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))
|
if ((unsigned short)item->Pose.Orientation.y > ANGLE(180.0f))
|
||||||
{
|
{
|
||||||
|
@ -1407,7 +1400,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
item->Animation.Velocity.y = 0;
|
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))
|
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(135.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(315.0f))
|
||||||
{
|
{
|
||||||
|
@ -1419,7 +1412,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
{
|
{
|
||||||
if (item->Animation.Velocity.z < 32)
|
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))
|
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);
|
item->Pose.Orientation.y -= ANGLE(22.5f);
|
||||||
|
@ -1441,7 +1434,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Hit angle = ANGLE(135.0f)
|
// 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))
|
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(225.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(45.0f))
|
||||||
{
|
{
|
||||||
|
@ -1453,7 +1446,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
{
|
{
|
||||||
if (item->Animation.Velocity.z < 32)
|
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))
|
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);
|
item->Pose.Orientation.y -= ANGLE(22.5f);
|
||||||
|
@ -1475,7 +1468,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Hit angle = ANGLE(225.5f)
|
// 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))
|
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(315.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(135.0f))
|
||||||
{
|
{
|
||||||
|
@ -1487,7 +1480,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
{
|
{
|
||||||
if (item->Animation.Velocity.z < 32)
|
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))
|
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);
|
item->Pose.Orientation.y -= ANGLE(22.5f);
|
||||||
|
@ -1509,7 +1502,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Hit angle = ANGLE(315.0f)
|
// 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))
|
if (((unsigned short)item->Pose.Orientation.y) > ANGLE(45.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(225.5f))
|
||||||
{
|
{
|
||||||
|
@ -1521,7 +1514,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
{
|
{
|
||||||
if (item->Animation.Velocity.z < 32)
|
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))
|
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);
|
item->Pose.Orientation.y -= ANGLE(22.5f);
|
||||||
|
@ -1543,7 +1536,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.x = x;
|
||||||
item->Pose.Position.y = y;
|
item->Pose.Position.y = y;
|
||||||
item->Pose.Position.z = z;
|
item->Pose.Position.z = z;
|
||||||
|
@ -1582,7 +1575,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.
|
// Check for on top of object.
|
||||||
|
@ -1590,8 +1583,8 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
{
|
{
|
||||||
if (yv >= 0)
|
if (yv >= 0)
|
||||||
{
|
{
|
||||||
prevCollResult = GetCollision(item->Pose.Position.x, y, item->Pose.Position.z, item->RoomNumber);
|
prevPointProbe = GetCollision(item->Pose.Position.x, y, item->Pose.Position.z, item->RoomNumber);
|
||||||
collResult = GetCollision(item);
|
pointProbe = GetCollision(item);
|
||||||
|
|
||||||
// Bounce off floor.
|
// Bounce off floor.
|
||||||
|
|
||||||
|
@ -1599,7 +1592,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.
|
// 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
|
// 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.
|
// Hit the floor; bounce and slow down.
|
||||||
if (item->Animation.Velocity.y > 0)
|
if (item->Animation.Velocity.y > 0)
|
||||||
|
@ -1633,17 +1626,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
|
// else
|
||||||
{
|
{
|
||||||
// Bounce off ceiling.
|
// 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))) ||
|
(((x / SECTOR(1)) != (item->Pose.Position.x / SECTOR(1))) ||
|
||||||
((z / SECTOR(1)) != (item->Pose.Position.z / SECTOR(1)))))
|
((z / SECTOR(1)) != (item->Pose.Position.z / SECTOR(1)))))
|
||||||
{
|
{
|
||||||
|
@ -1666,13 +1659,13 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv,
|
||||||
else
|
else
|
||||||
item->Animation.Velocity.z /= 2;
|
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.x = x;
|
||||||
item->Pose.Position.y = y;
|
item->Pose.Position.y = y;
|
||||||
item->Pose.Position.z = z;
|
item->Pose.Position.z = z;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
item->Pose.Position.y = collResult.Position.Ceiling;
|
item->Pose.Position.y = pointProbe.Position.Ceiling;
|
||||||
|
|
||||||
if (item->Animation.Velocity.y < 0)
|
if (item->Animation.Velocity.y < 0)
|
||||||
item->Animation.Velocity.y = -item->Animation.Velocity.y;
|
item->Animation.Velocity.y = -item->Animation.Velocity.y;
|
||||||
|
@ -1680,14 +1673,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);
|
Splash(item);
|
||||||
|
|
||||||
ItemNewRoom(itemNumber, collResult.RoomNumber);
|
ItemNewRoom(itemNumber, pointProbe.RoomNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
item->Pose.Position.y -= radius;
|
item->Pose.Position.y -= radius;
|
||||||
|
@ -1776,8 +1769,8 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DoDamage(item, INT_MAX);
|
DoDamage(item, INT_MAX);
|
||||||
|
DoLotsOfBlood(
|
||||||
DoLotsOfBlood(item->Pose.Position.x,
|
item->Pose.Position.x,
|
||||||
laraItem->Pose.Position.y - CLICK(1),
|
laraItem->Pose.Position.y - CLICK(1),
|
||||||
item->Pose.Position.z,
|
item->Pose.Position.z,
|
||||||
laraItem->Animation.Velocity.z,
|
laraItem->Animation.Velocity.z,
|
||||||
|
@ -1882,7 +1875,7 @@ void CreatureCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll
|
||||||
int rx = (frame->boundingBox.X1 + frame->boundingBox.X2) / 2;
|
int rx = (frame->boundingBox.X1 + frame->boundingBox.X2) / 2;
|
||||||
int rz = (frame->boundingBox.X2 + frame->boundingBox.Z2) / 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);
|
auto* lara = GetLaraInfo(laraItem);
|
||||||
|
|
||||||
|
@ -1890,7 +1883,7 @@ void CreatureCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll
|
||||||
|
|
||||||
lara->HitDirection = (short)angle;
|
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++;
|
lara->HitFrame++;
|
||||||
if (lara->HitFrame > 30)
|
if (lara->HitFrame > 30)
|
||||||
lara->HitFrame = 30;
|
lara->HitFrame = 30;
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Math/Math.h"
|
#include "Math/Math.h"
|
||||||
#include "Math/Math.h"
|
|
||||||
|
|
||||||
struct ItemInfo;
|
|
||||||
struct CollisionInfo;
|
|
||||||
class FloorInfo;
|
class FloorInfo;
|
||||||
|
struct CollisionInfo;
|
||||||
|
struct ItemInfo;
|
||||||
struct MESH_INFO;
|
struct MESH_INFO;
|
||||||
|
|
||||||
constexpr auto MAX_COLLIDED_OBJECTS = 1024;
|
constexpr auto MAX_COLLIDED_OBJECTS = 1024;
|
||||||
constexpr auto ITEM_RADIUS_YMAX = SECTOR(3);
|
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 BOUNDING_BOX GlobalCollisionBounds;
|
||||||
extern ItemInfo* CollidedItems[MAX_COLLIDED_OBJECTS];
|
extern ItemInfo* CollidedItems[MAX_COLLIDED_OBJECTS];
|
||||||
|
@ -36,17 +35,17 @@ bool TestLaraPosition(OBJECT_COLLISION_BOUNDS* bounds, ItemInfo* item, ItemInfo*
|
||||||
bool AlignLaraPosition(Vector3i* offset, ItemInfo* item, ItemInfo* laraItem);
|
bool AlignLaraPosition(Vector3i* offset, ItemInfo* item, ItemInfo* laraItem);
|
||||||
bool MoveLaraPosition(Vector3i* pos, ItemInfo* item, ItemInfo* laraItem);
|
bool MoveLaraPosition(Vector3i* pos, ItemInfo* item, ItemInfo* laraItem);
|
||||||
|
|
||||||
bool ItemNearLara(PoseData* pos, int radius);
|
bool ItemNearLara(Vector3i* origin, int radius);
|
||||||
bool ItemNearTarget(PoseData* origin, ItemInfo* target, int radius);
|
bool ItemNearTarget(Vector3i* origin, ItemInfo* targetEntity, int radius);
|
||||||
|
|
||||||
bool Move3DPosTo3DPos(PoseData* origin, PoseData* target, int velocity, short angleAdd);
|
bool Move3DPosTo3DPos(PoseData* fromPose, PoseData* toPose, int velocity, short angleAdd);
|
||||||
|
|
||||||
bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius);
|
bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius);
|
||||||
bool TestBoundsCollideStatic(ItemInfo* item, MESH_INFO* mesh, int radius);
|
bool TestBoundsCollideStatic(ItemInfo* item, MESH_INFO* mesh, int radius);
|
||||||
bool ItemPushItem(ItemInfo* item, ItemInfo* laraItem, CollisionInfo* coll, bool spasmEnabled, char bigPush);
|
bool ItemPushItem(ItemInfo* item, ItemInfo* laraItem, CollisionInfo* coll, bool spasmEnabled, char bigPush);
|
||||||
bool ItemPushStatic(ItemInfo* laraItem, MESH_INFO* mesh, CollisionInfo* coll);
|
bool ItemPushStatic(ItemInfo* laraItem, MESH_INFO* mesh, CollisionInfo* coll);
|
||||||
|
|
||||||
bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PoseData pos, CollisionInfo* coll);
|
bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PoseData pose, CollisionInfo* coll);
|
||||||
void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll);
|
void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll);
|
||||||
|
|
||||||
void AIPickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
void AIPickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||||
|
|
|
@ -77,7 +77,7 @@ int TestCollision(ItemInfo* item, ItemInfo* laraItem)
|
||||||
int dz = z1 - z2;
|
int dz = z1 - z2;
|
||||||
int r = r1 + r2;
|
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);
|
item->SetBits(JointBitType::Touch, i);
|
||||||
laraItem->SetBits(JointBitType::Touch, j);
|
laraItem->SetBits(JointBitType::Touch, j);
|
||||||
|
|
|
@ -92,7 +92,7 @@ void DrawNearbyPathfinding(int boxIndex)
|
||||||
|
|
||||||
void DropEntityPickups(ItemInfo* item)
|
void DropEntityPickups(ItemInfo* item)
|
||||||
{
|
{
|
||||||
ItemInfo* pickup = NULL;
|
ItemInfo* pickup = nullptr;
|
||||||
|
|
||||||
for (short pickupNumber = item->CarriedItem; pickupNumber != NO_ITEM; pickupNumber = pickup->CarriedItem)
|
for (short pickupNumber = item->CarriedItem; pickupNumber != NO_ITEM; pickupNumber = pickup->CarriedItem)
|
||||||
{
|
{
|
||||||
|
@ -157,7 +157,7 @@ void CreatureYRot2(PoseData* fromPose, short angle, short angleAdd)
|
||||||
|
|
||||||
bool SameZone(CreatureInfo* creature, ItemInfo* target)
|
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* item = &g_Level.Items[creature->ItemNumber];
|
||||||
|
|
||||||
auto* room = &g_Level.Rooms[item->RoomNumber];
|
auto* room = &g_Level.Rooms[item->RoomNumber];
|
||||||
|
@ -447,12 +447,13 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
||||||
short top;
|
short top;
|
||||||
|
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
if (!item->Data)
|
|
||||||
|
if (!item->IsCreature())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
auto* LOT = &creature->LOT;
|
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;
|
int boxHeight;
|
||||||
if (item->BoxNumber != NO_BOX)
|
if (item->BoxNumber != NO_BOX)
|
||||||
|
@ -460,7 +461,7 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
||||||
else
|
else
|
||||||
boxHeight = item->Floor;
|
boxHeight = item->Floor;
|
||||||
|
|
||||||
auto old = item->Pose.Position;
|
auto prevPos = item->Pose.Position;
|
||||||
|
|
||||||
AnimateItem(item);
|
AnimateItem(item);
|
||||||
|
|
||||||
|
@ -475,7 +476,7 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
||||||
int y = item->Pose.Position.y + bounds->Y1;
|
int y = item->Pose.Position.y + bounds->Y1;
|
||||||
|
|
||||||
short roomNumber = item->RoomNumber;
|
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);
|
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
|
// TODO: Check why some blocks have box = -1 assigned to them -- Lwmte, 10.11.21
|
||||||
|
@ -504,18 +505,18 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
||||||
{
|
{
|
||||||
xPos = item->Pose.Position.x / SECTOR(1);
|
xPos = item->Pose.Position.x / SECTOR(1);
|
||||||
zPos = item->Pose.Position.z / SECTOR(1);
|
zPos = item->Pose.Position.z / SECTOR(1);
|
||||||
shiftX = old.x / SECTOR(1);
|
shiftX = prevPos.x / SECTOR(1);
|
||||||
shiftZ = old.z / SECTOR(1);
|
shiftZ = prevPos.z / SECTOR(1);
|
||||||
|
|
||||||
if (xPos < shiftX)
|
if (xPos < shiftX)
|
||||||
item->Pose.Position.x = old.x & (~(SECTOR(1) - 1));
|
item->Pose.Position.x = prevPos.x & (~WALL_MASK);
|
||||||
else if (xPos > shiftX)
|
else if (xPos > shiftX)
|
||||||
item->Pose.Position.x = old.x | (SECTOR(1) - 1);
|
item->Pose.Position.x = prevPos.x | WALL_MASK;
|
||||||
|
|
||||||
if (zPos < shiftZ)
|
if (zPos < shiftZ)
|
||||||
item->Pose.Position.z = old.z & (~(SECTOR(1) - 1));
|
item->Pose.Position.z = prevPos.z & (~WALL_MASK);
|
||||||
else if (zPos > shiftZ)
|
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);
|
floor = GetFloor(item->Pose.Position.x, y, item->Pose.Position.z, &roomNumber);
|
||||||
height = g_Level.Boxes[floor->Box].height;
|
height = g_Level.Boxes[floor->Box].height;
|
||||||
|
@ -536,8 +537,8 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
||||||
|
|
||||||
int x = item->Pose.Position.x;
|
int x = item->Pose.Position.x;
|
||||||
int z = item->Pose.Position.z;
|
int z = item->Pose.Position.z;
|
||||||
xPos = x & (SECTOR(1) - 1);
|
xPos = x & WALL_MASK;
|
||||||
zPos = z & (SECTOR(1) - 1);
|
zPos = z & WALL_MASK;
|
||||||
short radius = Objects[item->ObjectNumber].radius;
|
short radius = Objects[item->ObjectNumber].radius;
|
||||||
shiftX = 0;
|
shiftX = 0;
|
||||||
shiftZ = 0;
|
shiftZ = 0;
|
||||||
|
@ -667,8 +668,8 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
||||||
{
|
{
|
||||||
if (item->Pose.Position.y + top < ceiling)
|
if (item->Pose.Position.y + top < ceiling)
|
||||||
{
|
{
|
||||||
item->Pose.Position.x = old.x;
|
item->Pose.Position.x = prevPos.x;
|
||||||
item->Pose.Position.z = old.z;
|
item->Pose.Position.z = prevPos.z;
|
||||||
dy = LOT->Fly;
|
dy = LOT->Fly;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -687,13 +688,13 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
||||||
}
|
}
|
||||||
else if (item->Pose.Position.y <= height)
|
else if (item->Pose.Position.y <= height)
|
||||||
{
|
{
|
||||||
dy = 0;
|
|
||||||
item->Pose.Position.y = height;
|
item->Pose.Position.y = height;
|
||||||
|
dy = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
item->Pose.Position.x = old.x;
|
item->Pose.Position.x = prevPos.x;
|
||||||
item->Pose.Position.z = old.z;
|
item->Pose.Position.z = prevPos.z;
|
||||||
dy = -LOT->Fly;
|
dy = -LOT->Fly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -730,11 +731,7 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
||||||
if (item->Pose.Position.y > item->Floor)
|
if (item->Pose.Position.y > item->Floor)
|
||||||
{
|
{
|
||||||
if (item->Pose.Position.y > (item->Floor + CLICK(1)))
|
if (item->Pose.Position.y > (item->Floor + CLICK(1)))
|
||||||
{
|
item->Pose.Position = prevPos;
|
||||||
item->Pose.Position.x = old.x;
|
|
||||||
item->Pose.Position.y = old.y;
|
|
||||||
item->Pose.Position.z = old.z;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
item->Pose.Position.y = item->Floor;
|
item->Pose.Position.y = item->Floor;
|
||||||
}
|
}
|
||||||
|
@ -751,11 +748,7 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
||||||
top = bounds->Y1; // TODO: check if Y1 or Y2
|
top = bounds->Y1; // TODO: check if Y1 or Y2
|
||||||
|
|
||||||
if (item->Pose.Position.y + top < ceiling)
|
if (item->Pose.Position.y + top < ceiling)
|
||||||
{
|
item->Pose.Position = prevPos;
|
||||||
item->Pose.Position.x = old.x;
|
|
||||||
item->Pose.Position.z = old.z;
|
|
||||||
item->Pose.Position.y = old.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber);
|
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);
|
item->Floor = GetFloorHeight(floor, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z);
|
||||||
|
@ -771,7 +764,6 @@ int CreatureAnimation(short itemNumber, short angle, short tilt)
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureSwitchRoom(itemNumber);
|
CreatureSwitchRoom(itemNumber);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -890,7 +882,7 @@ int ValidBox(ItemInfo* item, short zoneNumber, short boxNumber)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto* creature = GetCreatureInfo(item);
|
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)
|
if (creature->LOT.Fly == NO_FLYING && zone[boxNumber] != zoneNumber)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -976,7 +968,7 @@ int UpdateLOT(LOTInfo* LOT, int depth)
|
||||||
|
|
||||||
int SearchLOT(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];
|
int searchZone = zone[LOT->Head];
|
||||||
|
|
||||||
if (depth <= 0)
|
if (depth <= 0)
|
||||||
|
@ -1090,7 +1082,7 @@ int CreatureActive(short itemNumber)
|
||||||
if (item->Flags & IFLAG_KILLED)
|
if (item->Flags & IFLAG_KILLED)
|
||||||
return false; // Object is already dead
|
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))
|
if (!EnableEntityAI(itemNumber, 0))
|
||||||
return false; // AI couldn't be activated
|
return false; // AI couldn't be activated
|
||||||
|
@ -1168,7 +1160,7 @@ int CreatureVault(short itemNumber, short angle, int vault, int shift)
|
||||||
vault = 0;
|
vault = 0;
|
||||||
else if (item->Floor > y + CHECK_CLICK(7))
|
else if (item->Floor > y + CHECK_CLICK(7))
|
||||||
vault = -4;
|
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) &&
|
else if (item->Floor > y + CHECK_CLICK(5) &&
|
||||||
item->ObjectNumber != ID_VON_CROY &&
|
item->ObjectNumber != ID_VON_CROY &&
|
||||||
item->ObjectNumber != ID_BADDY1 &&
|
item->ObjectNumber != ID_BADDY1 &&
|
||||||
|
@ -1373,7 +1365,7 @@ void FindAITargetObject(CreatureInfo* creature, short objectNumber)
|
||||||
|
|
||||||
if (g_Level.AIObjects.size() > 0)
|
if (g_Level.AIObjects.size() > 0)
|
||||||
{
|
{
|
||||||
AI_OBJECT* foundObject = NULL;
|
AI_OBJECT* foundObject = nullptr;
|
||||||
|
|
||||||
for (int i = 0; i < g_Level.AIObjects.size(); i++)
|
for (int i = 0; i < g_Level.AIObjects.size(); i++)
|
||||||
{
|
{
|
||||||
|
@ -1381,7 +1373,7 @@ void FindAITargetObject(CreatureInfo* creature, short objectNumber)
|
||||||
|
|
||||||
if (aiObject->objectNumber == objectNumber && aiObject->triggerFlags == item->ItemFlags[3] && aiObject->roomNumber != NO_ROOM)
|
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];
|
auto* room = &g_Level.Rooms[item->RoomNumber];
|
||||||
item->BoxNumber = GetSector(room, item->Pose.Position.x - room->x, item->Pose.Position.z - room->z)->Box;
|
item->BoxNumber = GetSector(room, item->Pose.Position.x - room->x, item->Pose.Position.z - room->z)->Box;
|
||||||
|
@ -1400,7 +1392,7 @@ void FindAITargetObject(CreatureInfo* creature, short objectNumber)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundObject != NULL)
|
if (foundObject != nullptr)
|
||||||
{
|
{
|
||||||
auto* aiItem = creature->AITarget;
|
auto* aiItem = creature->AITarget;
|
||||||
|
|
||||||
|
@ -1441,7 +1433,7 @@ void CreatureAIInfo(ItemInfo* item, AI_INFO* AI)
|
||||||
creature->Enemy = LaraItem;
|
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];
|
auto* room = &g_Level.Rooms[item->RoomNumber];
|
||||||
item->BoxNumber = NO_BOX;
|
item->BoxNumber = NO_BOX;
|
||||||
|
@ -1463,8 +1455,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
|
// This prevents enemies from running to Lara and attacking nothing when she is hanging or shimmying. -- Lwmte, 27.06.22
|
||||||
|
|
||||||
bool reachable = false;
|
bool reachable = false;
|
||||||
if (object->zoneType == ZoneType::ZONE_FLYER ||
|
if (object->ZoneType == ZoneType::Flyer ||
|
||||||
(object->zoneType == ZoneType::ZONE_WATER && TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, item->RoomNumber)))
|
(object->ZoneType == ZoneType::Water && TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, item->RoomNumber)))
|
||||||
{
|
{
|
||||||
reachable = true; // If NPC is flying or swimming in water, always reach Lara
|
reachable = true; // If NPC is flying or swimming in water, always reach Lara
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,15 +112,15 @@ void DisableEntityAI(short itemNumber)
|
||||||
item->Data = nullptr;
|
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* item = &g_Level.Items[itemNumber];
|
||||||
auto* obj = &Objects[item->ObjectNumber];
|
auto* object = &Objects[item->ObjectNumber];
|
||||||
|
|
||||||
item->Data = CreatureInfo();
|
item->Data = CreatureInfo();
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
InitialiseLOTarray(itemNum);
|
|
||||||
creature->ItemNumber = itemNum;
|
InitialiseLOTarray(itemNumber);
|
||||||
|
creature->ItemNumber = itemNumber;
|
||||||
creature->Mood = MoodType::Bored;
|
creature->Mood = MoodType::Bored;
|
||||||
creature->JointRotation[0] = 0;
|
creature->JointRotation[0] = 0;
|
||||||
creature->JointRotation[1] = 0;
|
creature->JointRotation[1] = 0;
|
||||||
|
@ -136,12 +136,12 @@ void InitialiseSlot(short itemNum, short slot, bool makeTarget)
|
||||||
creature->MonkeySwingAhead = false;
|
creature->MonkeySwingAhead = false;
|
||||||
creature->LOT.CanJump = false;
|
creature->LOT.CanJump = false;
|
||||||
creature->LOT.CanMonkey = 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.IsJumping = false;
|
||||||
creature->LOT.IsMonkeying = false;
|
creature->LOT.IsMonkeying = false;
|
||||||
creature->MaxTurn = ANGLE(1);
|
creature->MaxTurn = ANGLE(1);
|
||||||
creature->Flags = 0;
|
creature->Flags = 0;
|
||||||
creature->Enemy = NULL;
|
creature->Enemy = nullptr;
|
||||||
creature->LOT.Fly = NO_FLYING;
|
creature->LOT.Fly = NO_FLYING;
|
||||||
creature->LOT.BlockMask = BLOCKED;
|
creature->LOT.BlockMask = BLOCKED;
|
||||||
|
|
||||||
|
@ -155,117 +155,116 @@ void InitialiseSlot(short itemNum, short slot, bool makeTarget)
|
||||||
else
|
else
|
||||||
creature->AITarget = nullptr;
|
creature->AITarget = nullptr;
|
||||||
|
|
||||||
switch (obj->zoneType)
|
switch (object->ZoneType)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
case ZONE_NULL:
|
case ZoneType::None:
|
||||||
creature->LOT.Step = CLICK(1);
|
creature->LOT.Step = CLICK(1);
|
||||||
creature->LOT.Drop = -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;
|
break;
|
||||||
|
|
||||||
case ZONE_SKELLY:
|
// Can jump.
|
||||||
// Can jump
|
case ZoneType::Skeleton:
|
||||||
creature->LOT.Step = CLICK(1);
|
creature->LOT.Step = CLICK(1);
|
||||||
creature->LOT.Drop = -CLICK(1);
|
creature->LOT.Drop = -CLICK(1);
|
||||||
creature->LOT.CanJump = true;
|
creature->LOT.CanJump = true;
|
||||||
creature->LOT.Zone = ZONE_SKELLY;
|
creature->LOT.Zone = ZoneType::Skeleton;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ZONE_BASIC:
|
case ZoneType::Basic:
|
||||||
creature->LOT.Step = CLICK(1);
|
creature->LOT.Step = CLICK(1);
|
||||||
creature->LOT.Drop = -CLICK(1);
|
creature->LOT.Drop = -CLICK(1);
|
||||||
creature->LOT.Zone = ZONE_BASIC;
|
creature->LOT.Zone = ZoneType::Basic;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ZONE_FLYER:
|
// Can fly.
|
||||||
// Can fly
|
case ZoneType::Flyer:
|
||||||
creature->LOT.Step = SECTOR(20);
|
creature->LOT.Step = SECTOR(20);
|
||||||
creature->LOT.Drop = -SECTOR(20);
|
creature->LOT.Drop = -SECTOR(20);
|
||||||
creature->LOT.Fly = DEFAULT_FLY_UPDOWN_SPEED;
|
creature->LOT.Fly = DEFAULT_FLY_UPDOWN_SPEED;
|
||||||
creature->LOT.Zone = ZONE_FLYER;
|
creature->LOT.Zone = ZoneType::Flyer;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ZONE_WATER:
|
// Can swim.
|
||||||
// Can swim
|
case ZoneType::Water:
|
||||||
creature->LOT.Step = SECTOR(20);
|
creature->LOT.Step = SECTOR(20);
|
||||||
creature->LOT.Drop = -SECTOR(20);
|
creature->LOT.Drop = -SECTOR(20);
|
||||||
creature->LOT.Zone = ZONE_WATER;
|
creature->LOT.Zone = ZoneType::Water;
|
||||||
|
|
||||||
if (item->ObjectNumber == ID_CROCODILE)
|
if (item->ObjectNumber == ID_CROCODILE)
|
||||||
{
|
{
|
||||||
creature->LOT.Fly = DEFAULT_SWIM_UPDOWN_SPEED / 2; // is more slow than the other underwater entity
|
creature->LOT.Fly = DEFAULT_SWIM_UPDOWN_SPEED / 2; // Slower than the other underwater creatures.
|
||||||
creature->LOT.IsAmphibious = true; // crocodile can walk and swim.
|
creature->LOT.IsAmphibious = true; // Can walk and swim.
|
||||||
}
|
}
|
||||||
else if (item->ObjectNumber == ID_BIG_RAT)
|
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.Fly = NO_FLYING; // Can't swim underwater, only on the surface.
|
||||||
creature->LOT.IsAmphibious = true; // bigrat can walk and swim.
|
creature->LOT.IsAmphibious = true; // Can walk and swim.
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
creature->LOT.Fly = DEFAULT_SWIM_UPDOWN_SPEED;
|
creature->LOT.Fly = DEFAULT_SWIM_UPDOWN_SPEED;
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ZONE_HUMAN_CLASSIC:
|
// Can climb.
|
||||||
// Can climb
|
case ZoneType::HumanClassic:
|
||||||
creature->LOT.Step = SECTOR(1);
|
creature->LOT.Step = SECTOR(1);
|
||||||
creature->LOT.Drop = -SECTOR(1);
|
creature->LOT.Drop = -SECTOR(1);
|
||||||
creature->LOT.Zone = ZONE_HUMAN_CLASSIC;
|
creature->LOT.Zone = ZoneType::HumanClassic;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ZONE_HUMAN_JUMP:
|
// Can climb and jump.
|
||||||
// Can climb and jump
|
case ZoneType::HumanJump:
|
||||||
creature->LOT.Step = SECTOR(1);
|
creature->LOT.Step = SECTOR(1);
|
||||||
creature->LOT.Drop = -SECTOR(1);
|
creature->LOT.Drop = -SECTOR(1);
|
||||||
creature->LOT.CanJump = true;
|
creature->LOT.CanJump = true;
|
||||||
creature->LOT.Zone = ZONE_HUMAN_CLASSIC;
|
creature->LOT.Zone = ZoneType::HumanClassic;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ZONE_HUMAN_JUMP_AND_MONKEY:
|
// Can climb, jump, monkeyswing.
|
||||||
// Can climb, jump, monkey
|
case ZoneType::HumanJumpAndMonkey:
|
||||||
creature->LOT.Step = SECTOR(1);
|
creature->LOT.Step = SECTOR(1);
|
||||||
creature->LOT.Drop = -SECTOR(1);
|
creature->LOT.Drop = -SECTOR(1);
|
||||||
creature->LOT.CanJump = true;
|
creature->LOT.CanJump = true;
|
||||||
creature->LOT.CanMonkey = true;
|
creature->LOT.CanMonkey = true;
|
||||||
creature->LOT.Zone = ZONE_HUMAN_CLASSIC;
|
creature->LOT.Zone = ZoneType::HumanClassic;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ZONE_HUMAN_LONGJUMP_AND_MONKEY:
|
// Can climb, jump, monkey swing, long jump.
|
||||||
// Can climb, jump, monkey, long jump
|
case ZoneType::HumanLongJumpAndMonkey:
|
||||||
creature->LOT.Step = SECTOR(1) + CLICK(3);
|
creature->LOT.Step = SECTOR(1) + CLICK(3);
|
||||||
creature->LOT.Drop = -(SECTOR(1) + CLICK(3));
|
creature->LOT.Drop = -(SECTOR(1) + CLICK(3));
|
||||||
creature->LOT.CanJump = true;
|
creature->LOT.CanJump = true;
|
||||||
creature->LOT.CanMonkey = true;
|
creature->LOT.CanMonkey = true;
|
||||||
creature->LOT.Zone = ZONE_VON_CROY;
|
creature->LOT.Zone = ZoneType::VonCroy;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ZONE_SPIDER:
|
case ZoneType::Spider:
|
||||||
creature->LOT.Step = SECTOR(1) - CLICK(2);
|
creature->LOT.Step = SECTOR(1) - CLICK(2);
|
||||||
creature->LOT.Drop = -(SECTOR(1) - CLICK(2));
|
creature->LOT.Drop = -(SECTOR(1) - CLICK(2));
|
||||||
creature->LOT.Zone = ZONE_HUMAN_CLASSIC;
|
creature->LOT.Zone = ZoneType::HumanClassic;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ZONE_BLOCKABLE:
|
case ZoneType::Blockable:
|
||||||
creature->LOT.BlockMask = BLOCKABLE;
|
creature->LOT.BlockMask = BLOCKABLE;
|
||||||
creature->LOT.Zone = ZONE_BASIC;
|
creature->LOT.Zone = ZoneType::Basic;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ZONE_APE:
|
case ZoneType::Ape:
|
||||||
creature->LOT.Step = CLICK(2);
|
creature->LOT.Step = CLICK(2);
|
||||||
creature->LOT.Drop = -SECTOR(1);
|
creature->LOT.Drop = -SECTOR(1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ZONE_SOPHIALEE:
|
case ZoneType::SophiaLee:
|
||||||
creature->LOT.Step = SECTOR(1);
|
creature->LOT.Step = SECTOR(1);
|
||||||
creature->LOT.Drop = -CLICK(3);
|
creature->LOT.Drop = -CLICK(3);
|
||||||
creature->LOT.Zone = ZONE_HUMAN_CLASSIC;
|
creature->LOT.Zone = ZoneType::HumanClassic;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearLOT(&creature->LOT);
|
ClearLOT(&creature->LOT);
|
||||||
if (itemNum != Lara.ItemNumber)
|
if (itemNumber != Lara.ItemNumber)
|
||||||
CreateZone(item);
|
CreateZone(item);
|
||||||
|
|
||||||
SlotsUsed++;
|
SlotsUsed++;
|
||||||
|
@ -304,16 +303,16 @@ void ClearLOT(LOTInfo* LOT)
|
||||||
void CreateZone(ItemInfo* item)
|
void CreateZone(ItemInfo* item)
|
||||||
{
|
{
|
||||||
auto* creature = GetCreatureInfo(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)
|
if (creature->LOT.Fly)
|
||||||
{
|
{
|
||||||
BOX_NODE* node = creature->LOT.Node.data();
|
auto* node = creature->LOT.Node.data();
|
||||||
creature->LOT.ZoneCount = 0;
|
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->boxNumber = i;
|
||||||
node++;
|
node++;
|
||||||
|
@ -322,8 +321,8 @@ void CreateZone(ItemInfo* item)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int* zone = g_Level.Zones[creature->LOT.Zone][0].data();
|
int* zone = g_Level.Zones[(int)creature->LOT.Zone][0].data();
|
||||||
int* flippedZone = g_Level.Zones[creature->LOT.Zone][1].data();
|
int* flippedZone = g_Level.Zones[(int)creature->LOT.Zone][1].data();
|
||||||
|
|
||||||
int zoneNumber = zone[item->BoxNumber];
|
int zoneNumber = zone[item->BoxNumber];
|
||||||
int flippedZoneNumber = flippedZone[item->BoxNumber];
|
int flippedZoneNumber = flippedZone[item->BoxNumber];
|
||||||
|
@ -331,7 +330,7 @@ void CreateZone(ItemInfo* item)
|
||||||
auto* node = creature->LOT.Node.data();
|
auto* node = creature->LOT.Node.data();
|
||||||
creature->LOT.ZoneCount = 0;
|
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)
|
if (*zone == zoneNumber || *flippedZone == flippedZoneNumber)
|
||||||
{
|
{
|
||||||
|
|
|
@ -56,6 +56,7 @@ int TriggerActive(ItemInfo* item)
|
||||||
flag = !flag;
|
flag = !flag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return flag;
|
return flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <vector>
|
|
||||||
#include "Math/Math.h"
|
#include "Math/Math.h"
|
||||||
|
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
struct ItemInfo;
|
struct ItemInfo;
|
||||||
|
|
||||||
struct BOX_NODE
|
struct BOX_NODE
|
||||||
|
@ -12,34 +13,37 @@ struct BOX_NODE
|
||||||
int boxNumber;
|
int boxNumber;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ZoneType : char
|
enum class ZoneType
|
||||||
{
|
{
|
||||||
ZONE_NULL = -1, // default zone
|
None = -1,
|
||||||
ZONE_SKELLY = 0,
|
Skeleton,
|
||||||
ZONE_BASIC,
|
Basic,
|
||||||
ZONE_FLYER,
|
Flyer,
|
||||||
ZONE_HUMAN_CLASSIC,
|
HumanClassic,
|
||||||
ZONE_VON_CROY,
|
VonCroy,
|
||||||
ZONE_WATER,
|
Water,
|
||||||
ZONE_MAX,
|
Max,
|
||||||
/// custom zone (using zone above for LOT.zone):
|
|
||||||
ZONE_HUMAN_JUMP_AND_MONKEY,
|
// Custom zones (above zones are used for LOT.zone):
|
||||||
ZONE_HUMAN_JUMP,
|
HumanJumpAndMonkey,
|
||||||
ZONE_SPIDER,
|
HumanJump,
|
||||||
ZONE_BLOCKABLE, // for trex, shiva, etc..
|
Spider,
|
||||||
ZONE_SOPHIALEE, // dont want sophia to go down again !
|
Blockable, // For large creatures such as trex and shiva.
|
||||||
ZONE_APE, // only 2 click climb
|
SophiaLee, // Prevents Sophia from going to lower levels again.
|
||||||
ZONE_HUMAN_LONGJUMP_AND_MONKEY,
|
Ape, // Only 0.5 block climb.
|
||||||
|
HumanLongJumpAndMonkey,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LOTInfo
|
struct LOTInfo
|
||||||
{
|
{
|
||||||
bool Initialised;
|
bool Initialised;
|
||||||
|
|
||||||
std::vector<BOX_NODE> Node;
|
vector<BOX_NODE> Node;
|
||||||
int Head;
|
int Head;
|
||||||
int Tail;
|
int Tail;
|
||||||
|
|
||||||
|
ZoneType Zone = ZoneType::None;
|
||||||
|
Vector3i Target = Vector3i::Zero;
|
||||||
int SearchNumber;
|
int SearchNumber;
|
||||||
int BlockMask;
|
int BlockMask;
|
||||||
short Step;
|
short Step;
|
||||||
|
@ -49,18 +53,16 @@ struct LOTInfo
|
||||||
int RequiredBox;
|
int RequiredBox;
|
||||||
short Fly;
|
short Fly;
|
||||||
|
|
||||||
bool CanJump;
|
bool CanJump = false;
|
||||||
bool CanMonkey;
|
bool CanMonkey = false;
|
||||||
bool IsJumping;
|
bool IsJumping = false;
|
||||||
bool IsMonkeying;
|
bool IsMonkeying = false;
|
||||||
bool IsAmphibious;
|
bool IsAmphibious = false;
|
||||||
|
|
||||||
Vector3i Target;
|
|
||||||
ZoneType Zone;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class MoodType
|
enum class MoodType
|
||||||
{
|
{
|
||||||
|
None,
|
||||||
Bored,
|
Bored,
|
||||||
Attack,
|
Attack,
|
||||||
Escape,
|
Escape,
|
||||||
|
@ -70,45 +72,43 @@ enum class MoodType
|
||||||
enum class CreatureAIPriority
|
enum class CreatureAIPriority
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
High,
|
Low,
|
||||||
Medium,
|
Medium,
|
||||||
Low
|
High
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CreatureInfo
|
struct CreatureInfo
|
||||||
{
|
{
|
||||||
short ItemNumber;
|
short ItemNumber = -1;
|
||||||
|
|
||||||
short MaxTurn;
|
LOTInfo LOT = {};
|
||||||
short JointRotation[4];
|
MoodType Mood = MoodType::None;
|
||||||
bool HeadLeft;
|
ItemInfo* Enemy = nullptr;
|
||||||
bool HeadRight;
|
ItemInfo* AITarget = nullptr;
|
||||||
|
short AITargetNumber = -1;
|
||||||
|
Vector3i Target = Vector3i::Zero;
|
||||||
|
|
||||||
bool Patrol; // Unused?
|
short MaxTurn = 0;
|
||||||
bool Alerted;
|
short JointRotation[4] = {};
|
||||||
bool Friendly;
|
bool HeadLeft = false;
|
||||||
bool HurtByLara;
|
bool HeadRight = false;
|
||||||
bool Poisoned;
|
|
||||||
bool JumpAhead;
|
|
||||||
bool MonkeySwingAhead;
|
|
||||||
bool ReachedGoal;
|
|
||||||
|
|
||||||
|
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 Tosspad;
|
||||||
short LocationAI;
|
short LocationAI;
|
||||||
short FiredWeapon;
|
short Flags = 0;
|
||||||
|
|
||||||
LOTInfo LOT;
|
|
||||||
MoodType Mood;
|
|
||||||
ItemInfo* Enemy;
|
|
||||||
short AITargetNumber;
|
|
||||||
ItemInfo* AITarget;
|
|
||||||
short Pad; // Unused?
|
|
||||||
Vector3i Target;
|
|
||||||
|
|
||||||
#ifdef CREATURE_AI_PRIORITY_OPTIMIZATION
|
#ifdef CREATURE_AI_PRIORITY_OPTIMIZATION
|
||||||
CreatureAIPriority Priority;
|
CreatureAIPriority Priority = CreatureAIPriority::None;
|
||||||
size_t FramesSinceLOTUpdate;
|
size_t FramesSinceLOTUpdate = 0;
|
||||||
#endif
|
#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...
|
template<class... Ts> visitor(Ts...)->visitor<Ts...>; // line not needed in C++20...
|
||||||
|
|
||||||
using namespace TEN::Entities::TR4;
|
using namespace TEN::Entities::TR4;
|
||||||
using namespace TEN::Entities::TR5;
|
using namespace TEN::Entities::Creatures::TR5;
|
||||||
using namespace TEN::Entities::Vehicles;
|
using namespace TEN::Entities::Vehicles;
|
||||||
|
|
||||||
struct ItemInfo;
|
struct ItemInfo;
|
||||||
|
|
|
@ -57,7 +57,7 @@ void ControlMissile(short fxNumber)
|
||||||
fx->pos.Position.x += velocity * phd_sin(fx->pos.Orientation.y);
|
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 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.
|
// Check for hitting something.
|
||||||
if (fx->pos.Position.y >= probe.Position.Floor ||
|
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* creature = GetCreatureInfo(item);
|
||||||
auto* enemy = creature->Enemy;
|
auto* enemy = creature->Enemy;
|
||||||
|
|
||||||
bool hit = false;
|
bool hasHit = false;
|
||||||
bool targetable = 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;
|
int distance = phd_sin(AI->enemyFacing) * enemy->Animation.Velocity.z * pow(MAX_VISIBILITY_DISTANCE, 2) / 300;
|
||||||
distance = pow(distance, 2) + AI->distance;
|
distance = SQUARE(distance) + AI->distance;
|
||||||
if (distance <= pow(MAX_VISIBILITY_DISTANCE, 2))
|
if (distance <= SQUARE(MAX_VISIBILITY_DISTANCE))
|
||||||
{
|
{
|
||||||
int random = (pow(MAX_VISIBILITY_DISTANCE, 2) - AI->distance) / (pow(MAX_VISIBILITY_DISTANCE, 2) / 0x5000) + 8192;
|
int random = (SQUARE(MAX_VISIBILITY_DISTANCE) - AI->distance) / (SQUARE(MAX_VISIBILITY_DISTANCE) / 0x5000) + 8192;
|
||||||
hit = GetRandomControl() < random;
|
hasHit = GetRandomControl() < random;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
hit = false;
|
hasHit = false;
|
||||||
|
|
||||||
targetable = true;
|
isTargetable = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hit = false;
|
hasHit = false;
|
||||||
targetable = false;
|
isTargetable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (damage)
|
if (damage)
|
||||||
{
|
{
|
||||||
if (enemy->IsLara())
|
if (enemy->IsLara())
|
||||||
{
|
{
|
||||||
if (hit)
|
if (hasHit)
|
||||||
{
|
{
|
||||||
DoDamage(enemy, damage);
|
DoDamage(enemy, damage);
|
||||||
CreatureEffect(item, gun, &GunHit);
|
CreatureEffect(item, gun, &GunHit);
|
||||||
}
|
}
|
||||||
else if (targetable)
|
else if (isTargetable)
|
||||||
CreatureEffect(item, gun, &GunMiss);
|
CreatureEffect(item, gun, &GunMiss);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
CreatureEffect(item, gun, &GunShot);
|
CreatureEffect(item, gun, &GunShot);
|
||||||
if (hit)
|
if (hasHit)
|
||||||
{
|
{
|
||||||
enemy->HitStatus = true;
|
enemy->HitStatus = true;
|
||||||
enemy->HitPoints += damage / -10;
|
enemy->HitPoints += damage / -10;
|
||||||
|
@ -72,7 +72,7 @@ bool ShotLara(ItemInfo* item, AI_INFO* AI, BiteInfo gun, short extraRotation, in
|
||||||
|
|
||||||
// TODO: smash objects
|
// TODO: smash objects
|
||||||
|
|
||||||
return targetable;
|
return isTargetable;
|
||||||
}
|
}
|
||||||
|
|
||||||
short GunMiss(int x, int y, int z, short velocity, short yRot, short roomNumber)
|
short GunMiss(int x, int y, int z, short velocity, short yRot, short roomNumber)
|
||||||
|
@ -141,32 +141,32 @@ bool TargetVisible(ItemInfo* item, AI_INFO* AI, float maxAngle)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check just in case.
|
// Check just in case.
|
||||||
auto* creatureInfo = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
if (creatureInfo == nullptr)
|
if (creature == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto* enemy = creatureInfo->Enemy;
|
auto* enemy = creature->Enemy;
|
||||||
if (enemy == nullptr || enemy->HitPoints == 0)
|
if (enemy == nullptr || enemy->HitPoints == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
short angle = AI->angle - creatureInfo->JointRotation[2];
|
short angle = AI->angle - creature->JointRotation[2];
|
||||||
if (angle > -ANGLE(maxAngle) && angle < ANGLE(maxAngle))
|
if (angle > ANGLE(-maxAngle) && angle < ANGLE(maxAngle))
|
||||||
{
|
{
|
||||||
GameVector start;
|
|
||||||
GameVector target;
|
|
||||||
auto& bounds = GetBestFrame(enemy)->boundingBox;
|
auto& bounds = GetBestFrame(enemy)->boundingBox;
|
||||||
|
|
||||||
start.x = item->Pose.Position.x;
|
auto origin = GameVector(
|
||||||
start.y = item->Pose.Position.y - CLICK(3);
|
item->Pose.Position.x,
|
||||||
start.z = item->Pose.Position.z;
|
item->Pose.Position.y - CLICK(3),
|
||||||
start.roomNumber = item->RoomNumber;
|
item->Pose.Position.z,
|
||||||
|
item->RoomNumber
|
||||||
target.x = enemy->Pose.Position.x;
|
);
|
||||||
target.y = enemy->Pose.Position.y + ((((bounds.Y1 * 2) + bounds.Y1) + bounds.Y2) / 4);
|
auto target = GameVector(
|
||||||
target.z = enemy->Pose.Position.z;
|
enemy->Pose.Position.x,
|
||||||
target.roomNumber = enemy->RoomNumber; // TODO: Check why this line didn't exist before. -- TokyoSU, 10/8/2022
|
enemy->Pose.Position.y + ((((bounds.Y1 * 2) + bounds.Y1) + bounds.Y2) / 4),
|
||||||
|
enemy->Pose.Position.z,
|
||||||
return LOS(&start, &target);
|
enemy->RoomNumber // TODO: Check why this line didn't exist before. -- TokyoSU, 10/8/2022
|
||||||
|
);
|
||||||
|
return LOS(&origin, &target);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -1093,11 +1093,11 @@ bool SaveGame::Save(int slot)
|
||||||
|
|
||||||
putDataInVec(Save::VarUnion::funcName, funcNameOffset);
|
putDataInVec(Save::VarUnion::funcName, funcNameOffset);
|
||||||
}
|
}
|
||||||
else if (std::holds_alternative<Vector3Int>(s))
|
else if (std::holds_alternative<Vector3i>(s))
|
||||||
{
|
{
|
||||||
Save::vec3TableBuilder vtb{ fbb };
|
Save::vec3TableBuilder vtb{ fbb };
|
||||||
Vector3Int data = std::get<Vector3Int>(s);
|
Vector3i data = std::get<Vector3i>(s);
|
||||||
Save::Vector3 saveVec = FromVector3(std::get<Vector3Int>(s));
|
Save::Vector3 saveVec = FromVector3(std::get<Vector3i>(s));
|
||||||
vtb.add_vec(&saveVec);
|
vtb.add_vec(&saveVec);
|
||||||
auto vec3Offset = vtb.Finish();
|
auto vec3Offset = vtb.Finish();
|
||||||
|
|
||||||
|
@ -1934,7 +1934,7 @@ bool SaveGame::Load(int slot)
|
||||||
}
|
}
|
||||||
else if (var->u_type() == Save::VarUnion::vec3)
|
else if (var->u_type() == Save::VarUnion::vec3)
|
||||||
{
|
{
|
||||||
loadedVars.push_back(ToVector3Int(var->u_as_vec3()->vec()));
|
loadedVars.push_back(ToVector3i(var->u_as_vec3()->vec()));
|
||||||
}
|
}
|
||||||
else if (var->u_type() == Save::VarUnion::funcName)
|
else if (var->u_type() == Save::VarUnion::funcName)
|
||||||
{
|
{
|
||||||
|
|
|
@ -260,7 +260,7 @@ namespace TEN::Entities::Effects
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ItemNearLara(&fx->pos, 200))
|
if (ItemNearLara(&fx->pos.Position, 200))
|
||||||
{
|
{
|
||||||
LaraItem->HitStatus = true;
|
LaraItem->HitStatus = true;
|
||||||
if (fx->flag1 != 6)
|
if (fx->flag1 != 6)
|
||||||
|
|
|
@ -154,7 +154,7 @@ namespace TEN::Entities::Effects
|
||||||
SoundEffect(SFX_TR4_LOOP_FOR_SMALL_FIRES, &item->Pose);
|
SoundEffect(SFX_TR4_LOOP_FOR_SMALL_FIRES, &item->Pose);
|
||||||
|
|
||||||
if (!Lara.Burn &&
|
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.x - item->Pose.Position.x, 2) +
|
||||||
pow(LaraItem->Pose.Position.z - item->Pose.Position.z, 2) < pow(SECTOR(0.5f), 2)) &&
|
pow(LaraItem->Pose.Position.z - item->Pose.Position.z, 2) < pow(SECTOR(0.5f), 2)) &&
|
||||||
Lara.Control.WaterStatus != WaterStatus::FlyCheat)
|
Lara.Control.WaterStatus != WaterStatus::FlyCheat)
|
||||||
|
@ -561,8 +561,7 @@ namespace TEN::Entities::Effects
|
||||||
|
|
||||||
TriggerDynamicLight(x, item->Pose.Position.y, z, 12, (GetRandomControl() & 0x3F) + 192, ((GetRandomControl() >> 4) & 0x1F) + 96, 0);
|
TriggerDynamicLight(x, item->Pose.Position.y, z, 12, (GetRandomControl() & 0x3F) + 192, ((GetRandomControl() >> 4) & 0x1F) + 96, 0);
|
||||||
|
|
||||||
auto pos = PoseData(item->Pose.Position);
|
auto pos = item->Pose.Position;
|
||||||
|
|
||||||
if (ItemNearLara(&pos, 600))
|
if (ItemNearLara(&pos, 600))
|
||||||
{
|
{
|
||||||
if ((!Lara.Burn) && Lara.Control.WaterStatus != WaterStatus::FlyCheat)
|
if ((!Lara.Burn) && Lara.Control.WaterStatus != WaterStatus::FlyCheat)
|
||||||
|
|
|
@ -186,7 +186,7 @@ namespace TEN::Entities::TR4
|
||||||
locust->pos.Position.y += locust->randomRotation * phd_sin(-locust->pos.Orientation.x);
|
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);
|
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);
|
TriggerBlood(locust->pos.Position.x, locust->pos.Position.y, locust->pos.Position.z, 2 * GetRandomControl(), 2);
|
||||||
DoDamage(LaraItem, LOCUST_LARA_DAMAGE);
|
DoDamage(LaraItem, LOCUST_LARA_DAMAGE);
|
||||||
|
|
|
@ -15,19 +15,23 @@
|
||||||
using namespace TEN::Math::Random;
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
constexpr auto APE_ATTACK_DAMAGE = 200;
|
constexpr auto APE_ATTACK_DAMAGE = 200;
|
||||||
|
|
||||||
constexpr auto APE_ATTACK_RANGE = SQUARE(SECTOR(0.42f));
|
constexpr auto APE_ATTACK_RANGE = SQUARE(SECTOR(0.42f));
|
||||||
constexpr auto APE_PANIC_RANGE = SQUARE(SECTOR(2));
|
constexpr auto APE_PANIC_RANGE = SQUARE(SECTOR(2));
|
||||||
|
|
||||||
constexpr auto APE_JUMP_CHANCE = 0xA0;
|
constexpr auto APE_IDLE_JUMP_CHANCE = 1.0f / 6;
|
||||||
constexpr auto APE_POUND_CHEST_CHANCE = APE_JUMP_CHANCE + 0xA0;
|
constexpr auto APE_IDLE_POUND_CHEST_CHANCE = 1.0f / 3;
|
||||||
constexpr auto APE_POUND_GROUND_CHANCE = APE_POUND_CHEST_CHANCE + 0xA0;
|
constexpr auto APE_IDLE_POUND_GROUND_CHANCE = 1.0f / 2;
|
||||||
constexpr auto APE_RUN_LEFT_CHANCE = APE_POUND_GROUND_CHANCE + 0xA0;
|
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_RUN_TURN_RATE_MAX ANGLE(5.0f)
|
||||||
#define APE_DISPLAY_ANGLE ANGLE(45.0f)
|
#define APE_DISPLAY_ANGLE ANGLE(45.0f)
|
||||||
|
@ -118,12 +122,12 @@ namespace TEN::Entities::TR1
|
||||||
|
|
||||||
if (yy < yFloor)
|
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);
|
item->Pose.Orientation.y = ANGLE(90.0f);
|
||||||
}
|
}
|
||||||
else
|
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);
|
item->Pose.Orientation.y = -ANGLE(90.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,12 +135,12 @@ namespace TEN::Entities::TR1
|
||||||
{
|
{
|
||||||
if (xx < xFloor)
|
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;
|
item->Pose.Orientation.y = 0;
|
||||||
}
|
}
|
||||||
else
|
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);
|
item->Pose.Orientation.y = -ANGLE(180.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +149,7 @@ namespace TEN::Entities::TR1
|
||||||
// diagonal
|
// diagonal
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CreatureVault(itemNumber, angle, 2, SHIFT) == 2)
|
if (CreatureVault(itemNumber, angle, 2, APE_SHIFT) == 2)
|
||||||
{
|
{
|
||||||
item->Pose.Position.y = y;
|
item->Pose.Position.y = y;
|
||||||
SetAnimation(item, APE_ANIM_VAULT);
|
SetAnimation(item, APE_ANIM_VAULT);
|
||||||
|
@ -184,8 +188,6 @@ namespace TEN::Entities::TR1
|
||||||
if (item->HitStatus || AI.distance < APE_PANIC_RANGE)
|
if (item->HitStatus || AI.distance < APE_PANIC_RANGE)
|
||||||
creatureInfo->Flags |= APE_FLAG_ATTACK;
|
creatureInfo->Flags |= APE_FLAG_ATTACK;
|
||||||
|
|
||||||
short random;
|
|
||||||
|
|
||||||
switch (item->Animation.ActiveState)
|
switch (item->Animation.ActiveState)
|
||||||
{
|
{
|
||||||
case APE_STATE_IDLE:
|
case APE_STATE_IDLE:
|
||||||
|
@ -207,14 +209,13 @@ namespace TEN::Entities::TR1
|
||||||
else if (!(creatureInfo->Flags & APE_FLAG_ATTACK) &&
|
else if (!(creatureInfo->Flags & APE_FLAG_ATTACK) &&
|
||||||
AI.zoneNumber == AI.enemyZone && AI.ahead)
|
AI.zoneNumber == AI.enemyZone && AI.ahead)
|
||||||
{
|
{
|
||||||
random = (short)(GetRandomControl() / 32);
|
if (TestProbability(APE_IDLE_JUMP_CHANCE))
|
||||||
if (random < APE_JUMP_CHANCE)
|
|
||||||
item->Animation.TargetState = APE_STATE_JUMP;
|
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;
|
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;
|
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;
|
item->Animation.TargetState = APE_STATE_RUN_LEFT;
|
||||||
creatureInfo->MaxTurn = 0;
|
creatureInfo->MaxTurn = 0;
|
||||||
|
@ -246,18 +247,17 @@ namespace TEN::Entities::TR1
|
||||||
}
|
}
|
||||||
else if (creatureInfo->Mood != MoodType::Escape)
|
else if (creatureInfo->Mood != MoodType::Escape)
|
||||||
{
|
{
|
||||||
random = (short)GetRandomControl();
|
if (TestProbability(APE_RUN_JUMP_CHANCE))
|
||||||
if (random < APE_JUMP_CHANCE)
|
|
||||||
{
|
{
|
||||||
item->Animation.RequiredState = APE_STATE_JUMP;
|
item->Animation.RequiredState = APE_STATE_JUMP;
|
||||||
item->Animation.TargetState = APE_STATE_IDLE;
|
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.RequiredState = APE_STATE_POUND_CHEST;
|
||||||
item->Animation.TargetState = APE_STATE_IDLE;
|
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.RequiredState = APE_STATE_POUND_GROUND;
|
||||||
item->Animation.TargetState = APE_STATE_IDLE;
|
item->Animation.TargetState = APE_STATE_IDLE;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
void ApeControl(short itemNumber);
|
void ApeControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,10 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.h"
|
#include "Specific/setup.h"
|
||||||
|
|
||||||
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
constexpr auto BEAR_RUN_DAMAGE = 3;
|
constexpr auto BEAR_RUN_DAMAGE = 3;
|
||||||
constexpr auto BEAR_ATTACK_DAMAGE = 200;
|
constexpr auto BEAR_ATTACK_DAMAGE = 200;
|
||||||
|
@ -24,10 +25,10 @@ namespace TEN::Entities::TR1
|
||||||
constexpr auto BEAR_REAR_RANGE = SECTOR(2);
|
constexpr auto BEAR_REAR_RANGE = SECTOR(2);
|
||||||
constexpr auto BEAR_REAR_SWIPE_ATTACK_RANGE = SECTOR(0.6f);
|
constexpr auto BEAR_REAR_SWIPE_ATTACK_RANGE = SECTOR(0.6f);
|
||||||
constexpr auto BEAR_EAT_RANGE = CLICK(3);
|
constexpr auto BEAR_EAT_RANGE = CLICK(3);
|
||||||
|
|
||||||
constexpr auto BEAR_ROAR_CHANCE = 0x50;
|
constexpr auto BEAR_ROAR_CHANCE = 1.0f / 400;
|
||||||
constexpr auto BEAR_REAR_CHANCE = 0x300;
|
constexpr auto BEAR_REAR_CHANCE = 1.0f / 40;
|
||||||
constexpr auto BEAR_DROP_CHANCE = 0x600;
|
constexpr auto BEAR_DROP_CHANCE = 1.0f / 22;
|
||||||
|
|
||||||
#define BEAR_WALK_TURN_RATE_MAX ANGLE(2.0f)
|
#define BEAR_WALK_TURN_RATE_MAX ANGLE(2.0f)
|
||||||
#define BEAR_RUN_TURN_RATE_MAX ANGLE(5.0f)
|
#define BEAR_RUN_TURN_RATE_MAX ANGLE(5.0f)
|
||||||
|
@ -81,8 +82,8 @@ namespace TEN::Entities::TR1
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
short head = 0;
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
|
short head = 0;
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
if (item->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
|
@ -117,8 +118,8 @@ namespace TEN::Entities::TR1
|
||||||
{
|
{
|
||||||
if (creature->Flags && item->TestBits(JointBitType::Touch, BearAttackJoints))
|
if (creature->Flags && item->TestBits(JointBitType::Touch, BearAttackJoints))
|
||||||
{
|
{
|
||||||
creature->Flags = 0;
|
|
||||||
DoDamage(creature->Enemy, BEAR_SLAM_DAMAGE);
|
DoDamage(creature->Enemy, BEAR_SLAM_DAMAGE);
|
||||||
|
creature->Flags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -141,12 +142,12 @@ namespace TEN::Entities::TR1
|
||||||
if (item->HitStatus)
|
if (item->HitStatus)
|
||||||
creature->Flags = 1;
|
creature->Flags = 1;
|
||||||
|
|
||||||
const bool laraDead = LaraItem->HitPoints <= 0;
|
bool isLaraDead = LaraItem->HitPoints <= 0;
|
||||||
|
|
||||||
switch (item->Animation.ActiveState)
|
switch (item->Animation.ActiveState)
|
||||||
{
|
{
|
||||||
case BEAR_STATE_IDLE:
|
case BEAR_STATE_IDLE:
|
||||||
if (laraDead)
|
if (isLaraDead)
|
||||||
{
|
{
|
||||||
if (AI.bite && AI.distance < pow(BEAR_EAT_RANGE, 2))
|
if (AI.bite && AI.distance < pow(BEAR_EAT_RANGE, 2))
|
||||||
item->Animation.TargetState = BEAR_STATE_EAT;
|
item->Animation.TargetState = BEAR_STATE_EAT;
|
||||||
|
@ -165,7 +166,7 @@ namespace TEN::Entities::TR1
|
||||||
case BEAR_STATE_STROLL:
|
case BEAR_STATE_STROLL:
|
||||||
creature->MaxTurn = BEAR_WALK_TURN_RATE_MAX;
|
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;
|
item->Animation.TargetState = BEAR_STATE_IDLE;
|
||||||
else if (creature->Mood != MoodType::Bored)
|
else if (creature->Mood != MoodType::Bored)
|
||||||
{
|
{
|
||||||
|
@ -174,10 +175,10 @@ namespace TEN::Entities::TR1
|
||||||
if (creature->Mood == MoodType::Escape)
|
if (creature->Mood == MoodType::Escape)
|
||||||
item->Animation.RequiredState = BEAR_STATE_STROLL;
|
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.TargetState = BEAR_STATE_IDLE;
|
||||||
|
item->Animation.RequiredState = BEAR_STATE_ROAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -186,16 +187,14 @@ namespace TEN::Entities::TR1
|
||||||
creature->MaxTurn = BEAR_RUN_TURN_RATE_MAX;
|
creature->MaxTurn = BEAR_RUN_TURN_RATE_MAX;
|
||||||
|
|
||||||
if (item->TestBits(JointBitType::Touch, BearAttackJoints))
|
if (item->TestBits(JointBitType::Touch, BearAttackJoints))
|
||||||
{
|
|
||||||
DoDamage(creature->Enemy, BEAR_RUN_DAMAGE);
|
DoDamage(creature->Enemy, BEAR_RUN_DAMAGE);
|
||||||
}
|
|
||||||
|
|
||||||
if (creature->Mood == MoodType::Bored || laraDead)
|
if (creature->Mood == MoodType::Bored || isLaraDead)
|
||||||
item->Animation.TargetState = BEAR_STATE_IDLE;
|
item->Animation.TargetState = BEAR_STATE_IDLE;
|
||||||
else if (AI.ahead && !item->Animation.RequiredState)
|
else if (AI.ahead && !item->Animation.RequiredState)
|
||||||
{
|
{
|
||||||
if (AI.distance < pow(BEAR_REAR_RANGE, 2) &&
|
if (AI.distance < pow(BEAR_REAR_RANGE, 2) &&
|
||||||
GetRandomControl() < BEAR_REAR_CHANCE &&
|
TestProbability(BEAR_REAR_CHANCE) &&
|
||||||
!creature->Flags)
|
!creature->Flags)
|
||||||
{
|
{
|
||||||
item->Animation.RequiredState = BEAR_STATE_REAR;
|
item->Animation.RequiredState = BEAR_STATE_REAR;
|
||||||
|
@ -227,8 +226,8 @@ namespace TEN::Entities::TR1
|
||||||
case BEAR_STATE_WALK_FORWARD:
|
case BEAR_STATE_WALK_FORWARD:
|
||||||
if (creature->Flags)
|
if (creature->Flags)
|
||||||
{
|
{
|
||||||
item->Animation.RequiredState = BEAR_STATE_STROLL;
|
|
||||||
item->Animation.TargetState = BEAR_STATE_REAR;
|
item->Animation.TargetState = BEAR_STATE_REAR;
|
||||||
|
item->Animation.RequiredState = BEAR_STATE_STROLL;
|
||||||
}
|
}
|
||||||
else if (AI.ahead && item->TestBits(JointBitType::Touch, BearAttackJoints))
|
else if (AI.ahead && item->TestBits(JointBitType::Touch, BearAttackJoints))
|
||||||
item->Animation.TargetState = BEAR_STATE_REAR;
|
item->Animation.TargetState = BEAR_STATE_REAR;
|
||||||
|
@ -237,15 +236,15 @@ namespace TEN::Entities::TR1
|
||||||
item->Animation.TargetState = BEAR_STATE_REAR;
|
item->Animation.TargetState = BEAR_STATE_REAR;
|
||||||
item->Animation.RequiredState = BEAR_STATE_STROLL;
|
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.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.TargetState = BEAR_STATE_REAR;
|
||||||
|
item->Animation.RequiredState = BEAR_STATE_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -264,8 +263,8 @@ namespace TEN::Entities::TR1
|
||||||
if (!item->Animation.RequiredState &&
|
if (!item->Animation.RequiredState &&
|
||||||
item->TestBits(JointBitType::Touch, BearAttackJoints))
|
item->TestBits(JointBitType::Touch, BearAttackJoints))
|
||||||
{
|
{
|
||||||
CreatureEffect(item, BearBite, DoBloodSplat);
|
|
||||||
DoDamage(creature->Enemy, BEAR_ATTACK_DAMAGE);
|
DoDamage(creature->Enemy, BEAR_ATTACK_DAMAGE);
|
||||||
|
CreatureEffect(item, BearBite, DoBloodSplat);
|
||||||
item->Animation.RequiredState = BEAR_STATE_IDLE;
|
item->Animation.RequiredState = BEAR_STATE_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
void BearControl(short itemNumber);
|
void BearControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
using namespace TEN::Math::Random;
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
constexpr auto BIG_RAT_BITE_ATTACK_DAMAGE = 20;
|
constexpr auto BIG_RAT_BITE_ATTACK_DAMAGE = 20;
|
||||||
constexpr auto BIG_RAT_POUNCE_ATTACK_DAMAGE = 25;
|
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_POUNCE_ATTACK_RANGE = SQUARE(SECTOR(0.5f));
|
||||||
constexpr auto BIG_RAT_WATER_BITE_ATTACK_RANGE = SQUARE(SECTOR(0.3f));
|
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_SWIM_UP_DOWN_SPEED = 32;
|
||||||
constexpr auto BIG_RAT_WATER_SURFACE_OFFSET = 10;
|
constexpr auto BIG_RAT_WATER_SURFACE_OFFSET = 10;
|
||||||
|
|
||||||
|
@ -115,10 +115,11 @@ namespace TEN::Entities::TR1
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
int waterHeight = GetRatWaterHeight(item);
|
int waterHeight = GetRatWaterHeight(item);
|
||||||
short head = 0;
|
|
||||||
short angle = 0;
|
|
||||||
bool isOnWater = waterHeight != NO_HEIGHT;
|
bool isOnWater = waterHeight != NO_HEIGHT;
|
||||||
|
|
||||||
|
short angle = 0;
|
||||||
|
short head = 0;
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
if (item->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
if (item->Animation.ActiveState != BIG_RAT_STATE_LAND_DEATH &&
|
if (item->Animation.ActiveState != BIG_RAT_STATE_LAND_DEATH &&
|
||||||
|
@ -183,9 +184,9 @@ namespace TEN::Entities::TR1
|
||||||
if (!item->Animation.RequiredState && AI.ahead &&
|
if (!item->Animation.RequiredState && AI.ahead &&
|
||||||
item->TestBits(JointBitType::Touch, BigRatBite.meshNum))
|
item->TestBits(JointBitType::Touch, BigRatBite.meshNum))
|
||||||
{
|
{
|
||||||
item->Animation.RequiredState = BIG_RAT_STATE_IDLE;
|
|
||||||
DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE);
|
DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE);
|
||||||
CreatureEffect(item, BigRatBite, DoBloodSplat);
|
CreatureEffect(item, BigRatBite, DoBloodSplat);
|
||||||
|
item->Animation.RequiredState = BIG_RAT_STATE_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -194,9 +195,9 @@ namespace TEN::Entities::TR1
|
||||||
if (!item->Animation.RequiredState && AI.ahead &&
|
if (!item->Animation.RequiredState && AI.ahead &&
|
||||||
item->TestBits(JointBitType::Touch, BigRatBite.meshNum))
|
item->TestBits(JointBitType::Touch, BigRatBite.meshNum))
|
||||||
{
|
{
|
||||||
item->Animation.RequiredState = BIG_RAT_STATE_RUN_FORWARD;
|
|
||||||
DoDamage(creature->Enemy, BIG_RAT_POUNCE_ATTACK_DAMAGE);
|
DoDamage(creature->Enemy, BIG_RAT_POUNCE_ATTACK_DAMAGE);
|
||||||
CreatureEffect(item, BigRatBite, DoBloodSplat);
|
CreatureEffect(item, BigRatBite, DoBloodSplat);
|
||||||
|
item->Animation.RequiredState = BIG_RAT_STATE_RUN_FORWARD;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
void InitialiseBigRat(short itemNumber);
|
void InitialiseBigRat(short itemNumber);
|
||||||
void BigRatControl(short itemNumber);
|
void BigRatControl(short itemNumber);
|
||||||
|
|
|
@ -17,19 +17,20 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.h"
|
#include "Specific/setup.h"
|
||||||
|
|
||||||
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
constexpr auto CENTAUR_REAR_DAMAGE = 200;
|
constexpr auto CENTAUR_REAR_DAMAGE = 200;
|
||||||
constexpr auto CENTAUR_REAR_RANGE = SECTOR(1.5f);
|
constexpr auto CENTAUR_REAR_RANGE = SECTOR(1.5f);
|
||||||
constexpr auto CENTAUR_REAR_CHANCE = 0x60;
|
constexpr auto CENTAUR_REAR_CHANCE = 1.0f / 340;
|
||||||
constexpr auto CENTAUR_BOMB_VELOCITY = 20;
|
constexpr auto CENTAUR_BOMB_VELOCITY = 20;
|
||||||
|
|
||||||
#define CENTAUR_TURN_RATE_MAX ANGLE(4.0f)
|
#define CENTAUR_TURN_RATE_MAX ANGLE(4.0f)
|
||||||
|
|
||||||
const auto CentaurRocketBite = BiteInfo(Vector3(11.0f, 415.0f, 41.0f), 13);
|
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 };
|
const vector<int> CentaurAttackJoints = { 0, 3, 4, 7, 8, 16, 17 };
|
||||||
|
|
||||||
enum CentaurState
|
enum CentaurState
|
||||||
|
@ -56,17 +57,14 @@ namespace TEN::Entities::TR1
|
||||||
|
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
short head = 0;
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
|
short head = 0;
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
if (item->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
if (item->Animation.ActiveState != CENTAUR_STATE_DEATH)
|
if (item->Animation.ActiveState != CENTAUR_STATE_DEATH)
|
||||||
{
|
SetAnimation(item, CENTAUR_ANIM_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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -98,18 +96,18 @@ namespace TEN::Entities::TR1
|
||||||
case CENTAUR_STATE_RUN_FORWARD:
|
case CENTAUR_STATE_RUN_FORWARD:
|
||||||
if (AI.bite && AI.distance < pow(CENTAUR_REAR_RANGE, 2))
|
if (AI.bite && AI.distance < pow(CENTAUR_REAR_RANGE, 2))
|
||||||
{
|
{
|
||||||
item->Animation.RequiredState = CENTAUR_STATE_WARNING;
|
|
||||||
item->Animation.TargetState = CENTAUR_STATE_IDLE;
|
item->Animation.TargetState = CENTAUR_STATE_IDLE;
|
||||||
|
item->Animation.RequiredState = CENTAUR_STATE_WARNING;
|
||||||
}
|
}
|
||||||
else if (Targetable(item, &AI))
|
else if (Targetable(item, &AI))
|
||||||
{
|
{
|
||||||
|
item->Animation.TargetState = CENTAUR_STATE_IDLE;
|
||||||
item->Animation.RequiredState = CENTAUR_STATE_AIM;
|
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.TargetState = CENTAUR_STATE_IDLE;
|
||||||
|
item->Animation.RequiredState = CENTAUR_STATE_WARNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -137,8 +135,8 @@ namespace TEN::Entities::TR1
|
||||||
if (!item->Animation.RequiredState &&
|
if (!item->Animation.RequiredState &&
|
||||||
item->TestBits(JointBitType::Touch, CentaurAttackJoints))
|
item->TestBits(JointBitType::Touch, CentaurAttackJoints))
|
||||||
{
|
{
|
||||||
CreatureEffect(item, CentaurRearBite, DoBloodSplat);
|
|
||||||
DoDamage(creature->Enemy, CENTAUR_REAR_DAMAGE);
|
DoDamage(creature->Enemy, CENTAUR_REAR_DAMAGE);
|
||||||
|
CreatureEffect(item, CentaurRearBite, DoBloodSplat);
|
||||||
item->Animation.RequiredState = CENTAUR_STATE_IDLE;
|
item->Animation.RequiredState = CENTAUR_STATE_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
constexpr auto SHARD_VELOCITY = 250;
|
constexpr auto SHARD_VELOCITY = 250;
|
||||||
constexpr auto BOMB_VELOCITY = 220;
|
constexpr auto BOMB_VELOCITY = 220;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
// - Bacon Lara cannot be targeted.
|
// - Bacon Lara cannot be targeted.
|
||||||
// - Bacon Lara cannot move like Lara.
|
// - Bacon Lara cannot move like Lara.
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
// Original:
|
// Original:
|
||||||
void InitialiseDoppelganger(short itemNumber)
|
void InitialiseDoppelganger(short itemNumber)
|
||||||
|
@ -80,26 +80,24 @@ namespace TEN::Entities::TR1
|
||||||
int laraFloorHeight = GetCollision(LaraItem).Position.Floor;
|
int laraFloorHeight = GetCollision(LaraItem).Position.Floor;
|
||||||
|
|
||||||
// Animate bacon Lara, mirroring Lara's position.
|
// Animate bacon Lara, mirroring Lara's position.
|
||||||
item->Animation.FrameNumber = LaraItem->Animation.FrameNumber;
|
|
||||||
item->Animation.AnimNumber = LaraItem->Animation.AnimNumber;
|
item->Animation.AnimNumber = LaraItem->Animation.AnimNumber;
|
||||||
item->Pose.Position.x = pos.x;
|
item->Animation.FrameNumber = LaraItem->Animation.FrameNumber;
|
||||||
item->Pose.Position.y = pos.y;
|
item->Pose.Position = pos;
|
||||||
item->Pose.Position.z = pos.z;
|
|
||||||
item->Pose.Orientation.x = LaraItem->Pose.Orientation.x;
|
item->Pose.Orientation.x = LaraItem->Pose.Orientation.x;
|
||||||
item->Pose.Orientation.y = LaraItem->Pose.Orientation.y - ANGLE(180.0f);
|
item->Pose.Orientation.y = LaraItem->Pose.Orientation.y - ANGLE(180.0f);
|
||||||
item->Pose.Orientation.z = LaraItem->Pose.Orientation.z;
|
item->Pose.Orientation.z = LaraItem->Pose.Orientation.z;
|
||||||
ItemNewRoom(itemNumber, LaraItem->RoomNumber);
|
ItemNewRoom(itemNumber, LaraItem->RoomNumber);
|
||||||
|
|
||||||
// Compare floor heights.
|
// 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)
|
!LaraItem->Animation.IsAirborne)
|
||||||
{
|
{
|
||||||
SetAnimation(item, LA_JUMP_WALL_SMASH_START);
|
SetAnimation(item, LA_JUMP_WALL_SMASH_START);
|
||||||
item->Animation.Velocity.z = 0;
|
|
||||||
item->Animation.Velocity.y = 0;
|
|
||||||
item->Animation.IsAirborne = true;
|
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->Pose.Position.y += 50;
|
||||||
|
item->Data = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,8 +112,8 @@ namespace TEN::Entities::TR1
|
||||||
item->Pose.Position.y = item->Floor;
|
item->Pose.Position.y = item->Floor;
|
||||||
TestTriggers(item, true);
|
TestTriggers(item, true);
|
||||||
|
|
||||||
item->Animation.Velocity.y = 0;
|
|
||||||
item->Animation.IsAirborne = false;
|
item->Animation.IsAirborne = false;
|
||||||
|
item->Animation.Velocity.y = 0.0f;
|
||||||
item->Animation.TargetState = LS_DEATH;
|
item->Animation.TargetState = LS_DEATH;
|
||||||
item->Animation.RequiredState = LS_DEATH;
|
item->Animation.RequiredState = LS_DEATH;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
void InitialiseDoppelganger(short itemNumber);
|
void InitialiseDoppelganger(short itemNumber);
|
||||||
void DoppelgangerControl(short itemNumber);
|
void DoppelgangerControl(short itemNumber);
|
||||||
|
|
|
@ -14,9 +14,10 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.h"
|
#include "Specific/setup.h"
|
||||||
|
|
||||||
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
constexpr auto MUTANT_ATTACK_DAMAGE = 500;
|
constexpr auto MUTANT_ATTACK_DAMAGE = 500;
|
||||||
constexpr auto MUTANT_CONTACT_DAMAGE = 6;
|
constexpr auto MUTANT_CONTACT_DAMAGE = 6;
|
||||||
|
@ -24,16 +25,17 @@ namespace TEN::Entities::TR1
|
||||||
constexpr auto MUTANT_ATTACK_RANGE = SQUARE(SECTOR(2.5f));
|
constexpr auto MUTANT_ATTACK_RANGE = SQUARE(SECTOR(2.5f));
|
||||||
constexpr auto MUTANT_CLOSE_RANGE = SQUARE(SECTOR(2.2f));
|
constexpr auto MUTANT_CLOSE_RANGE = SQUARE(SECTOR(2.2f));
|
||||||
|
|
||||||
constexpr auto MUTANT_ATTACK_1_CHANCE = 0x2AF8;
|
// TODO: Unused.
|
||||||
constexpr auto MUTANT_ATTACK_2_CHANCE = 0x55F0;
|
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_NEED_TURN ANGLE(45.0f)
|
||||||
#define MUTANT_TURN ANGLE(3.0f)
|
#define MUTANT_TURN ANGLE(3.0f)
|
||||||
|
|
||||||
#define LARA_GIANT_MUTANT_DEATH 6 // TODO: Not 13? Check this.
|
#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> 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> 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 };
|
const vector<int> MutantAttackRightJoints = { 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
|
||||||
|
|
||||||
enum GiantMutantState
|
enum GiantMutantState
|
||||||
|
@ -66,17 +68,13 @@ namespace TEN::Entities::TR1
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
short head = 0;
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
|
short head = 0;
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
if (item->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
if (item->Animation.ActiveState != MUTANT_STATE_DEATH)
|
if (item->Animation.ActiveState != MUTANT_STATE_DEATH)
|
||||||
{
|
SetAnimation(item, MUTANT_ANIM_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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -92,9 +90,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;
|
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)
|
if (item->TouchBits)
|
||||||
{
|
|
||||||
DoDamage(creature->Enemy, MUTANT_CONTACT_DAMAGE);
|
DoDamage(creature->Enemy, MUTANT_CONTACT_DAMAGE);
|
||||||
}
|
|
||||||
|
|
||||||
switch (item->Animation.ActiveState)
|
switch (item->Animation.ActiveState)
|
||||||
{
|
{
|
||||||
|
@ -122,7 +118,7 @@ namespace TEN::Entities::TR1
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = MUTANT_STATE_FORWARD;
|
item->Animation.TargetState = MUTANT_STATE_FORWARD;
|
||||||
}
|
}
|
||||||
else if (GetRandomControl() < 0x4000)
|
else if (TestProbability(0.5f))
|
||||||
item->Animation.TargetState = MUTANT_STATE_ATTACK_1;
|
item->Animation.TargetState = MUTANT_STATE_ATTACK_1;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = MUTANT_STATE_ATTACK_2;
|
item->Animation.TargetState = MUTANT_STATE_ATTACK_2;
|
||||||
|
@ -178,8 +174,8 @@ namespace TEN::Entities::TR1
|
||||||
case MUTANT_STATE_ATTACK_1:
|
case MUTANT_STATE_ATTACK_1:
|
||||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, MutantAttackRightJoints))
|
if (!creature->Flags && item->TestBits(JointBitType::Touch, MutantAttackRightJoints))
|
||||||
{
|
{
|
||||||
creature->Flags = 1;
|
|
||||||
DoDamage(creature->Enemy, MUTANT_ATTACK_DAMAGE);
|
DoDamage(creature->Enemy, MUTANT_ATTACK_DAMAGE);
|
||||||
|
creature->Flags = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -187,8 +183,8 @@ namespace TEN::Entities::TR1
|
||||||
case MUTANT_STATE_ATTACK_2:
|
case MUTANT_STATE_ATTACK_2:
|
||||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, MutantAttackJoints))
|
if (!creature->Flags && item->TestBits(JointBitType::Touch, MutantAttackJoints))
|
||||||
{
|
{
|
||||||
creature->Flags = 1;
|
|
||||||
DoDamage(creature->Enemy, MUTANT_ATTACK_DAMAGE);
|
DoDamage(creature->Enemy, MUTANT_ATTACK_DAMAGE);
|
||||||
|
creature->Flags = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -202,14 +198,11 @@ namespace TEN::Entities::TR1
|
||||||
|
|
||||||
LaraItem->Animation.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + LARA_GIANT_MUTANT_DEATH;
|
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.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase;
|
||||||
LaraItem->Animation.ActiveState = LaraItem->Animation.TargetState = 46;
|
LaraItem->Animation.ActiveState = 46;
|
||||||
LaraItem->RoomNumber = item->RoomNumber;
|
LaraItem->Animation.TargetState = 46;
|
||||||
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.IsAirborne = false;
|
LaraItem->Animation.IsAirborne = false;
|
||||||
|
LaraItem->Pose = PoseData(item->Pose.Position, 0, item->Pose.Orientation.y, 0);
|
||||||
|
LaraItem->RoomNumber = item->RoomNumber;
|
||||||
LaraItem->HitPoints = -1;
|
LaraItem->HitPoints = -1;
|
||||||
Lara.Air = -1;
|
Lara.Air = -1;
|
||||||
Lara.Control.HandStatus = HandStatus::Busy;
|
Lara.Control.HandStatus = HandStatus::Busy;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
void GiantMutantControl(short itemNumber);
|
void GiantMutantControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,13 @@
|
||||||
#include "Game/misc.h"
|
#include "Game/misc.h"
|
||||||
#include "Game/missile.h"
|
#include "Game/missile.h"
|
||||||
#include "Game/people.h"
|
#include "Game/people.h"
|
||||||
|
#include "Math/Math.h"
|
||||||
#include "Sound/sound.h"
|
#include "Sound/sound.h"
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Math/Math.h"
|
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
using namespace TEN::Math::Random;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
// TODO: Organise.
|
// TODO: Organise.
|
||||||
constexpr auto NATLA_SHOT_DAMAGE = 100;
|
constexpr auto NATLA_SHOT_DAMAGE = 100;
|
||||||
|
@ -20,9 +22,10 @@ namespace TEN::Entities::TR1
|
||||||
constexpr auto NATLA_DEATH_TIME = (FPS * 16); // 16 seconds.
|
constexpr auto NATLA_DEATH_TIME = (FPS * 16); // 16 seconds.
|
||||||
constexpr auto NATLA_FLYMODE = 0x8000;
|
constexpr auto NATLA_FLYMODE = 0x8000;
|
||||||
constexpr auto NATLA_TIMER = 0x7FFF;
|
constexpr auto NATLA_TIMER = 0x7FFF;
|
||||||
constexpr auto NATLA_LAND_CHANCE = 0x100;
|
|
||||||
constexpr auto NATLA_GUN_VELOCITY = 400;
|
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_NEAR_DEATH_SPEED ANGLE(6.0f)
|
||||||
#define NATLA_TURN_SPEED ANGLE(5.0f)
|
#define NATLA_TURN_SPEED ANGLE(5.0f)
|
||||||
#define NATLA_FLY_ANGLE_SPEED ANGLE(5.0f)
|
#define NATLA_FLY_ANGLE_SPEED ANGLE(5.0f)
|
||||||
|
@ -183,7 +186,7 @@ namespace TEN::Entities::TR1
|
||||||
|
|
||||||
if (item->Animation.ActiveState == NATLA_STATE_FLY && (creature->Flags & NATLA_FLYMODE))
|
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;
|
creature->Flags -= NATLA_FLYMODE;
|
||||||
|
|
||||||
if (!(creature->Flags & NATLA_FLYMODE))
|
if (!(creature->Flags & NATLA_FLYMODE))
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
void NatlaControl(short itemNumber);
|
void NatlaControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,14 @@
|
||||||
#include "Game/misc.h"
|
#include "Game/misc.h"
|
||||||
#include "Game/missile.h"
|
#include "Game/missile.h"
|
||||||
#include "Game/people.h"
|
#include "Game/people.h"
|
||||||
|
#include "Math/Math.h"
|
||||||
#include "Sound/sound.h"
|
#include "Sound/sound.h"
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Math/Math.h"
|
|
||||||
|
|
||||||
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
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_IDLE_JUMP_ATTACK_DAMAGE = 150;
|
||||||
constexpr auto WINGED_MUTANT_RUN_JUMP_ATTACK_DAMAGE = 100;
|
constexpr auto WINGED_MUTANT_RUN_JUMP_ATTACK_DAMAGE = 100;
|
||||||
|
@ -29,8 +30,8 @@ namespace TEN::Entities::TR1
|
||||||
constexpr auto WINGED_MUTANT_IDLE_JUMP_ATTACK_RANGE = SQUARE(SECTOR(2.5f));
|
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_ATTACK_RANGE = SQUARE(SECTOR(3.75f));
|
||||||
|
|
||||||
constexpr auto WINGED_MUTANT_POSE_CHANCE = 85;
|
constexpr auto WINGED_MUTANT_POSE_CHANCE = 1.0f / 400;
|
||||||
constexpr auto WINGED_MUTANT_UNPOSE_CHANCE = 200;
|
constexpr auto WINGED_MUTANT_UNPOSE_CHANCE = 1.0f / 164;
|
||||||
|
|
||||||
constexpr auto WINGED_MUTANT_FLY_VELOCITY = CLICK(1) / 8;
|
constexpr auto WINGED_MUTANT_FLY_VELOCITY = CLICK(1) / 8;
|
||||||
constexpr auto WINGED_MUTANT_SHARD_VELOCITY = 250;
|
constexpr auto WINGED_MUTANT_SHARD_VELOCITY = 250;
|
||||||
|
@ -337,7 +338,7 @@ namespace TEN::Entities::TR1
|
||||||
if (AI.distance < WINGED_MUTANT_WALK_RANGE)
|
if (AI.distance < WINGED_MUTANT_WALK_RANGE)
|
||||||
{
|
{
|
||||||
if (AI.zoneNumber == AI.enemyZone ||
|
if (AI.zoneNumber == AI.enemyZone ||
|
||||||
GetRandomControl() < WINGED_MUTANT_UNPOSE_CHANCE)
|
TestProbability(WINGED_MUTANT_UNPOSE_CHANCE))
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = WMUTANT_STATE_WALK_FORWARD;
|
item->Animation.TargetState = WMUTANT_STATE_WALK_FORWARD;
|
||||||
}
|
}
|
||||||
|
@ -345,7 +346,7 @@ namespace TEN::Entities::TR1
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = WMUTANT_STATE_IDLE;
|
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;
|
item->Animation.TargetState = WMUTANT_STATE_WALK_FORWARD;
|
||||||
else if (creature->Mood == MoodType::Attack ||
|
else if (creature->Mood == MoodType::Attack ||
|
||||||
creature->Mood == MoodType::Escape)
|
creature->Mood == MoodType::Escape)
|
||||||
|
@ -363,7 +364,7 @@ namespace TEN::Entities::TR1
|
||||||
else if (creature->Mood == MoodType::Bored ||
|
else if (creature->Mood == MoodType::Bored ||
|
||||||
(creature->Mood == MoodType::Stalk && AI.zoneNumber != AI.enemyZone))
|
(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;
|
item->Animation.TargetState = WMUTANT_STATE_POSE;
|
||||||
}
|
}
|
||||||
else if (creature->Mood == MoodType::Stalk &&
|
else if (creature->Mood == MoodType::Stalk &&
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
void InitialiseWingedMutant(short itemNumber);
|
void InitialiseWingedMutant(short itemNumber);
|
||||||
void WingedMutantControl(short itemNumber);
|
void WingedMutantControl(short itemNumber);
|
||||||
|
|
|
@ -11,9 +11,10 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.h"
|
#include "Specific/setup.h"
|
||||||
|
|
||||||
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
constexpr auto WOLF_BITE_DAMAGE = 100;
|
constexpr auto WOLF_BITE_DAMAGE = 100;
|
||||||
constexpr auto WOLF_LUNGE_DAMAGE = 50;
|
constexpr auto WOLF_LUNGE_DAMAGE = 50;
|
||||||
|
@ -21,9 +22,9 @@ namespace TEN::Entities::TR1
|
||||||
constexpr auto WOLF_ATTACK_RANGE = SQUARE(SECTOR(1.5f));
|
constexpr auto WOLF_ATTACK_RANGE = SQUARE(SECTOR(1.5f));
|
||||||
constexpr auto WOLF_STALK_RANGE = SQUARE(SECTOR(2));
|
constexpr auto WOLF_STALK_RANGE = SQUARE(SECTOR(2));
|
||||||
|
|
||||||
constexpr auto WOLF_WAKE_CHANCE = 0x20;
|
constexpr auto WOLF_WAKE_CHANCE = 1.0f / 1000;
|
||||||
constexpr auto WOLF_SLEEP_CHANCE = 0x20;
|
constexpr auto WOLF_SLEEP_CHANCE = 1.0f / 1000;
|
||||||
constexpr auto WOLF_HOWL_CHANCE = 0x180;
|
constexpr auto WOLF_HOWL_CHANCE = 1.0f / 85;
|
||||||
|
|
||||||
constexpr auto WOLF_SLEEP_FRAME = 96;
|
constexpr auto WOLF_SLEEP_FRAME = 96;
|
||||||
|
|
||||||
|
@ -110,7 +111,7 @@ namespace TEN::Entities::TR1
|
||||||
item->Animation.RequiredState = WOLF_STATE_CROUCH;
|
item->Animation.RequiredState = WOLF_STATE_CROUCH;
|
||||||
item->Animation.TargetState = WOLF_STATE_IDLE;
|
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.RequiredState = WOLF_STATE_WALK;
|
||||||
item->Animation.TargetState = WOLF_STATE_IDLE;
|
item->Animation.TargetState = WOLF_STATE_IDLE;
|
||||||
|
@ -133,7 +134,7 @@ namespace TEN::Entities::TR1
|
||||||
item->Animation.TargetState = WOLF_STATE_STALK;
|
item->Animation.TargetState = WOLF_STATE_STALK;
|
||||||
item->Animation.RequiredState = WOLF_STATE_NONE;
|
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.RequiredState = WOLF_STATE_SLEEP;
|
||||||
item->Animation.TargetState = WOLF_STATE_IDLE;
|
item->Animation.TargetState = WOLF_STATE_IDLE;
|
||||||
|
@ -174,7 +175,7 @@ namespace TEN::Entities::TR1
|
||||||
item->Animation.TargetState = WOLF_STATE_RUN;
|
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.RequiredState = WOLF_STATE_HOWL;
|
||||||
item->Animation.TargetState = WOLF_STATE_CROUCH;
|
item->Animation.TargetState = WOLF_STATE_CROUCH;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR1
|
namespace TEN::Entities::Creatures::TR1
|
||||||
{
|
{
|
||||||
void InitialiseWolf(short itemNumber);
|
void InitialiseWolf(short itemNumber);
|
||||||
void WolfControl(short itemNumber);
|
void WolfControl(short itemNumber);
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#include "framework.h"
|
#include "framework.h"
|
||||||
#include "Objects/TR1/tr1_objects.h"
|
#include "Objects/TR1/tr1_objects.h"
|
||||||
|
|
||||||
/// necessary import
|
|
||||||
#include "Game/control/box.h"
|
#include "Game/control/box.h"
|
||||||
#include "Game/collision/collide_item.h"
|
#include "Game/collision/collide_item.h"
|
||||||
#include "Game/itemdata/creature_info.h"
|
#include "Game/itemdata/creature_info.h"
|
||||||
|
@ -9,7 +8,7 @@
|
||||||
#include "Specific/setup.h"
|
#include "Specific/setup.h"
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
|
|
||||||
/// entities
|
// Creatures
|
||||||
#include "Objects/TR1/Entity/tr1_ape.h" // OK
|
#include "Objects/TR1/Entity/tr1_ape.h" // OK
|
||||||
#include "Objects/TR1/Entity/tr1_bear.h" // OK
|
#include "Objects/TR1/Entity/tr1_bear.h" // OK
|
||||||
#include "Objects/TR1/Entity/tr1_doppelganger.h" // OK
|
#include "Objects/TR1/Entity/tr1_doppelganger.h" // OK
|
||||||
|
@ -21,7 +20,7 @@
|
||||||
#include "Objects/TR1/Entity/tr1_winged_mutant.h"
|
#include "Objects/TR1/Entity/tr1_winged_mutant.h"
|
||||||
#include "Objects/Utils/object_helper.h"
|
#include "Objects/Utils/object_helper.h"
|
||||||
|
|
||||||
using namespace TEN::Entities::TR1;
|
using namespace TEN::Entities::Creatures::TR1;
|
||||||
|
|
||||||
static void StartEntity(ObjectInfo* obj)
|
static void StartEntity(ObjectInfo* obj)
|
||||||
{
|
{
|
||||||
|
@ -78,7 +77,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->saveHitpoints = true;
|
obj->saveHitpoints = true;
|
||||||
obj->saveAnim = true;
|
obj->saveAnim = true;
|
||||||
obj->saveFlags = true;
|
obj->saveFlags = true;
|
||||||
obj->zoneType = ZONE_APE;
|
obj->ZoneType = ZoneType::Ape;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_BIG_RAT];
|
obj = &Objects[ID_BIG_RAT];
|
||||||
|
@ -98,7 +97,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->saveAnim = true;
|
obj->saveAnim = true;
|
||||||
obj->saveFlags = true;
|
obj->saveFlags = true;
|
||||||
obj->waterCreature = true;
|
obj->waterCreature = true;
|
||||||
obj->zoneType = ZONE_WATER;
|
obj->ZoneType = ZoneType::Water;
|
||||||
obj->SetBoneRotation(1, ROT_Y); // head
|
obj->SetBoneRotation(1, ROT_Y); // head
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +173,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->saveHitpoints = true;
|
obj->saveHitpoints = true;
|
||||||
obj->saveAnim = true;
|
obj->saveAnim = true;
|
||||||
obj->saveFlags = true;
|
obj->saveFlags = true;
|
||||||
obj->zoneType = ZONE_BLOCKABLE;
|
obj->ZoneType = ZoneType::Blockable;
|
||||||
obj->SetBoneRotation(10, ROT_X | ROT_Y);
|
obj->SetBoneRotation(10, ROT_X | ROT_Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +193,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->saveFlags = true;
|
obj->saveFlags = true;
|
||||||
obj->saveHitpoints = true;
|
obj->saveHitpoints = true;
|
||||||
obj->savePosition = true;
|
obj->savePosition = true;
|
||||||
obj->zoneType = ZONE_FLYER;
|
obj->ZoneType = ZoneType::Flyer;
|
||||||
obj->SetBoneRotation(1, ROT_Y); // torso
|
obj->SetBoneRotation(1, ROT_Y); // torso
|
||||||
obj->SetBoneRotation(2, ROT_Y); // head
|
obj->SetBoneRotation(2, ROT_Y); // head
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
constexpr auto BARRACUDA_ATTACK_DAMAGE = 100;
|
constexpr auto BARRACUDA_ATTACK_DAMAGE = 100;
|
||||||
constexpr auto BARRACUDA_IDLE_ATTACK_RANGE = SQUARE(SECTOR(0.67f));
|
constexpr auto BARRACUDA_IDLE_ATTACK_RANGE = SQUARE(SECTOR(0.67f));
|
||||||
|
@ -23,7 +23,7 @@ namespace TEN::Entities::TR2
|
||||||
|
|
||||||
enum BarracudaState
|
enum BarracudaState
|
||||||
{
|
{
|
||||||
BARRACUDA_STATE_NONE = 0,
|
// No state 0.
|
||||||
BARRACUDA_STATE_IDLE = 1,
|
BARRACUDA_STATE_IDLE = 1,
|
||||||
BARRACUDA_STATE_SWIM_SLOW = 2,
|
BARRACUDA_STATE_SWIM_SLOW = 2,
|
||||||
BARRACUDA_STATE_SWIM_FAST = 3,
|
BARRACUDA_STATE_SWIM_FAST = 3,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void BarracudaControl(short itemNumber);
|
void BarracudaControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
using namespace TEN::Math::Random;
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
constexpr auto BIRD_MONSTER_ATTACK_DAMAGE = 200;
|
constexpr auto BIRD_MONSTER_ATTACK_DAMAGE = 200;
|
||||||
constexpr auto BIRD_MONSTER_SLAM_CRUSH_ATTACK_RANGE = SQUARE(SECTOR(1));
|
constexpr auto BIRD_MONSTER_SLAM_CRUSH_ATTACK_RANGE = SQUARE(SECTOR(1));
|
||||||
|
@ -29,7 +29,7 @@ namespace TEN::Entities::TR2
|
||||||
|
|
||||||
enum BirdMonsterState
|
enum BirdMonsterState
|
||||||
{
|
{
|
||||||
BMONSTER_STATE_NONE = 0,
|
// No state 0.
|
||||||
BMONSTER_STATE_IDLE = 1,
|
BMONSTER_STATE_IDLE = 1,
|
||||||
BMONSTER_STATE_WALK_FORWARD = 2,
|
BMONSTER_STATE_WALK_FORWARD = 2,
|
||||||
BMONSTER_STATE_SLAM_ATTACK_START = 3,
|
BMONSTER_STATE_SLAM_ATTACK_START = 3,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void BirdMonsterControl(short itemNumber);
|
void BirdMonsterControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,28 +18,23 @@
|
||||||
#include "Specific/setup.h"
|
#include "Specific/setup.h"
|
||||||
|
|
||||||
using namespace TEN::Input;
|
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_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.
|
// TODO: Organise.
|
||||||
#define DRAGON_SWIPE_DAMAGE 250
|
|
||||||
#define DRAGON_TOUCH_DAMAGE 10
|
|
||||||
|
|
||||||
#define DRAGON_LIVE_TIME (30 * 11)
|
#define DRAGON_LIVE_TIME (30 * 11)
|
||||||
#define DRAGON_CLOSE_RANGE pow(SECTOR(3), 2)
|
#define DRAGON_CLOSE_RANGE pow(SECTOR(3), 2)
|
||||||
#define DRAGON_STATE_IDLE_RANGE pow(SECTOR(6), 2)
|
#define DRAGON_STATE_IDLE_RANGE pow(SECTOR(6), 2)
|
||||||
#define DRAGON_FLAME_SPEED 200
|
#define DRAGON_FLAME_SPEED 200
|
||||||
|
|
||||||
#define DRAGON_TOUCH_R 0x0fe
|
|
||||||
#define DRAGON_TOUCH_L 0x7f000000
|
|
||||||
|
|
||||||
#define DRAGON_ALMOST_LIVE 100
|
#define DRAGON_ALMOST_LIVE 100
|
||||||
#define BOOM_TIME 130
|
#define BOOM_TIME 130
|
||||||
#define BOOM_TIME_MIDDLE 140
|
#define BOOM_TIME_MIDDLE 140
|
||||||
|
@ -58,7 +53,7 @@ namespace TEN::Entities::TR2
|
||||||
|
|
||||||
enum DragonState
|
enum DragonState
|
||||||
{
|
{
|
||||||
DRAGON_STATE_NONE = 0,
|
// No state 0.
|
||||||
DRAGON_STATE_WALK = 1,
|
DRAGON_STATE_WALK = 1,
|
||||||
DRAGON_STATE_LEFT = 2,
|
DRAGON_STATE_LEFT = 2,
|
||||||
DRAGON_STATE_RIGHT = 3,
|
DRAGON_STATE_RIGHT = 3,
|
||||||
|
@ -255,8 +250,8 @@ namespace TEN::Entities::TR2
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
short head = 0;
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
|
short head = 0;
|
||||||
|
|
||||||
bool ahead;
|
bool ahead;
|
||||||
|
|
||||||
|
@ -318,9 +313,7 @@ namespace TEN::Entities::TR2
|
||||||
ahead = (AI.ahead && AI.distance > DRAGON_CLOSE_RANGE && AI.distance < DRAGON_STATE_IDLE_RANGE);
|
ahead = (AI.ahead && AI.distance > DRAGON_CLOSE_RANGE && AI.distance < DRAGON_STATE_IDLE_RANGE);
|
||||||
|
|
||||||
if (item->TouchBits)
|
if (item->TouchBits)
|
||||||
{
|
DoDamage(creature->Enemy, DRAGON_CONTACT_DAMAGE);
|
||||||
DoDamage(creature->Enemy, DRAGON_TOUCH_DAMAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (item->Animation.ActiveState)
|
switch (item->Animation.ActiveState)
|
||||||
{
|
{
|
||||||
|
@ -350,19 +343,19 @@ namespace TEN::Entities::TR2
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DRAGON_STATE_SWIPE_LEFT:
|
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;
|
creature->Flags = 0;
|
||||||
DoDamage(creature->Enemy, DRAGON_SWIPE_DAMAGE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DRAGON_STATE_SWIPE_RIGHT:
|
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;
|
creature->Flags = 0;
|
||||||
DoDamage(creature->Enemy, DRAGON_SWIPE_DAMAGE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -404,13 +397,11 @@ namespace TEN::Entities::TR2
|
||||||
case DRAGON_STATE_TURN_LEFT:
|
case DRAGON_STATE_TURN_LEFT:
|
||||||
item->Pose.Orientation.y += -(ANGLE(1.0f) - angle);
|
item->Pose.Orientation.y += -(ANGLE(1.0f) - angle);
|
||||||
creature->Flags = 0;
|
creature->Flags = 0;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DRAGON_STATE_TURN_RIGHT:
|
case DRAGON_STATE_TURN_RIGHT:
|
||||||
item->Pose.Orientation.y += (ANGLE(1.0f) - angle);
|
item->Pose.Orientation.y += (ANGLE(1.0f) - angle);
|
||||||
creature->Flags = 0;
|
creature->Flags = 0;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DRAGON_STATE_AIM_1:
|
case DRAGON_STATE_AIM_1:
|
||||||
|
@ -434,12 +425,11 @@ namespace TEN::Entities::TR2
|
||||||
|
|
||||||
case DRAGON_STATE_FIRE_1:
|
case DRAGON_STATE_FIRE_1:
|
||||||
item->Pose.Orientation.y -= angle;
|
item->Pose.Orientation.y -= angle;
|
||||||
|
SoundEffect(SFX_TR2_DRAGON_FIRE, &item->Pose);
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
head = -AI.angle;
|
head = -AI.angle;
|
||||||
|
|
||||||
SoundEffect(SFX_TR2_DRAGON_FIRE, &item->Pose);
|
|
||||||
|
|
||||||
if (creature->Flags)
|
if (creature->Flags)
|
||||||
{
|
{
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
|
@ -459,12 +449,7 @@ namespace TEN::Entities::TR2
|
||||||
back->Animation.ActiveState = item->Animation.ActiveState;
|
back->Animation.ActiveState = item->Animation.ActiveState;
|
||||||
back->Animation.AnimNumber = Objects[ID_DRAGON_BACK].animIndex + (item->Animation.AnimNumber - Objects[ID_DRAGON_FRONT].animIndex);
|
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->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 = item->Pose;
|
||||||
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;
|
|
||||||
|
|
||||||
if (back->RoomNumber != item->RoomNumber)
|
if (back->RoomNumber != item->RoomNumber)
|
||||||
ItemNewRoom(backItemNumber, item->RoomNumber);
|
ItemNewRoom(backItemNumber, item->RoomNumber);
|
||||||
|
@ -484,9 +469,7 @@ namespace TEN::Entities::TR2
|
||||||
{
|
{
|
||||||
auto* back = &g_Level.Items[backItem];
|
auto* back = &g_Level.Items[backItem];
|
||||||
back->ObjectNumber = ID_DRAGON_BACK;
|
back->ObjectNumber = ID_DRAGON_BACK;
|
||||||
back->Pose.Position.x = item->Pose.Position.x;
|
back->Pose.Position = item->Pose.Position;
|
||||||
back->Pose.Position.y = item->Pose.Position.y;
|
|
||||||
back->Pose.Position.z = item->Pose.Position.z;
|
|
||||||
back->Pose.Orientation.y = item->Pose.Orientation.y;
|
back->Pose.Orientation.y = item->Pose.Orientation.y;
|
||||||
back->RoomNumber = item->RoomNumber;
|
back->RoomNumber = item->RoomNumber;
|
||||||
back->Status = ITEM_INVISIBLE;
|
back->Status = ITEM_INVISIBLE;
|
||||||
|
@ -500,9 +483,7 @@ namespace TEN::Entities::TR2
|
||||||
auto* front = &g_Level.Items[frontItem];
|
auto* front = &g_Level.Items[frontItem];
|
||||||
|
|
||||||
front->ObjectNumber = ID_DRAGON_FRONT;
|
front->ObjectNumber = ID_DRAGON_FRONT;
|
||||||
front->Pose.Position.x = item->Pose.Position.x;
|
front->Pose.Position = item->Pose.Position;
|
||||||
front->Pose.Position.y = item->Pose.Position.y;
|
|
||||||
front->Pose.Position.z = item->Pose.Position.z;
|
|
||||||
front->Pose.Orientation.y = item->Pose.Orientation.y;
|
front->Pose.Orientation.y = item->Pose.Orientation.y;
|
||||||
front->RoomNumber = item->RoomNumber;
|
front->RoomNumber = item->RoomNumber;
|
||||||
front->Status = ITEM_INVISIBLE;
|
front->Status = ITEM_INVISIBLE;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#include "Game/collision/collide_room.h"
|
#include "Game/collision/collide_room.h"
|
||||||
#include "Game/items.h"
|
#include "Game/items.h"
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void DragonCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
void DragonCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||||
void DragonControl(short backNumber);
|
void DragonControl(short backNumber);
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.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 EagleBite = BiteInfo(Vector3(15.0f, 46.0f, 21.0f), 6);
|
||||||
const auto CrowBite = BiteInfo(Vector3(2.0f, 10.0f, 60.0f), 14);
|
const auto CrowBite = BiteInfo(Vector3(2.0f, 10.0f, 60.0f), 14);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void InitialiseEagle(short itemNumber);
|
void InitialiseEagle(short itemNumber);
|
||||||
void EagleControl(short itemNumber);
|
void EagleControl(short itemNumber);
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
using namespace TEN::Math::Random;
|
using namespace TEN::Math::Random;
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
constexpr auto KNIFE_PROJECTILE_DAMAGE = 50;
|
constexpr auto KNIFE_PROJECTILE_DAMAGE = 50;
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ namespace TEN::Entities::TR2
|
||||||
|
|
||||||
enum KnifeThrowerState
|
enum KnifeThrowerState
|
||||||
{
|
{
|
||||||
KTHROWER_STATE_NONE = 0,
|
// No state 0.
|
||||||
KTHROWER_STATE_IDLE = 1,
|
KTHROWER_STATE_IDLE = 1,
|
||||||
KTHROWER_STATE_WALK_FORWARD = 2,
|
KTHROWER_STATE_WALK_FORWARD = 2,
|
||||||
KTHROWER_STATE_RUN_FORWARD = 3,
|
KTHROWER_STATE_RUN_FORWARD = 3,
|
||||||
|
@ -101,7 +101,7 @@ namespace TEN::Entities::TR2
|
||||||
|
|
||||||
fx->pos.Orientation.z += ANGLE(30.0f);
|
fx->pos.Orientation.z += ANGLE(30.0f);
|
||||||
|
|
||||||
if (ItemNearLara(&fx->pos, 200))
|
if (ItemNearLara(&fx->pos.Position, 200))
|
||||||
{
|
{
|
||||||
DoDamage(LaraItem, KNIFE_PROJECTILE_DAMAGE);
|
DoDamage(LaraItem, KNIFE_PROJECTILE_DAMAGE);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void KnifeControl(short fxNumber);
|
void KnifeControl(short fxNumber);
|
||||||
void KnifeThrowerControl(short itemNumber);
|
void KnifeThrowerControl(short itemNumber);
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#include "Specific/setup.h"
|
#include "Specific/setup.h"
|
||||||
#include "Math/Math.h"
|
#include "Math/Math.h"
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
const auto MercenaryUziBite = BiteInfo(Vector3(0.0f, 150.0f, 19.0f), 17);
|
const auto MercenaryUziBite = BiteInfo(Vector3(0.0f, 150.0f, 19.0f), 17);
|
||||||
const auto MercenaryAutoPistolBite = BiteInfo(Vector3(0.0f, 230.0f, 9.0f), 17);
|
const auto MercenaryAutoPistolBite = BiteInfo(Vector3(0.0f, 230.0f, 9.0f), 17);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void MercenaryUziControl(short itemNumber);
|
void MercenaryUziControl(short itemNumber);
|
||||||
void MercenaryAutoPistolControl(short itemNumber);
|
void MercenaryAutoPistolControl(short itemNumber);
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.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);
|
const auto MonkBite = BiteInfo(Vector3(-23.0f, 16.0f, 265.0f), 14);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void MonkControl(short itemNumber);
|
void MonkControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
using namespace TEN::Math::Random;
|
using namespace TEN::Math::Random;
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
constexpr auto RAT_ATTACK_DAMAGE = 20;
|
constexpr auto RAT_ATTACK_DAMAGE = 20;
|
||||||
constexpr auto RAT_ATTACK_RANGE = SQUARE(CLICK(0.7f));
|
constexpr auto RAT_ATTACK_RANGE = SQUARE(CLICK(0.7f));
|
||||||
|
@ -29,7 +29,7 @@ namespace TEN::Entities::TR2
|
||||||
|
|
||||||
enum RatState
|
enum RatState
|
||||||
{
|
{
|
||||||
RAT_STATE_NONE = 0,
|
// No state 0.
|
||||||
RAT_STATE_WALK_FORWARD = 1,
|
RAT_STATE_WALK_FORWARD = 1,
|
||||||
RAT_STATE_IDLE = 2,
|
RAT_STATE_IDLE = 2,
|
||||||
RAT_STATE_SQUEAK = 3,
|
RAT_STATE_SQUEAK = 3,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void RatControl(short itemNumber);
|
void RatControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,9 +11,14 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.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 auto SharkBite = BiteInfo(Vector3(17.0f, -22.0f, 344.0f), 12);
|
||||||
|
const vector<int> SharkBiteAttackJoints = { 10, 12, 13 };
|
||||||
|
|
||||||
void SharkControl(short itemNumber)
|
void SharkControl(short itemNumber)
|
||||||
{
|
{
|
||||||
|
@ -21,7 +26,7 @@ namespace TEN::Entities::TR2
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* info = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
short head = 0;
|
short head = 0;
|
||||||
|
@ -29,11 +34,7 @@ namespace TEN::Entities::TR2
|
||||||
if (item->HitPoints <= 0)
|
if (item->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
if (item->Animation.ActiveState != 5)
|
if (item->Animation.ActiveState != 5)
|
||||||
{
|
SetAnimation(item, 4);
|
||||||
item->Animation.AnimNumber = Objects[ID_SHARK].animIndex + 4;
|
|
||||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
|
||||||
item->Animation.ActiveState = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
CreatureFloat(itemNumber);
|
CreatureFloat(itemNumber);
|
||||||
return;
|
return;
|
||||||
|
@ -46,39 +47,40 @@ namespace TEN::Entities::TR2
|
||||||
GetCreatureMood(item, &AI, true);
|
GetCreatureMood(item, &AI, true);
|
||||||
CreatureMood(item, &AI, true);
|
CreatureMood(item, &AI, true);
|
||||||
|
|
||||||
angle = CreatureTurn(item, info->MaxTurn);
|
angle = CreatureTurn(item, creature->MaxTurn);
|
||||||
|
|
||||||
switch (item->Animation.ActiveState)
|
switch (item->Animation.ActiveState)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
info->Flags = 0;
|
creature->MaxTurn = 0;
|
||||||
info->MaxTurn = 0;
|
creature->Flags = 0;
|
||||||
|
|
||||||
if (AI.ahead && AI.distance < pow(SECTOR(0.75f), 2) && AI.zoneNumber == AI.enemyZone)
|
if (AI.ahead && AI.distance < pow(SECTOR(0.75f), 2) && AI.zoneNumber == AI.enemyZone)
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = 3;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = 1;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
info->MaxTurn = ANGLE(0.5f);
|
creature->MaxTurn = ANGLE(0.5f);
|
||||||
|
|
||||||
if (info->Mood == MoodType::Bored)
|
if (creature->Mood == MoodType::Bored)
|
||||||
break;
|
break;
|
||||||
else if (AI.ahead && AI.distance < pow(SECTOR(0.75f), 2))
|
else if (AI.ahead && AI.distance < pow(SECTOR(0.75f), 2))
|
||||||
item->Animation.TargetState = 0;
|
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;
|
item->Animation.TargetState = 2;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
info->MaxTurn = ANGLE(2.0f);
|
creature->MaxTurn = ANGLE(2.0f);
|
||||||
info->Flags = 0;
|
creature->Flags = 0;
|
||||||
|
|
||||||
if (info->Mood == MoodType::Bored)
|
if (creature->Mood == MoodType::Bored)
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = 1;
|
||||||
else if (info->Mood == MoodType::Escape)
|
else if (creature->Mood == MoodType::Escape)
|
||||||
break;
|
break;
|
||||||
else if (AI.ahead && AI.distance < pow(1365, 2) && AI.zoneNumber == AI.enemyZone)
|
else if (AI.ahead && AI.distance < pow(1365, 2) && AI.zoneNumber == AI.enemyZone)
|
||||||
{
|
{
|
||||||
|
@ -95,11 +97,11 @@ namespace TEN::Entities::TR2
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
head = AI.angle;
|
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);
|
CreatureEffect(item, SharkBite, DoBloodSplat);
|
||||||
DoDamage(info->Enemy, 400);
|
creature->Flags = 1;
|
||||||
info->Flags = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void SharkControl(short itemNumber);
|
void SharkControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,20 +10,67 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.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);
|
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
|
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
|
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)
|
void SilencerControl(short itemNumber)
|
||||||
|
@ -32,21 +79,19 @@ namespace TEN::Entities::TR2
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* info = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
short torsoX = 0;
|
|
||||||
short torsoY = 0;
|
|
||||||
short head = 0;
|
|
||||||
short tilt = 0;
|
short tilt = 0;
|
||||||
|
auto extraHeadRot = EulerAngles::Zero;
|
||||||
|
auto extraTorsoRot = EulerAngles::Zero;
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
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;
|
SetAnimation(item, SILENCER_ANIM_DEATH_1);
|
||||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
|
||||||
item->Animation.ActiveState = 13;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -57,205 +102,207 @@ namespace TEN::Entities::TR2
|
||||||
GetCreatureMood(item, &AI, true);
|
GetCreatureMood(item, &AI, true);
|
||||||
CreatureMood(item, &AI, true);
|
CreatureMood(item, &AI, true);
|
||||||
|
|
||||||
angle = CreatureTurn(item, info->MaxTurn);
|
angle = CreatureTurn(item, creature->MaxTurn);
|
||||||
|
|
||||||
switch (item->Animation.ActiveState)
|
switch (item->Animation.ActiveState)
|
||||||
{
|
{
|
||||||
case 3:
|
case SILENCER_STATE_IDLE_FRAME:
|
||||||
|
creature->MaxTurn = 0;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
head = AI.angle;
|
extraHeadRot.y = AI.angle;
|
||||||
info->MaxTurn = 0;
|
|
||||||
|
|
||||||
if (item->Animation.RequiredState)
|
if (item->Animation.RequiredState)
|
||||||
item->Animation.TargetState = item->Animation.RequiredState;
|
item->Animation.TargetState = item->Animation.RequiredState;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case SILENCER_STATE_IDLE:
|
||||||
if (AI.ahead)
|
creature->MaxTurn = 0;
|
||||||
head = AI.angle;
|
|
||||||
info->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 = SILENCER_STATE_IDLE_FRAME;
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.RequiredState = SILENCER_STATE_RUN_FORWARD;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Targetable(item, &AI))
|
if (Targetable(item, &AI))
|
||||||
{
|
{
|
||||||
item->Animation.RequiredState = (GetRandomControl() >= 0x4000 ? 10 : 6);
|
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||||
item->Animation.TargetState = 3;
|
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 = SILENCER_STATE_IDLE_FRAME;
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.RequiredState = SILENCER_STATE_RUN_FORWARD;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
item->Animation.RequiredState = 1;
|
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.RequiredState = SILENCER_STATE_WALK_FORWARD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (GetRandomControl() >= 1280)
|
if (TestProbability(0.96f))
|
||||||
{
|
{
|
||||||
if (GetRandomControl() < 2560)
|
if (TestProbability(0.08f))
|
||||||
{
|
{
|
||||||
item->Animation.RequiredState = 1;
|
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.RequiredState = SILENCER_STATE_WALK_FORWARD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
item->Animation.RequiredState = 5;
|
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.RequiredState = SILENCER_STATE_POSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case SILENCER_STATE_WALK_FORWARD:
|
||||||
|
creature->MaxTurn = SILENCER_WALK_TURN_RATE_MAX;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
head = AI.angle;
|
extraHeadRot.y = AI.angle;
|
||||||
|
|
||||||
info->MaxTurn = 910;
|
if (creature->Mood == MoodType::Escape)
|
||||||
|
item->Animation.TargetState = SILENCER_STATE_RUN_FORWARD;
|
||||||
if (info->Mood == MoodType::Escape)
|
|
||||||
item->Animation.TargetState = 2;
|
|
||||||
else if (Targetable(item, &AI))
|
else if (Targetable(item, &AI))
|
||||||
{
|
{
|
||||||
item->Animation.RequiredState = (GetRandomControl() >= 0x4000 ? 10 : 6);
|
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.RequiredState = TestProbability(0.5f) ? SILENCER_STATE_AIM_1 : SILENCER_STATE_AIM_2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (AI.distance > pow(SECTOR(2), 2) || !AI.ahead)
|
if (AI.distance > SILENCER_RUN_RANGE || !AI.ahead)
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = SILENCER_STATE_RUN_FORWARD;
|
||||||
if (info->Mood == MoodType::Bored && GetRandomControl() < 0x300)
|
if (creature->Mood == MoodType::Bored && TestProbability(0.025f))
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case SILENCER_STATE_RUN_FORWARD:
|
||||||
if (AI.ahead)
|
creature->MaxTurn = SILENCER_RUN_TURN_RATE_MAX;
|
||||||
head = AI.angle;
|
creature->Flags = 0;
|
||||||
|
|
||||||
info->MaxTurn = ANGLE(5.0f);
|
|
||||||
info->Flags = 0;
|
|
||||||
tilt = angle / 4;
|
tilt = angle / 4;
|
||||||
|
|
||||||
if (info->Mood == MoodType::Escape)
|
if (AI.ahead)
|
||||||
|
extraHeadRot.y = AI.angle;
|
||||||
|
|
||||||
|
if (creature->Mood == MoodType::Escape)
|
||||||
{
|
{
|
||||||
if (Targetable(item, &AI))
|
if (Targetable(item, &AI))
|
||||||
item->Animation.TargetState = 9;
|
item->Animation.TargetState = SILENCER_STATE_RUN_SHOOT;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Targetable(item, &AI))
|
if (Targetable(item, &AI))
|
||||||
{
|
{
|
||||||
if (AI.distance >= pow(SECTOR(2), 2) && AI.zoneNumber == AI.enemyZone)
|
if (AI.distance >= SILENCER_RUN_RANGE && AI.zoneNumber == AI.enemyZone)
|
||||||
item->Animation.TargetState = 9;
|
item->Animation.TargetState = SILENCER_STATE_RUN_SHOOT;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (info->Mood == MoodType::Attack)
|
else if (creature->Mood == MoodType::Attack)
|
||||||
item->Animation.TargetState = (GetRandomControl() >= 0x4000) ? 3 : 2;
|
item->Animation.TargetState = TestProbability(0.5f) ? SILENCER_STATE_RUN_FORWARD : SILENCER_STATE_IDLE_FRAME;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5:
|
case SILENCER_STATE_POSE:
|
||||||
if (AI.ahead)
|
creature->MaxTurn = 0;
|
||||||
head = AI.angle;
|
|
||||||
|
|
||||||
info->MaxTurn = 0;
|
if (AI.ahead)
|
||||||
|
extraHeadRot.y = AI.angle;
|
||||||
|
|
||||||
if (Targetable(item, &AI))
|
if (Targetable(item, &AI))
|
||||||
{
|
{
|
||||||
item->Animation.RequiredState = 6;
|
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.RequiredState = SILENCER_STATE_AIM_1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (info->Mood == MoodType::Attack || GetRandomControl() < 0x100)
|
if (creature->Mood == MoodType::Attack || TestProbability(1.0f / 128))
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||||
|
|
||||||
if (!AI.ahead)
|
if (!AI.ahead)
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6:
|
case SILENCER_STATE_AIM_1:
|
||||||
case 10:
|
case SILENCER_STATE_AIM_2:
|
||||||
info->MaxTurn = 0;
|
creature->MaxTurn = 0;
|
||||||
info->Flags = 0;
|
creature->Flags = 0;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
head = AI.angle;
|
extraHeadRot.y = AI.angle;
|
||||||
|
|
||||||
if (info->Mood == MoodType::Escape)
|
if (creature->Mood == MoodType::Escape)
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||||
else if (Targetable(item, &AI))
|
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
|
else
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 7:
|
case SILENCER_STATE_SHOOT_1:
|
||||||
case 11:
|
case SILENCER_STATE_SHOOT_2:
|
||||||
info->MaxTurn = 0;
|
creature->MaxTurn = 0;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
head = AI.angle;
|
extraHeadRot.y = AI.angle;
|
||||||
|
|
||||||
if (!info->Flags)
|
if (!creature->Flags)
|
||||||
{
|
{
|
||||||
ShotLara(item, &AI, SilencerGunBite, torsoY, 50);
|
ShotLara(item, &AI, SilencerGunBite, extraTorsoRot.y, SILENCER_SHOOT_ATTACK_DAMAGE);
|
||||||
info->Flags = 1;
|
creature->Flags = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 9:
|
case SILENCER_STATE_RUN_SHOOT:
|
||||||
info->MaxTurn = ANGLE(5.0f);
|
creature->MaxTurn = SILENCER_RUN_TURN_RATE_MAX;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
head = AI.angle;
|
extraHeadRot.y = AI.angle;
|
||||||
|
|
||||||
if (!item->Animation.RequiredState)
|
if (!item->Animation.RequiredState)
|
||||||
{
|
{
|
||||||
if (!ShotLara(item, &AI, SilencerGunBite, torsoY, 50))
|
if (!ShotLara(item, &AI, SilencerGunBite, extraTorsoRot.y, SILENCER_SHOOT_ATTACK_DAMAGE))
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = SILENCER_STATE_RUN_FORWARD;
|
||||||
|
|
||||||
item->Animation.RequiredState = 9;
|
item->Animation.RequiredState = SILENCER_STATE_RUN_SHOOT;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -263,9 +310,9 @@ namespace TEN::Entities::TR2
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureTilt(item, tilt);
|
CreatureTilt(item, tilt);
|
||||||
CreatureJoint(item, 0, torsoY);
|
CreatureJoint(item, 0, extraTorsoRot.y);
|
||||||
CreatureJoint(item, 1, torsoX);
|
CreatureJoint(item, 1, extraTorsoRot.x);
|
||||||
CreatureJoint(item, 2, head);
|
CreatureJoint(item, 2, extraHeadRot.y);
|
||||||
CreatureAnimation(itemNumber, angle, tilt);
|
CreatureAnimation(itemNumber, angle, tilt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void SilencerControl(short itemNumber);
|
void SilencerControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.h"
|
#include "Specific/setup.h"
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
#define SMAN_MIN_TURN (ANGLE(2.0f))
|
#define SMAN_MIN_TURN (ANGLE(2.0f))
|
||||||
#define SMAN_TARGET_ANGLE ANGLE(15.0f)
|
#define SMAN_TARGET_ANGLE ANGLE(15.0f)
|
||||||
|
@ -28,7 +28,7 @@ namespace TEN::Entities::TR2
|
||||||
|
|
||||||
enum SnowmobileManState
|
enum SnowmobileManState
|
||||||
{
|
{
|
||||||
SMAN_STATE_NONE = 0,
|
// No state 0.
|
||||||
SMAN_STATE_WAIT = 1,
|
SMAN_STATE_WAIT = 1,
|
||||||
SMAN_STATE_MOVING = 2,
|
SMAN_STATE_MOVING = 2,
|
||||||
SMAN_STATE_START_LEFT = 3,
|
SMAN_STATE_START_LEFT = 3,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#include "Game/collision/collide_room.h"
|
#include "Game/collision/collide_room.h"
|
||||||
#include "Game/items.h"
|
#include "Game/items.h"
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void InitialiseSkidooMan(short itemNumber);
|
void InitialiseSkidooMan(short itemNumber);
|
||||||
void SkidooManCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
void SkidooManCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll);
|
||||||
|
|
|
@ -12,7 +12,9 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.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 SpearBiteLeft = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 11);
|
||||||
const auto SpearBiteRight = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 18);
|
const auto SpearBiteRight = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 18);
|
||||||
|
@ -29,7 +31,7 @@ namespace TEN::Entities::TR2
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void XianDamage(ItemInfo* item, int damage)
|
void XianDamage(ItemInfo* item, int damage)
|
||||||
{
|
{
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
|
@ -76,7 +78,7 @@ namespace TEN::Entities::TR2
|
||||||
short neck = 0;
|
short neck = 0;
|
||||||
short tilt = 0;
|
short tilt = 0;
|
||||||
|
|
||||||
bool laraAlive = LaraItem->HitPoints > 0;
|
bool isLaraAlive = LaraItem->HitPoints > 0;
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
if (item->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
|
@ -125,10 +127,9 @@ namespace TEN::Entities::TR2
|
||||||
|
|
||||||
if (creature->Mood == MoodType::Bored)
|
if (creature->Mood == MoodType::Bored)
|
||||||
{
|
{
|
||||||
int random = GetRandomControl();
|
if (TestProbability(1.0f / 64))
|
||||||
if (random < 0x200)
|
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = 2;
|
||||||
else if (random < 0x400)
|
else if (TestProbability(1.0f / 30))
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = 3;
|
||||||
}
|
}
|
||||||
else if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
else if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||||
|
@ -148,10 +149,9 @@ namespace TEN::Entities::TR2
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = 3;
|
||||||
else if (creature->Mood == MoodType::Bored)
|
else if (creature->Mood == MoodType::Bored)
|
||||||
{
|
{
|
||||||
int random = GetRandomControl();
|
if (TestProbability(1.0f / 64))
|
||||||
if (random < 0x200)
|
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = 1;
|
||||||
else if (random < 0x400)
|
else if (TestProbability(1.0f / 30))
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = 3;
|
||||||
}
|
}
|
||||||
else if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
else if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||||
|
@ -171,17 +171,16 @@ namespace TEN::Entities::TR2
|
||||||
item->Animation.TargetState = 4;
|
item->Animation.TargetState = 4;
|
||||||
else if (creature->Mood == MoodType::Bored)
|
else if (creature->Mood == MoodType::Bored)
|
||||||
{
|
{
|
||||||
int random = GetRandomControl();
|
if (TestProbability(1.0f / 64))
|
||||||
if (random < 0x200)
|
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = 1;
|
||||||
else if (random < 0x400)
|
else if (TestProbability(1.0f / 30))
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = 2;
|
||||||
}
|
}
|
||||||
else if (AI.ahead && AI.distance < pow(SECTOR(2), 2))
|
else if (AI.ahead && AI.distance < pow(SECTOR(2), 2))
|
||||||
{
|
{
|
||||||
if (AI.distance < pow(SECTOR(1.5f), 2))
|
if (AI.distance < pow(SECTOR(1.5f), 2))
|
||||||
item->Animation.TargetState = 7;
|
item->Animation.TargetState = 7;
|
||||||
else if (GetRandomControl() < 0x4000)
|
else if (TestProbability(0.5f))
|
||||||
item->Animation.TargetState = 9;
|
item->Animation.TargetState = 9;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 11;
|
item->Animation.TargetState = 11;
|
||||||
|
@ -201,7 +200,7 @@ namespace TEN::Entities::TR2
|
||||||
break;
|
break;
|
||||||
else if (creature->Mood == MoodType::Bored)
|
else if (creature->Mood == MoodType::Bored)
|
||||||
{
|
{
|
||||||
if (GetRandomControl() < 0x4000)
|
if (TestProbability(0.5f))
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = 1;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = 2;
|
||||||
|
@ -301,7 +300,7 @@ namespace TEN::Entities::TR2
|
||||||
|
|
||||||
if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||||
{
|
{
|
||||||
if (GetRandomControl() < 0x4000)
|
if (TestProbability(0.5f))
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = 1;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = 2;
|
||||||
|
@ -332,7 +331,7 @@ namespace TEN::Entities::TR2
|
||||||
|
|
||||||
if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||||
{
|
{
|
||||||
if (GetRandomControl() < 0x4000)
|
if (TestProbability(0.5f))
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = 1;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = 2;
|
||||||
|
@ -346,7 +345,7 @@ namespace TEN::Entities::TR2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (laraAlive && LaraItem->HitPoints <= 0)
|
if (isLaraAlive && LaraItem->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
CreatureKill(item, 49, 19, 2);
|
CreatureKill(item, 49, 19, 2);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void InitialiseSpearGuardian(short itemNumber);
|
void InitialiseSpearGuardian(short itemNumber);
|
||||||
void SpearGuardianControl(short itemNumber);
|
void SpearGuardianControl(short itemNumber);
|
||||||
|
|
|
@ -14,17 +14,17 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.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);
|
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 = GetJointPosition(item, SpiderBite.meshNum, Vector3i(SpiderBite.Position));
|
auto pos = GetJointPosition(item, SpiderBite.meshNum, Vector3i(SpiderBite.Position));
|
||||||
DoBloodSplat(pos.x, pos.y, pos.z, 10, item->Pose.Position.y, item->RoomNumber);
|
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(
|
auto vec = GameVector(
|
||||||
item->Pose.Position.x,
|
item->Pose.Position.x,
|
||||||
|
@ -38,9 +38,7 @@ namespace TEN::Entities::TR2
|
||||||
if (item->Pose.Position.y > (vec.y - CLICK(1.5f)))
|
if (item->Pose.Position.y > (vec.y - CLICK(1.5f)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
item->Pose.Position.x = vec.x;
|
item->Pose.Position = Vector3i(vec.x, vec.y, vec.z);
|
||||||
item->Pose.Position.y = vec.y;
|
|
||||||
item->Pose.Position.z = vec.z;
|
|
||||||
if (item->RoomNumber != vec.roomNumber)
|
if (item->RoomNumber != vec.roomNumber)
|
||||||
ItemNewRoom(item->RoomNumber, vec.roomNumber);
|
ItemNewRoom(item->RoomNumber, vec.roomNumber);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void SmallSpiderControl(short itemNumber);
|
void SmallSpiderControl(short itemNumber);
|
||||||
void BigSpiderControl(short itemNumber);
|
void BigSpiderControl(short itemNumber);
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
#include "Sound/sound.h"
|
#include "Sound/sound.h"
|
||||||
#include "Specific/level.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);
|
const auto SwordBite = BiteInfo(Vector3(0.0f, 37.0f, 550.0f), 15);
|
||||||
|
|
||||||
|
@ -24,7 +26,7 @@ namespace TEN::Entities::TR2
|
||||||
ClearItem(itemNumber);
|
ClearItem(itemNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SwordGuardianFly(ItemInfo* item)
|
void SwordGuardianFly(ItemInfo* item)
|
||||||
{
|
{
|
||||||
Vector3i pos;
|
Vector3i pos;
|
||||||
pos.x = (GetRandomControl() * 256 / 32768) + item->Pose.Position.x - 128;
|
pos.x = (GetRandomControl() * 256 / 32768) + item->Pose.Position.x - 128;
|
||||||
|
@ -47,7 +49,7 @@ namespace TEN::Entities::TR2
|
||||||
short head = 0;
|
short head = 0;
|
||||||
short torso = 0;
|
short torso = 0;
|
||||||
|
|
||||||
bool laraAlive = LaraItem->HitPoints > 0;
|
bool isLaraAlive = LaraItem->HitPoints > 0;
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
if (item->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
|
@ -68,7 +70,7 @@ namespace TEN::Entities::TR2
|
||||||
creature->LOT.Step = STEP_SIZE;
|
creature->LOT.Step = STEP_SIZE;
|
||||||
creature->LOT.Drop = -STEP_SIZE;
|
creature->LOT.Drop = -STEP_SIZE;
|
||||||
creature->LOT.Fly = NO_FLYING;
|
creature->LOT.Fly = NO_FLYING;
|
||||||
creature->LOT.Zone = ZONE_BASIC;
|
creature->LOT.Zone = ZoneType::Basic;
|
||||||
|
|
||||||
AI_INFO AI;
|
AI_INFO AI;
|
||||||
CreatureAIInfo(item, &AI);
|
CreatureAIInfo(item, &AI);
|
||||||
|
@ -80,7 +82,7 @@ namespace TEN::Entities::TR2
|
||||||
creature->LOT.Step = WALL_SIZE * 20;
|
creature->LOT.Step = WALL_SIZE * 20;
|
||||||
creature->LOT.Drop = -WALL_SIZE * 20;
|
creature->LOT.Drop = -WALL_SIZE * 20;
|
||||||
creature->LOT.Fly = STEP_SIZE / 4;
|
creature->LOT.Fly = STEP_SIZE / 4;
|
||||||
creature->LOT.Zone = ZONE_FLYER;
|
creature->LOT.Zone = ZoneType::Flyer;
|
||||||
CreatureAIInfo(item, &AI);
|
CreatureAIInfo(item, &AI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,15 +116,10 @@ namespace TEN::Entities::TR2
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
head = AI.angle;
|
head = AI.angle;
|
||||||
|
|
||||||
if (laraAlive)
|
if (isLaraAlive)
|
||||||
{
|
{
|
||||||
if (AI.bite && AI.distance < pow(SECTOR(1), 2))
|
if (AI.bite && AI.distance < pow(SECTOR(1), 2))
|
||||||
{
|
item->Animation.TargetState = TestProbability(0.5f) ? 3 : 5;
|
||||||
if (GetRandomControl() >= 0x4000)
|
|
||||||
item->Animation.TargetState = 5;
|
|
||||||
else
|
|
||||||
item->Animation.TargetState = 3;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (AI.zoneNumber == AI.enemyZone)
|
if (AI.zoneNumber == AI.enemyZone)
|
||||||
|
@ -142,7 +139,7 @@ namespace TEN::Entities::TR2
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
head = AI.angle;
|
head = AI.angle;
|
||||||
|
|
||||||
if (laraAlive)
|
if (isLaraAlive)
|
||||||
{
|
{
|
||||||
if (AI.bite && AI.distance < pow(SECTOR(2), 2))
|
if (AI.bite && AI.distance < pow(SECTOR(2), 2))
|
||||||
item->Animation.TargetState = 10;
|
item->Animation.TargetState = 10;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void InitialiseSwordGuardian(short itemNumber);
|
void InitialiseSwordGuardian(short itemNumber);
|
||||||
void SwordGuardianControl(short itemNumber);
|
void SwordGuardianControl(short itemNumber);
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.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 WorkerDualGunBiteLeft = BiteInfo(Vector3(-2.0f, 275.0f, 23.0f), 6);
|
||||||
const auto WorkerDualGunBiteRight = BiteInfo(Vector3(2.0f, 275.0f, 23.0f), 10);
|
const auto WorkerDualGunBiteRight = BiteInfo(Vector3(2.0f, 275.0f, 23.0f), 10);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void WorkerDualGunControl(short itemNumber);
|
void WorkerDualGunControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Math/Math.h"
|
#include "Math/Math.h"
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
const auto WorkerFlamethrowerOffset = Vector3i(0, 140, 0);
|
const auto WorkerFlamethrowerOffset = Vector3i(0, 140, 0);
|
||||||
const auto WorkerFlamethrowerBite = BiteInfo(Vector3(0.0f, 250.0f, 32.0f), 9);
|
const auto WorkerFlamethrowerBite = BiteInfo(Vector3(0.0f, 250.0f, 32.0f), 9);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void InitialiseWorkerFlamethrower(short itemNumber);
|
void InitialiseWorkerFlamethrower(short itemNumber);
|
||||||
void WorkerFlamethrower(short itemNumber);
|
void WorkerFlamethrower(short itemNumber);
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.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);
|
const auto WorkerMachineGunBite = BiteInfo(Vector3(0.0f, 308.0f, 32.0f), 9);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void InitialiseWorkerMachineGun(short itemNumber);
|
void InitialiseWorkerMachineGun(short itemNumber);
|
||||||
void WorkerMachineGunControl(short itemNumber);
|
void WorkerMachineGunControl(short itemNumber);
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.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);
|
const auto WorkerShotgunBite = BiteInfo(Vector3(0.0f, 281.0f, 40.0f), 9);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void InitialiseWorkerShotgun(short itemNumber);
|
void InitialiseWorkerShotgun(short itemNumber);
|
||||||
void WorkerShotgunControl(short itemNumber);
|
void WorkerShotgunControl(short itemNumber);
|
||||||
|
|
|
@ -13,11 +13,14 @@
|
||||||
#include "Specific/setup.h"
|
#include "Specific/setup.h"
|
||||||
|
|
||||||
using namespace TEN::Math::Random;
|
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 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
|
// TODO
|
||||||
enum YetiState
|
enum YetiState
|
||||||
|
@ -50,9 +53,9 @@ namespace TEN::Entities::TR2
|
||||||
bool isLaraAlive = LaraItem->HitPoints > 0;
|
bool isLaraAlive = LaraItem->HitPoints > 0;
|
||||||
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
|
short tilt = 0;
|
||||||
short torso = 0;
|
short torso = 0;
|
||||||
short head = 0;
|
short head = 0;
|
||||||
short tilt = 0;
|
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
if (item->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
|
@ -88,9 +91,9 @@ namespace TEN::Entities::TR2
|
||||||
item->Animation.TargetState = item->Animation.RequiredState;
|
item->Animation.TargetState = item->Animation.RequiredState;
|
||||||
else if (info->Mood == MoodType::Bored)
|
else if (info->Mood == MoodType::Bored)
|
||||||
{
|
{
|
||||||
if (TestProbability(0.008f) || !isLaraAlive)
|
if (TestProbability(1.0f / 128) || !isLaraAlive)
|
||||||
item->Animation.TargetState = 7;
|
item->Animation.TargetState = 7;
|
||||||
else if (TestProbability(0.015f))
|
else if (TestProbability(1.0f / 64))
|
||||||
item->Animation.TargetState = 9;
|
item->Animation.TargetState = 9;
|
||||||
else if (TestProbability(0.025f))
|
else if (TestProbability(0.025f))
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = 3;
|
||||||
|
@ -116,9 +119,9 @@ namespace TEN::Entities::TR2
|
||||||
{
|
{
|
||||||
if (isLaraAlive)
|
if (isLaraAlive)
|
||||||
{
|
{
|
||||||
if (TestProbability(0.008f))
|
if (TestProbability(1.0f / 128))
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = 2;
|
||||||
else if (TestProbability(0.015f))
|
else if (TestProbability(1.0f / 64))
|
||||||
item->Animation.TargetState = 9;
|
item->Animation.TargetState = 9;
|
||||||
else if (TestProbability(0.025f))
|
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;
|
item->Animation.TargetState = 2;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -140,9 +143,9 @@ namespace TEN::Entities::TR2
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = 2;
|
||||||
else if (info->Mood == MoodType::Bored)
|
else if (info->Mood == MoodType::Bored)
|
||||||
{
|
{
|
||||||
if (TestProbability(0.008f) || !isLaraAlive)
|
if (TestProbability(1.0f / 128) || !isLaraAlive)
|
||||||
item->Animation.TargetState = 7;
|
item->Animation.TargetState = 7;
|
||||||
else if (TestProbability(0.015f))
|
else if (TestProbability(1.0f / 64))
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = 2;
|
||||||
else if (TestProbability(0.025f))
|
else if (TestProbability(0.025f))
|
||||||
{
|
{
|
||||||
|
@ -150,7 +153,7 @@ namespace TEN::Entities::TR2
|
||||||
item->Animation.RequiredState = 3;
|
item->Animation.RequiredState = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (TestProbability(0.015f))
|
else if (TestProbability(1.0f / 64))
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = 2;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -165,12 +168,12 @@ namespace TEN::Entities::TR2
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = 1;
|
||||||
else if (info->Mood == MoodType::Bored)
|
else if (info->Mood == MoodType::Bored)
|
||||||
{
|
{
|
||||||
if (TestProbability(0.008f) || !isLaraAlive)
|
if (TestProbability(1.0f / 128) || !isLaraAlive)
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = 2;
|
||||||
item->Animation.RequiredState = 7;
|
item->Animation.RequiredState = 7;
|
||||||
}
|
}
|
||||||
else if (TestProbability(0.015f))
|
else if (TestProbability(1.0f / 64))
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = 2;
|
||||||
item->Animation.RequiredState = 9;
|
item->Animation.RequiredState = 9;
|
||||||
|
@ -213,8 +216,7 @@ namespace TEN::Entities::TR2
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
torso = AI.angle;
|
torso = AI.angle;
|
||||||
|
|
||||||
if (!info->Flags &&
|
if (!info->Flags && item->TestBits(JointBitType::Touch, YetiAttackJoints1))
|
||||||
item->TouchBits & 0x1400)
|
|
||||||
{
|
{
|
||||||
CreatureEffect(item, YetiBiteRight, DoBloodSplat);
|
CreatureEffect(item, YetiBiteRight, DoBloodSplat);
|
||||||
DoDamage(info->Enemy, 100);
|
DoDamage(info->Enemy, 100);
|
||||||
|
@ -230,11 +232,12 @@ namespace TEN::Entities::TR2
|
||||||
torso = AI.angle;
|
torso = AI.angle;
|
||||||
|
|
||||||
if (!info->Flags &&
|
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);
|
CreatureEffect(item, YetiBiteLeft, DoBloodSplat);
|
||||||
if (item->TouchBits & 0x1400)
|
|
||||||
|
if (item->TestBits(JointBitType::Touch, YetiAttackJoints1))
|
||||||
CreatureEffect(item, YetiBiteRight, DoBloodSplat);
|
CreatureEffect(item, YetiBiteRight, DoBloodSplat);
|
||||||
|
|
||||||
DoDamage(info->Enemy, 150);
|
DoDamage(info->Enemy, 150);
|
||||||
|
@ -248,11 +251,12 @@ namespace TEN::Entities::TR2
|
||||||
torso = AI.angle;
|
torso = AI.angle;
|
||||||
|
|
||||||
if (!info->Flags &&
|
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);
|
CreatureEffect(item, YetiBiteLeft, DoBloodSplat);
|
||||||
if (item->TouchBits & 0x1400)
|
|
||||||
|
if (item->TestBits(JointBitType::Touch, YetiAttackJoints1))
|
||||||
CreatureEffect(item, YetiBiteRight, DoBloodSplat);
|
CreatureEffect(item, YetiBiteRight, DoBloodSplat);
|
||||||
|
|
||||||
DoDamage(info->Enemy, 200);
|
DoDamage(info->Enemy, 200);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR2
|
namespace TEN::Entities::Creatures::TR2
|
||||||
{
|
{
|
||||||
void InitialiseYeti(short itemNumber);
|
void InitialiseYeti(short itemNumber);
|
||||||
void YetiControl(short itemNumber);
|
void YetiControl(short itemNumber);
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
#include "Objects/TR2/Vehicles/speedboat.h"
|
#include "Objects/TR2/Vehicles/speedboat.h"
|
||||||
#include "Objects/TR2/Vehicles/skidoo.h"
|
#include "Objects/TR2/Vehicles/skidoo.h"
|
||||||
|
|
||||||
using namespace TEN::Entities::TR2;
|
using namespace TEN::Entities::Creatures::TR2;
|
||||||
|
|
||||||
static void StartEntity(ObjectInfo* obj)
|
static void StartEntity(ObjectInfo* obj)
|
||||||
{
|
{
|
||||||
|
@ -57,7 +57,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->saveHitpoints = true;
|
obj->saveHitpoints = true;
|
||||||
obj->saveAnim = true;
|
obj->saveAnim = true;
|
||||||
obj->saveFlags = true;
|
obj->saveFlags = true;
|
||||||
obj->zoneType = ZONE_WATER;
|
obj->ZoneType = ZoneType::Water;
|
||||||
|
|
||||||
g_Level.Bones[obj->boneIndex + 9 * 4] |= ROT_Y;
|
g_Level.Bones[obj->boneIndex + 9 * 4] |= ROT_Y;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->saveHitpoints = true;
|
obj->saveHitpoints = true;
|
||||||
obj->saveAnim = true;
|
obj->saveAnim = true;
|
||||||
obj->saveFlags = true;
|
obj->saveFlags = true;
|
||||||
obj->zoneType = ZONE_WATER;
|
obj->ZoneType = ZoneType::Water;
|
||||||
|
|
||||||
g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y;
|
g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y;
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->saveAnim = true;
|
obj->saveAnim = true;
|
||||||
obj->saveFlags = true;
|
obj->saveFlags = true;
|
||||||
obj->pivotLength = 0;
|
obj->pivotLength = 0;
|
||||||
obj->zoneType = ZONE_FLYER;
|
obj->ZoneType = ZoneType::Flyer;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_CROW];
|
obj = &Objects[ID_CROW];
|
||||||
|
@ -118,7 +118,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->saveAnim = true;
|
obj->saveAnim = true;
|
||||||
obj->saveFlags = true;
|
obj->saveFlags = true;
|
||||||
obj->pivotLength = 0;
|
obj->pivotLength = 0;
|
||||||
obj->zoneType = ZONE_FLYER;
|
obj->ZoneType = ZoneType::Flyer;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = &Objects[ID_RAT];
|
obj = &Objects[ID_RAT];
|
||||||
|
@ -154,7 +154,7 @@ static void StartEntity(ObjectInfo* obj)
|
||||||
obj->saveFlags = true;
|
obj->saveFlags = true;
|
||||||
obj->saveHitpoints = true;
|
obj->saveHitpoints = true;
|
||||||
obj->savePosition = 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 + 6 * 4] |= (ROT_Y);
|
||||||
g_Level.Bones[obj->boneIndex + 14 * 4] |= (ROT_Y);
|
g_Level.Bones[obj->boneIndex + 14 * 4] |= (ROT_Y);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
using namespace TEN::Math::Random;
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
constexpr auto CIVVY_ATTACK_DAMAGE = 40;
|
constexpr auto CIVVY_ATTACK_DAMAGE = 40;
|
||||||
constexpr auto CIVVY_SWIPE_DAMAGE = 50;
|
constexpr auto CIVVY_SWIPE_DAMAGE = 50;
|
||||||
|
@ -42,7 +42,6 @@ namespace TEN::Entities::TR3
|
||||||
// TODO
|
// TODO
|
||||||
enum CivvyState
|
enum CivvyState
|
||||||
{
|
{
|
||||||
CIVVY_STATE_NONE,
|
|
||||||
CIVVY_STATE_IDLE,
|
CIVVY_STATE_IDLE,
|
||||||
CIVVY_STATE_WALK_FORWARD,
|
CIVVY_STATE_WALK_FORWARD,
|
||||||
CIVVY_PUNCH2,
|
CIVVY_PUNCH2,
|
||||||
|
@ -89,11 +88,10 @@ namespace TEN::Entities::TR3
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
short torsoX = 0;
|
|
||||||
short torsoY = 0;
|
|
||||||
short head = 0;
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
short tilt = 0;
|
short tilt = 0;
|
||||||
|
auto extraHeadRot = EulerAngles::Zero;
|
||||||
|
auto extraTorsoRot = EulerAngles::Zero;
|
||||||
|
|
||||||
if (item->BoxNumber != NO_BOX && (g_Level.Boxes[item->BoxNumber].flags & BLOCKED))
|
if (item->BoxNumber != NO_BOX && (g_Level.Boxes[item->BoxNumber].flags & BLOCKED))
|
||||||
{
|
{
|
||||||
|
@ -119,18 +117,18 @@ namespace TEN::Entities::TR3
|
||||||
AI_INFO AI;
|
AI_INFO AI;
|
||||||
CreatureAIInfo(item, &AI);
|
CreatureAIInfo(item, &AI);
|
||||||
|
|
||||||
AI_INFO laraAiInfo;
|
AI_INFO laraAI;
|
||||||
if (creature->Enemy == LaraItem)
|
if (creature->Enemy == LaraItem)
|
||||||
{
|
{
|
||||||
laraAiInfo.angle = AI.angle;
|
laraAI.angle = AI.angle;
|
||||||
laraAiInfo.distance = AI.distance;
|
laraAI.distance = AI.distance;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int laraDz = LaraItem->Pose.Position.z - item->Pose.Position.z;
|
int laraDz = LaraItem->Pose.Position.z - item->Pose.Position.z;
|
||||||
int laraDx = LaraItem->Pose.Position.x - item->Pose.Position.x;
|
int laraDx = LaraItem->Pose.Position.x - item->Pose.Position.x;
|
||||||
laraAiInfo.angle = phd_atan(laraDz, laraDx) - item->Pose.Orientation.y;
|
laraAI.angle = phd_atan(laraDz, laraDx) - item->Pose.Orientation.y;
|
||||||
laraAiInfo.distance = pow(laraDx, 2) + pow(laraDz, 2);
|
laraAI.distance = pow(laraDx, 2) + pow(laraDz, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
GetCreatureMood(item, &AI, true);
|
GetCreatureMood(item, &AI, true);
|
||||||
|
@ -150,7 +148,7 @@ namespace TEN::Entities::TR3
|
||||||
auto* realEnemy = creature->Enemy;
|
auto* realEnemy = creature->Enemy;
|
||||||
creature->Enemy = LaraItem;
|
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))
|
!(item->AIBits & FOLLOW))
|
||||||
{
|
{
|
||||||
if (!creature->Alerted)
|
if (!creature->Alerted)
|
||||||
|
@ -170,13 +168,13 @@ namespace TEN::Entities::TR3
|
||||||
}
|
}
|
||||||
|
|
||||||
case CIVVY_STATE_IDLE:
|
case CIVVY_STATE_IDLE:
|
||||||
head = laraAiInfo.angle;
|
|
||||||
creature->MaxTurn = 0;
|
creature->MaxTurn = 0;
|
||||||
creature->Flags = 0;
|
creature->Flags = 0;
|
||||||
|
extraHeadRot.y = laraAI.angle;
|
||||||
|
|
||||||
if (item->AIBits & GUARD)
|
if (item->AIBits & GUARD)
|
||||||
{
|
{
|
||||||
head = AIGuard(creature);
|
extraHeadRot.y = AIGuard(creature);
|
||||||
if (!(GetRandomControl() & 0xFF))
|
if (!(GetRandomControl() & 0xFF))
|
||||||
{
|
{
|
||||||
if (item->Animation.ActiveState == CIVVY_STATE_IDLE)
|
if (item->Animation.ActiveState == CIVVY_STATE_IDLE)
|
||||||
|
@ -199,7 +197,7 @@ namespace TEN::Entities::TR3
|
||||||
item->Animation.TargetState = CIVVY_STATE_RUN_FORWARD;
|
item->Animation.TargetState = CIVVY_STATE_RUN_FORWARD;
|
||||||
}
|
}
|
||||||
else if (creature->Mood == MoodType::Bored ||
|
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)
|
if (item->Animation.RequiredState)
|
||||||
item->Animation.TargetState = item->Animation.RequiredState;
|
item->Animation.TargetState = item->Animation.RequiredState;
|
||||||
|
@ -221,11 +219,11 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
case CIVVY_STATE_WALK_FORWARD:
|
case CIVVY_STATE_WALK_FORWARD:
|
||||||
creature->MaxTurn = CIVVY_WALK_TURN_RATE_MAX;
|
creature->MaxTurn = CIVVY_WALK_TURN_RATE_MAX;
|
||||||
head = laraAiInfo.angle;
|
extraHeadRot.y = laraAI.angle;
|
||||||
|
|
||||||
if (item->AIBits & PATROL1)
|
if (item->AIBits & PATROL1)
|
||||||
{
|
{
|
||||||
head = 0;
|
extraHeadRot.y = 0;
|
||||||
item->Animation.TargetState = CIVVY_STATE_WALK_FORWARD;
|
item->Animation.TargetState = CIVVY_STATE_WALK_FORWARD;
|
||||||
}
|
}
|
||||||
else if (creature->Mood == MoodType::Escape)
|
else if (creature->Mood == MoodType::Escape)
|
||||||
|
@ -252,7 +250,7 @@ namespace TEN::Entities::TR3
|
||||||
tilt = angle / 2;
|
tilt = angle / 2;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
head = AI.angle;
|
extraHeadRot.y = AI.angle;
|
||||||
|
|
||||||
if (item->AIBits & GUARD)
|
if (item->AIBits & GUARD)
|
||||||
item->Animation.TargetState = CIVVY_WAIT;
|
item->Animation.TargetState = CIVVY_WAIT;
|
||||||
|
@ -262,7 +260,7 @@ namespace TEN::Entities::TR3
|
||||||
item->Animation.TargetState = CIVVY_STATE_IDLE;
|
item->Animation.TargetState = CIVVY_STATE_IDLE;
|
||||||
break;
|
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;
|
item->Animation.TargetState = CIVVY_STATE_IDLE;
|
||||||
else if (creature->Mood == MoodType::Bored)
|
else if (creature->Mood == MoodType::Bored)
|
||||||
item->Animation.TargetState = CIVVY_STATE_WALK_FORWARD;
|
item->Animation.TargetState = CIVVY_STATE_WALK_FORWARD;
|
||||||
|
@ -273,11 +271,12 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
case CIVVY_AIM0:
|
case CIVVY_AIM0:
|
||||||
creature->MaxTurn = CIVVY_WALK_TURN_RATE_MAX;
|
creature->MaxTurn = CIVVY_WALK_TURN_RATE_MAX;
|
||||||
|
creature->Flags = 0;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AI.bite && AI.distance < CIVVY_ATTACK0_RANGE)
|
if (AI.bite && AI.distance < CIVVY_ATTACK0_RANGE)
|
||||||
|
@ -285,16 +284,16 @@ namespace TEN::Entities::TR3
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = CIVVY_STATE_IDLE;
|
item->Animation.TargetState = CIVVY_STATE_IDLE;
|
||||||
|
|
||||||
creature->Flags = 0;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CIVVY_AIM1:
|
case CIVVY_AIM1:
|
||||||
creature->MaxTurn = CIVVY_WALK_TURN_RATE_MAX;
|
creature->MaxTurn = CIVVY_WALK_TURN_RATE_MAX;
|
||||||
|
creature->Flags = 0;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AI.ahead && AI.distance < CIVVY_ATTACK1_RANGE)
|
if (AI.ahead && AI.distance < CIVVY_ATTACK1_RANGE)
|
||||||
|
@ -302,7 +301,6 @@ namespace TEN::Entities::TR3
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = CIVVY_STATE_IDLE;
|
item->Animation.TargetState = CIVVY_STATE_IDLE;
|
||||||
|
|
||||||
creature->Flags = 0;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CIVVY_AIM2:
|
case CIVVY_AIM2:
|
||||||
|
@ -311,8 +309,8 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AI.bite && AI.distance < CIVVY_ATTACK2_RANGE)
|
if (AI.bite && AI.distance < CIVVY_ATTACK2_RANGE)
|
||||||
|
@ -327,8 +325,8 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, CivvyAttackJoints))
|
if (!creature->Flags && item->TestBits(JointBitType::Touch, CivvyAttackJoints))
|
||||||
|
@ -346,8 +344,8 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, CivvyAttackJoints))
|
if (!creature->Flags && item->TestBits(JointBitType::Touch, CivvyAttackJoints))
|
||||||
|
@ -368,8 +366,8 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (creature->Flags != 2 && item->TestBits(JointBitType::Touch, CivvyAttackJoints))
|
if (creature->Flags != 2 && item->TestBits(JointBitType::Touch, CivvyAttackJoints))
|
||||||
|
@ -385,9 +383,9 @@ namespace TEN::Entities::TR3
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureTilt(item, tilt);
|
CreatureTilt(item, tilt);
|
||||||
CreatureJoint(item, 0, torsoY);
|
CreatureJoint(item, 0, extraTorsoRot.y);
|
||||||
CreatureJoint(item, 1, torsoX);
|
CreatureJoint(item, 1, extraTorsoRot.x);
|
||||||
CreatureJoint(item, 2, head);
|
CreatureJoint(item, 2, extraHeadRot.y);
|
||||||
|
|
||||||
if (item->Animation.ActiveState < CIVVY_DEATH)
|
if (item->Animation.ActiveState < CIVVY_DEATH)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
void InitialiseCivvy(short itemNumber);
|
void InitialiseCivvy(short itemNumber);
|
||||||
void CivvyControl(short itemNumber);
|
void CivvyControl(short itemNumber);
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
constexpr auto COBRA_BITE_ATTACK_DAMAGE = 80;
|
constexpr auto COBRA_BITE_ATTACK_DAMAGE = 80;
|
||||||
constexpr auto COBRA_BITE_POISON_POTENCY = 8;
|
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_AWARE_RANGE = SQUARE(SECTOR(1.5f));
|
||||||
constexpr auto COBRA_SLEEP_RANGE = SQUARE(SECTOR(2.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;
|
constexpr auto COBRA_SLEEP_FRAME = 45;
|
||||||
|
|
||||||
const auto CobraBite = BiteInfo(Vector3::Zero, 13);
|
const auto CobraBite = BiteInfo(Vector3::Zero, 13);
|
||||||
|
@ -42,7 +41,7 @@ namespace TEN::Entities::TR3
|
||||||
enum CobraAnim
|
enum CobraAnim
|
||||||
{
|
{
|
||||||
COBRA_ANIM_IDLE = 0,
|
COBRA_ANIM_IDLE = 0,
|
||||||
COBRA_ANIM_WAKE_UP = 1,
|
COBRA_ANIM_SLEEP_TO_IDLE = 1,
|
||||||
COBRA_ANIM_IDLE_TO_SLEEP = 2,
|
COBRA_ANIM_IDLE_TO_SLEEP = 2,
|
||||||
COBRA_ANIM_BITE_ATTACK = 3,
|
COBRA_ANIM_BITE_ATTACK = 3,
|
||||||
COBRA_ANIM_DEATH = 4
|
COBRA_ANIM_DEATH = 4
|
||||||
|
@ -65,9 +64,9 @@ namespace TEN::Entities::TR3
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
short head = 0;
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
short tilt = 0;
|
short tilt = 0;
|
||||||
|
short head = 0;
|
||||||
|
|
||||||
if (item->HitPoints <= 0 && item->HitPoints != NOT_TARGETABLE)
|
if (item->HitPoints <= 0 && item->HitPoints != NOT_TARGETABLE)
|
||||||
{
|
{
|
||||||
|
@ -84,19 +83,23 @@ namespace TEN::Entities::TR3
|
||||||
GetCreatureMood(item, &AI, 1);
|
GetCreatureMood(item, &AI, 1);
|
||||||
CreatureMood(item, &AI, 1);
|
CreatureMood(item, &AI, 1);
|
||||||
|
|
||||||
bool enemyMoving = false;
|
bool isEnemyMoving = false;
|
||||||
bool enemyVisible = false;
|
bool isEnemyVisible = 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);
|
|
||||||
|
|
||||||
enemyMoving = creature->Enemy->Animation.Velocity.z > PLAYER_DISTURB_VELOCITY ||
|
if (creature->Enemy != nullptr && (GlobalCounter & 2))
|
||||||
abs(creature->Enemy->Animation.Velocity.y) > PLAYER_DISTURB_VELOCITY;
|
{
|
||||||
|
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.x = creature->Enemy->Pose.Position.x;
|
||||||
creature->Target.z = creature->Enemy->Pose.Position.z;
|
creature->Target.z = creature->Enemy->Pose.Position.z;
|
||||||
|
@ -119,12 +122,10 @@ namespace TEN::Entities::TR3
|
||||||
creature->Flags = 0;
|
creature->Flags = 0;
|
||||||
|
|
||||||
if (AI.distance > COBRA_SLEEP_RANGE)
|
if (AI.distance > COBRA_SLEEP_RANGE)
|
||||||
{
|
|
||||||
item->Animation.TargetState = COBRA_STATE_SLEEP;
|
item->Animation.TargetState = COBRA_STATE_SLEEP;
|
||||||
}
|
else if (creature->Enemy->HitPoints > 0 && isEnemyVisible &&
|
||||||
else if (creature->Enemy->HitPoints > 0 && enemyVisible &&
|
((AI.ahead && AI.distance < COBRA_ATTACK_RANGE && AI.verticalDistance <= GetBoundsAccurate(item)->Height()) ||
|
||||||
((AI.ahead && AI.distance < COBRA_ATTACK_RANGE && AI.verticalDistance <= GetBoundsAccurate(item)->Height()) ||
|
item->HitStatus || isEnemyMoving))
|
||||||
item->HitStatus || enemyMoving))
|
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = COBRA_STATE_ATTACK;
|
item->Animation.TargetState = COBRA_STATE_ATTACK;
|
||||||
}
|
}
|
||||||
|
@ -149,12 +150,12 @@ namespace TEN::Entities::TR3
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COBRA_STATE_ATTACK:
|
case COBRA_STATE_ATTACK:
|
||||||
if (creature->Flags != 1 &&
|
if (!(creature->Flags & 1) && // 1 = is attacking.
|
||||||
item->TestBits(JointBitType::Touch, CobraAttackJoints))
|
item->TestBits(JointBitType::Touch, CobraAttackJoints))
|
||||||
{
|
{
|
||||||
DoDamage(creature->Enemy, COBRA_BITE_ATTACK_DAMAGE);
|
DoDamage(creature->Enemy, COBRA_BITE_ATTACK_DAMAGE);
|
||||||
CreatureEffect(item, CobraBite, DoBloodSplat);
|
CreatureEffect(item, CobraBite, DoBloodSplat);
|
||||||
creature->Flags = 1;
|
creature->Flags |= 1; // 1 = is attacking.
|
||||||
|
|
||||||
if (creature->Enemy->IsLara())
|
if (creature->Enemy->IsLara())
|
||||||
GetLaraInfo(creature->Enemy)->PoisonPotency += COBRA_BITE_POISON_POTENCY;
|
GetLaraInfo(creature->Enemy)->PoisonPotency += COBRA_BITE_POISON_POTENCY;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
void InitialiseCobra(short itemNum);
|
void InitialiseCobra(short itemNum);
|
||||||
void CobraControl(short itemNum);
|
void CobraControl(short itemNum);
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "Objects/TR3/fish.h"
|
#include "Objects/TR3/fish.h"
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
int PirahnaHitWait = false;
|
int PirahnaHitWait = false;
|
||||||
int CarcassItem = NO_ITEM;
|
int CarcassItem = NO_ITEM;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Game/items.h"
|
#include "Game/items.h"
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
void SetupShoal(int shoalNumber);
|
void SetupShoal(int shoalNumber);
|
||||||
void ControlFish(short itemNumber);
|
void ControlFish(short itemNumber);
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.h"
|
#include "Specific/setup.h"
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
using namespace TEN::Math::Random;
|
||||||
|
|
||||||
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
const auto FlamethrowerOffset = Vector3i(0, 340, 0);
|
const auto FlamethrowerOffset = Vector3i(0, 340, 0);
|
||||||
const auto FlamethrowerBite = BiteInfo(Vector3(0.0f, 340.0f, 64.0f), 7);
|
const auto FlamethrowerBite = BiteInfo(Vector3(0.0f, 340.0f, 64.0f), 7);
|
||||||
|
@ -41,33 +43,26 @@ namespace TEN::Entities::TR3
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
short torsoX = 0;
|
|
||||||
short torsoY = 0;
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
short tilt = 0;
|
short tilt = 0;
|
||||||
short head = 0;
|
auto extraHeadRot = EulerAngles::Zero;
|
||||||
|
auto extraTorsoRot = EulerAngles::Zero;
|
||||||
|
|
||||||
auto pos = GetJointPosition(item, FlamethrowerBite.meshNum, Vector3i(FlamethrowerBite.Position));
|
auto pos = GetJointPosition(item, FlamethrowerBite.meshNum, Vector3i(FlamethrowerBite.Position));
|
||||||
|
|
||||||
int random = GetRandomControl();
|
int randomInt = GetRandomControl();
|
||||||
if (item->Animation.ActiveState != 6 && item->Animation.ActiveState != 11)
|
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);
|
TriggerPilotFlame(itemNumber, 9);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
TriggerDynamicLight(pos.x, pos.y, pos.z, (randomInt & 3) + 10, 31 - ((randomInt / 16) & 3), 24 - ((randomInt / 64) & 3), randomInt & 7);
|
||||||
TriggerDynamicLight(pos.x, pos.y, pos.z, (random & 3) + 10, 31 - ((random / 16) & 3), 24 - ((random / 64) & 3), random & 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
if (item->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
if (item->Animation.ActiveState != 7)
|
if (item->Animation.ActiveState != 7)
|
||||||
{
|
SetAnimation(item, 19);
|
||||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 19;
|
|
||||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
|
||||||
item->Animation.ActiveState = 7;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -82,9 +77,8 @@ namespace TEN::Entities::TR3
|
||||||
ItemInfo* target = nullptr;
|
ItemInfo* target = nullptr;
|
||||||
int minDistance = INT_MAX;
|
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)
|
if (currentCreature->ItemNumber == NO_ITEM || currentCreature->ItemNumber == itemNumber)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -147,13 +141,13 @@ namespace TEN::Entities::TR3
|
||||||
case 1:
|
case 1:
|
||||||
creature->MaxTurn = 0;
|
creature->MaxTurn = 0;
|
||||||
creature->Flags = 0;
|
creature->Flags = 0;
|
||||||
head = laraAI.angle;
|
extraHeadRot.y = laraAI.angle;
|
||||||
|
|
||||||
if (item->AIBits & GUARD)
|
if (item->AIBits & GUARD)
|
||||||
{
|
{
|
||||||
head = AIGuard(creature);
|
extraHeadRot.y = AIGuard(creature);
|
||||||
|
|
||||||
if (!(GetRandomControl() & 0xFF))
|
if (TestProbability(1.0f / 128))
|
||||||
item->Animation.TargetState = 4;
|
item->Animation.TargetState = 4;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -169,21 +163,21 @@ namespace TEN::Entities::TR3
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 2;
|
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;
|
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;
|
item->Animation.TargetState = 2;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
head = laraAI.angle;
|
extraHeadRot.y = laraAI.angle;
|
||||||
|
|
||||||
if (item->AIBits & GUARD)
|
if (item->AIBits & GUARD)
|
||||||
{
|
{
|
||||||
head = AIGuard(creature);
|
extraHeadRot.y = AIGuard(creature);
|
||||||
|
|
||||||
if (!(GetRandomControl() & 0xFF))
|
if (TestProbability(1.0f / 128))
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = 1;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -192,7 +186,7 @@ namespace TEN::Entities::TR3
|
||||||
AI.distance < pow(SECTOR(4), 2) &&
|
AI.distance < pow(SECTOR(4), 2) &&
|
||||||
(realEnemy != LaraItem || creature->HurtByLara) ||
|
(realEnemy != LaraItem || creature->HurtByLara) ||
|
||||||
creature->Mood != MoodType::Bored ||
|
creature->Mood != MoodType::Bored ||
|
||||||
!(GetRandomControl() & 0xFF)))
|
TestProbability(1.0f / 128)))
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = 1;
|
||||||
}
|
}
|
||||||
|
@ -200,17 +194,12 @@ namespace TEN::Entities::TR3
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
creature->Flags = 0;
|
|
||||||
creature->MaxTurn = ANGLE(5.0f);
|
creature->MaxTurn = ANGLE(5.0f);
|
||||||
head = laraAI.angle;
|
creature->Flags = 0;
|
||||||
|
extraHeadRot.y = laraAI.angle;
|
||||||
|
|
||||||
if (item->AIBits & GUARD)
|
if (item->AIBits & GUARD)
|
||||||
{
|
SetAnimation(item, 12);
|
||||||
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;
|
|
||||||
}
|
|
||||||
else if (item->AIBits & PATROL1)
|
else if (item->AIBits & PATROL1)
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = 2;
|
||||||
else if (creature->Mood == MoodType::Escape)
|
else if (creature->Mood == MoodType::Escape)
|
||||||
|
@ -235,8 +224,8 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
|
|
||||||
if (Targetable(item, &AI) &&
|
if (Targetable(item, &AI) &&
|
||||||
AI.distance < pow(SECTOR(4), 2) &&
|
AI.distance < pow(SECTOR(4), 2) &&
|
||||||
|
@ -255,8 +244,8 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
|
|
||||||
if (Targetable(item, &AI) &&
|
if (Targetable(item, &AI) &&
|
||||||
AI.distance < pow(SECTOR(4), 2) &&
|
AI.distance < pow(SECTOR(4), 2) &&
|
||||||
|
@ -276,8 +265,8 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
|
|
||||||
if (Targetable(item, &AI) &&
|
if (Targetable(item, &AI) &&
|
||||||
AI.distance < pow(SECTOR(4), 2) &&
|
AI.distance < pow(SECTOR(4), 2) &&
|
||||||
|
@ -311,12 +300,12 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
|
|
||||||
if (Targetable(item, &AI) &&
|
if (Targetable(item, &AI) &&
|
||||||
AI.distance < pow(SECTOR(4), 2) &&
|
AI.distance < pow(SECTOR(4), 2) &&
|
||||||
(realEnemy != LaraItem || creature->HurtByLara))
|
(!realEnemy->IsLara() || creature->HurtByLara))
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = 6;
|
item->Animation.TargetState = 6;
|
||||||
}
|
}
|
||||||
|
@ -343,9 +332,9 @@ namespace TEN::Entities::TR3
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureTilt(item, tilt);
|
CreatureTilt(item, tilt);
|
||||||
CreatureJoint(item, 0, torsoY);
|
CreatureJoint(item, 0, extraTorsoRot.y);
|
||||||
CreatureJoint(item, 1, torsoX);
|
CreatureJoint(item, 1, extraTorsoRot.x);
|
||||||
CreatureJoint(item, 2, head);
|
CreatureJoint(item, 2, extraHeadRot.y);
|
||||||
|
|
||||||
CreatureAnimation(itemNumber, angle, 0);
|
CreatureAnimation(itemNumber, angle, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
void FlameThrowerControl(short itemNumber);
|
void FlameThrowerControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,23 +12,85 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.h"
|
#include "Specific/setup.h"
|
||||||
|
|
||||||
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
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 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)
|
void InitialiseMonkey(short itemNumber)
|
||||||
{
|
{
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
|
|
||||||
ClearItem(itemNumber);
|
ClearItem(itemNumber);
|
||||||
|
SetAnimation(item, MONKEY_ANIM_SIT);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MonkeyControl(short itemNumber)
|
void MonkeyControl(short itemNumber)
|
||||||
|
@ -39,20 +101,17 @@ namespace TEN::Entities::TR3
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
short headX = 0;
|
|
||||||
short headY = 0;
|
|
||||||
short torsoY = 0;
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
short tilt = 0;
|
short tilt = 0;
|
||||||
|
auto extraHeadRot = EulerAngles::Zero;
|
||||||
|
auto extraTorsoRot = EulerAngles::Zero;
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
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->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
|
else
|
||||||
|
@ -63,12 +122,11 @@ namespace TEN::Entities::TR3
|
||||||
creature->Enemy = LaraItem;
|
creature->Enemy = LaraItem;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int minDistance = 0x7FFFFFFF;
|
|
||||||
creature->Enemy = nullptr;
|
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)
|
if (currentCreature->ItemNumber == NO_ITEM || currentCreature->ItemNumber == itemNumber)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -109,11 +167,11 @@ namespace TEN::Entities::TR3
|
||||||
AI_INFO AI;
|
AI_INFO AI;
|
||||||
CreatureAIInfo(item, &AI);
|
CreatureAIInfo(item, &AI);
|
||||||
|
|
||||||
if (!creature->HurtByLara && creature->Enemy == LaraItem)
|
if (!creature->HurtByLara && creature->Enemy->IsLara())
|
||||||
creature->Enemy = nullptr;
|
creature->Enemy = nullptr;
|
||||||
|
|
||||||
AI_INFO laraAI;
|
AI_INFO laraAI;
|
||||||
if (creature->Enemy == LaraItem)
|
if (creature->Enemy->IsLara())
|
||||||
{
|
{
|
||||||
laraAI.angle = AI.angle;
|
laraAI.angle = AI.angle;
|
||||||
laraAI.distance = AI.distance;
|
laraAI.distance = AI.distance;
|
||||||
|
@ -146,144 +204,147 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
switch (item->Animation.ActiveState)
|
switch (item->Animation.ActiveState)
|
||||||
{
|
{
|
||||||
case 6:
|
case MONKEY_STATE_SIT:
|
||||||
creature->Flags = 0;
|
|
||||||
creature->MaxTurn = 0;
|
creature->MaxTurn = 0;
|
||||||
torsoY = laraAI.angle;
|
creature->Flags = 0;
|
||||||
|
extraTorsoRot.y = laraAI.angle;
|
||||||
|
|
||||||
if (item->AIBits & GUARD)
|
if (item->AIBits & GUARD)
|
||||||
{
|
{
|
||||||
torsoY = AIGuard(creature);
|
extraTorsoRot.y = AIGuard(creature);
|
||||||
if (!(GetRandomControl() & 0xF))
|
if (TestProbability(0.06f))
|
||||||
{
|
{
|
||||||
if (GetRandomControl() & 0x1)
|
if (TestProbability(0.5f))
|
||||||
item->Animation.TargetState = 8;
|
item->Animation.TargetState = MONKEY_STATE_SIT_EAT;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 7;
|
item->Animation.TargetState = MONKEY_STATE_SIT_SCRATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (item->AIBits & PATROL1)
|
else if (item->AIBits & PATROL1)
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||||
else if (creature->Mood == MoodType::Escape)
|
else if (creature->Mood == MoodType::Escape)
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||||
else if (creature->Mood == MoodType::Bored)
|
else if (creature->Mood == MoodType::Bored)
|
||||||
{
|
{
|
||||||
if (item->Animation.RequiredState)
|
if (item->Animation.RequiredState)
|
||||||
item->Animation.TargetState = item->Animation.RequiredState;
|
item->Animation.TargetState = item->Animation.RequiredState;
|
||||||
else if (!(GetRandomControl() & 0xF))
|
else if (TestProbability(0.06f))
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||||
else if (!(GetRandomControl() & 0xF))
|
else if (TestProbability(0.06f))
|
||||||
{
|
{
|
||||||
if (GetRandomControl() & 0x1)
|
if (TestProbability(0.5f))
|
||||||
item->Animation.TargetState = 8;
|
item->Animation.TargetState = MONKEY_STATE_SIT_EAT;
|
||||||
else
|
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)
|
if (item->Animation.RequiredState)
|
||||||
item->Animation.TargetState = item->Animation.RequiredState;
|
item->Animation.TargetState = item->Animation.RequiredState;
|
||||||
else if (AI.ahead)
|
else if (AI.ahead)
|
||||||
item->Animation.TargetState = 6;
|
item->Animation.TargetState = MONKEY_STATE_SIT;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||||
}
|
}
|
||||||
else if (AI.bite && AI.distance < pow(682, 2))
|
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))
|
else if (AI.bite && AI.distance < pow(682, 2))
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case MONKEY_STATE_IDLE:
|
||||||
creature->MaxTurn = 0;
|
creature->MaxTurn = 0;
|
||||||
creature->Flags = 0;
|
creature->Flags = 0;
|
||||||
torsoY = laraAI.angle;
|
extraTorsoRot.y = laraAI.angle;
|
||||||
|
|
||||||
if (item->AIBits & GUARD)
|
if (item->AIBits & GUARD)
|
||||||
{
|
{
|
||||||
torsoY = AIGuard(creature);
|
extraTorsoRot.y = AIGuard(creature);
|
||||||
|
|
||||||
if (!(GetRandomControl() & 15))
|
if (TestProbability(0.06f))
|
||||||
{
|
{
|
||||||
if (GetRandomControl() & 1)
|
if (TestProbability(0.5f))
|
||||||
item->Animation.TargetState = 10;
|
item->Animation.TargetState = MONKEY_STATE_POUND_GROUND;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 6;
|
item->Animation.TargetState = MONKEY_STATE_SIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (item->AIBits & PATROL1)
|
else if (item->AIBits & PATROL1)
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||||
else if (creature->Mood == MoodType::Escape)
|
else if (creature->Mood == MoodType::Escape)
|
||||||
{
|
{
|
||||||
if (Lara.TargetEntity != item && AI.ahead)
|
if (Lara.TargetEntity != item && AI.ahead)
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 4;
|
item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD;
|
||||||
}
|
}
|
||||||
else if (creature->Mood == MoodType::Bored)
|
else if (creature->Mood == MoodType::Bored)
|
||||||
{
|
{
|
||||||
if (item->Animation.RequiredState)
|
if (item->Animation.RequiredState)
|
||||||
item->Animation.TargetState = item->Animation.RequiredState;
|
item->Animation.TargetState = item->Animation.RequiredState;
|
||||||
else if (!(GetRandomControl() & 15))
|
else if (TestProbability(0.06f))
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||||
else if (!(GetRandomControl() & 15))
|
else if (TestProbability(0.06f))
|
||||||
{
|
{
|
||||||
if (GetRandomControl() & 1)
|
if (TestProbability(0.5f))
|
||||||
item->Animation.TargetState = 10;
|
item->Animation.TargetState = MONKEY_STATE_POUND_GROUND;
|
||||||
else
|
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)
|
if (item->Animation.RequiredState)
|
||||||
item->Animation.TargetState = item->Animation.RequiredState;
|
item->Animation.TargetState = item->Animation.RequiredState;
|
||||||
else if (AI.ahead)
|
else if (AI.ahead)
|
||||||
item->Animation.TargetState = 6;
|
item->Animation.TargetState = MONKEY_STATE_SIT;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 4;
|
item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD;
|
||||||
}
|
}
|
||||||
else if (AI.bite && AI.distance < pow(341, 2))
|
else if (AI.bite && AI.distance < pow(341, 2))
|
||||||
{
|
{
|
||||||
if (LaraItem->Pose.Position.y < item->Pose.Position.y)
|
if (LaraItem->Pose.Position.y < item->Pose.Position.y)
|
||||||
item->Animation.TargetState = 13;
|
item->Animation.TargetState = MONKEY_STATE_JUMP_ATTACK;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 12;
|
item->Animation.TargetState = MONKEY_STATE_SWIPE_ATTACK;
|
||||||
}
|
}
|
||||||
else if (AI.bite && AI.distance < pow(682, 2))
|
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))
|
else if (AI.bite && AI.distance < pow(682, 2))
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||||
else if (AI.distance < pow(682, 2) && creature->Enemy != LaraItem && creature->Enemy != nullptr &&
|
else if (AI.distance < pow(682, 2) &&
|
||||||
creature->Enemy->ObjectNumber != ID_AI_PATROL1 && creature->Enemy->ObjectNumber != ID_AI_PATROL2 &&
|
!creature->Enemy->IsLara() && creature->Enemy != nullptr &&
|
||||||
abs(item->Pose.Position.y - creature->Enemy->Pose.Position.y) < 256)
|
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))
|
else if (AI.bite && AI.distance < pow(SECTOR(1), 2))
|
||||||
item->Animation.TargetState = 9;
|
item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD_ROLL;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 4;
|
item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 5:
|
case MONKEY_STATE_BITE_ATTACK:
|
||||||
creature->ReachedGoal = true;
|
creature->ReachedGoal = true;
|
||||||
|
|
||||||
if (creature->Enemy == nullptr)
|
if (creature->Enemy == nullptr)
|
||||||
break;
|
break;
|
||||||
else if ((creature->Enemy->ObjectNumber == ID_SMALLMEDI_ITEM ||
|
else if ((creature->Enemy->ObjectNumber == ID_SMALLMEDI_ITEM ||
|
||||||
creature->Enemy->ObjectNumber == ID_KEY_ITEM4) &&
|
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 ||
|
if (creature->Enemy->RoomNumber == NO_ROOM ||
|
||||||
creature->Enemy->Status == ITEM_INVISIBLE ||
|
creature->Enemy->Status == ITEM_INVISIBLE ||
|
||||||
|
@ -298,9 +359,8 @@ namespace TEN::Entities::TR3
|
||||||
creature->Enemy->RoomNumber = NO_ROOM;
|
creature->Enemy->RoomNumber = NO_ROOM;
|
||||||
creature->Enemy->CarriedItem = NO_ITEM;
|
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)
|
if (currentCreature->ItemNumber == NO_ITEM || currentCreature->ItemNumber == itemNumber)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -318,15 +378,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;
|
item->AIBits = 0;
|
||||||
|
|
||||||
auto* carriedItem = &g_Level.Items[item->CarriedItem];
|
auto* carriedItem = &g_Level.Items[item->CarriedItem];
|
||||||
|
|
||||||
carriedItem->Pose.Position.x = item->Pose.Position.x;
|
carriedItem->Pose.Position = item->Pose.Position;
|
||||||
carriedItem->Pose.Position.y = item->Pose.Position.y;
|
|
||||||
carriedItem->Pose.Position.z = item->Pose.Position.z;
|
|
||||||
|
|
||||||
ItemNewRoom(item->CarriedItem, item->RoomNumber);
|
ItemNewRoom(item->CarriedItem, item->RoomNumber);
|
||||||
item->CarriedItem = NO_ITEM;
|
item->CarriedItem = NO_ITEM;
|
||||||
|
@ -348,60 +407,106 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case MONKEY_STATE_WALK_FORWARD:
|
||||||
creature->MaxTurn = ANGLE(7.0f);
|
creature->MaxTurn = ANGLE(7.0f);
|
||||||
torsoY = laraAI.angle;
|
extraTorsoRot.y = laraAI.angle;
|
||||||
|
|
||||||
if (item->AIBits & PATROL1)
|
if (item->AIBits & PATROL1)
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD;
|
||||||
torsoY = 0;
|
extraTorsoRot.y = 0;
|
||||||
}
|
}
|
||||||
else if (creature->Mood == MoodType::Escape)
|
else if (creature->Mood == MoodType::Escape)
|
||||||
item->Animation.TargetState = 4;
|
item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD;
|
||||||
else if (creature->Mood == MoodType::Bored)
|
else if (creature->Mood == MoodType::Bored)
|
||||||
{
|
{
|
||||||
if (GetRandomControl() < 256)
|
if (TestProbability(1.0f / 128))
|
||||||
item->Animation.TargetState = 6;
|
item->Animation.TargetState = MONKEY_STATE_SIT;
|
||||||
}
|
}
|
||||||
else if (AI.bite && AI.distance < pow(682, 2))
|
else if (AI.bite && AI.distance < pow(682, 2))
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case MONKEY_STATE_RUN_FORWARD:
|
||||||
creature->MaxTurn = ANGLE(11.0f);
|
creature->MaxTurn = ANGLE(11.0f);
|
||||||
tilt = angle / 2;
|
tilt = angle / 2;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
|
|
||||||
if (item->AIBits & GUARD)
|
if (item->AIBits & GUARD)
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||||
else if (creature->Mood == MoodType::Escape)
|
else if (creature->Mood == MoodType::Escape)
|
||||||
{
|
{
|
||||||
if (Lara.TargetEntity != item && AI.ahead)
|
if (Lara.TargetEntity != item && AI.ahead)
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if ((item->AIBits & FOLLOW) && (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2)))
|
else if ((item->AIBits & FOLLOW) &&
|
||||||
item->Animation.TargetState = 3;
|
(creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2)))
|
||||||
|
{
|
||||||
|
item->Animation.TargetState = MONKEY_STATE_IDLE;
|
||||||
|
}
|
||||||
else if (creature->Mood == MoodType::Bored)
|
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))
|
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))
|
else if (AI.bite && AI.distance < pow(SECTOR(1), 2))
|
||||||
item->Animation.TargetState = 9;
|
item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD_ROLL;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 12:
|
case MONKEY_STATE_SWIPE_ATTACK:
|
||||||
creature->MaxTurn = 0;
|
creature->MaxTurn = 0;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
headY = AI.angle;
|
extraHeadRot.x = AI.xAngle;
|
||||||
headX = 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))
|
if (abs(AI.angle) < ANGLE(7.0f))
|
||||||
|
@ -422,11 +527,9 @@ namespace TEN::Entities::TR3
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!creature->Flags && enemy)
|
if (!creature->Flags && enemy != nullptr)
|
||||||
{
|
{
|
||||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < CLICK(1) &&
|
if (Vector3i::Distance(item->Pose.Position, enemy->Pose.Position) <= 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);
|
DoDamage(enemy, 20);
|
||||||
CreatureEffect(item, MonkeyBite, DoBloodSplat);
|
CreatureEffect(item, MonkeyBite, DoBloodSplat);
|
||||||
|
@ -437,55 +540,13 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 13:
|
case MONKEY_STATE_HIGH_JUMP_ATTACK:
|
||||||
creature->MaxTurn = 0;
|
creature->MaxTurn = 0;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
headY = AI.angle;
|
extraHeadRot.x = AI.xAngle;
|
||||||
headX = 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, 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abs(AI.angle) < ANGLE(7.0f))
|
if (abs(AI.angle) < ANGLE(7.0f))
|
||||||
|
@ -508,9 +569,7 @@ namespace TEN::Entities::TR3
|
||||||
{
|
{
|
||||||
if (creature->Flags != 1 && enemy)
|
if (creature->Flags != 1 && enemy)
|
||||||
{
|
{
|
||||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < CLICK(1) &&
|
if (Vector3i::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= 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, 25);
|
DoDamage(enemy, 25);
|
||||||
CreatureEffect(item, MonkeyBite, DoBloodSplat);
|
CreatureEffect(item, MonkeyBite, DoBloodSplat);
|
||||||
|
@ -524,54 +583,42 @@ namespace TEN::Entities::TR3
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureTilt(item, tilt);
|
CreatureTilt(item, tilt);
|
||||||
CreatureJoint(item, 0, headY);
|
CreatureJoint(item, 0, extraHeadRot.y);
|
||||||
CreatureJoint(item, 1, headX);
|
CreatureJoint(item, 1, extraHeadRot.x);
|
||||||
CreatureJoint(item, 2, torsoY);
|
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:
|
case 2:
|
||||||
|
SetAnimation(item, MONKEY_ANIM_VAULT_UP_0_POINT_2_BLOCKS);
|
||||||
creature->MaxTurn = 0;
|
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;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
|
SetAnimation(item, MONKEY_ANIM_VAULT_UP_0_POINT_3_BLOCKS);
|
||||||
creature->MaxTurn = 0;
|
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;
|
break;
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
|
SetAnimation(item, MONKEY_ANIM_VAULT_UP_1_BLOCK);
|
||||||
creature->MaxTurn = 0;
|
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;
|
break;
|
||||||
|
|
||||||
case -2:
|
case -2:
|
||||||
|
SetAnimation(item, MONKEY_ANIM_VAULT_DOWN_0_POINT_2_BLOCKS);
|
||||||
creature->MaxTurn = 0;
|
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;
|
break;
|
||||||
|
|
||||||
case -3:
|
case -3:
|
||||||
|
SetAnimation(item, MONKEY_ANIM_VAULT_DOWN_0_POINT_3_BLOCKS);
|
||||||
creature->MaxTurn = 0;
|
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;
|
break;
|
||||||
|
|
||||||
case -4:
|
case -4:
|
||||||
|
SetAnimation(item, MONKEY_ANIM_VAULT_DOWN_1_BLOCK);
|
||||||
creature->MaxTurn = 0;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
void InitialiseMonkey(short itemNumber);
|
void InitialiseMonkey(short itemNumber);
|
||||||
void MonkeyControl(short itemNumber);
|
void MonkeyControl(short itemNumber);
|
||||||
|
|
|
@ -16,13 +16,14 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.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);
|
const auto MPGunBite = BiteInfo(Vector3(0.0f, 160.0f, 40.0f), 13);
|
||||||
|
|
||||||
enum MPGunState
|
enum MPGunState
|
||||||
{
|
{
|
||||||
MPGUN_STATE_NONE = 0,
|
|
||||||
MPGUN_STATE_WAIT = 1,
|
MPGUN_STATE_WAIT = 1,
|
||||||
MPGUN_STATE_WALK = 2,
|
MPGUN_STATE_WALK = 2,
|
||||||
MPGUN_STATE_RUN = 3,
|
MPGUN_STATE_RUN = 3,
|
||||||
|
@ -59,11 +60,10 @@ namespace TEN::Entities::TR3
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
short head = 0;
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
short tilt = 0;
|
short tilt = 0;
|
||||||
short torsoX = 0;
|
short head = 0;
|
||||||
short torsoY = 0;
|
auto extraTorsoRot = EulerAngles::Zero;
|
||||||
|
|
||||||
if (creature->FiredWeapon)
|
if (creature->FiredWeapon)
|
||||||
{
|
{
|
||||||
|
@ -75,8 +75,8 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (item->BoxNumber != NO_BOX && (g_Level.Boxes[item->BoxNumber].flags & BLOCKED))
|
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);
|
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;
|
AI_INFO AI;
|
||||||
|
@ -90,7 +90,7 @@ namespace TEN::Entities::TR3
|
||||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
||||||
item->Animation.ActiveState = 13;
|
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);
|
CreatureAIInfo(item, &AI);
|
||||||
|
|
||||||
|
@ -100,8 +100,8 @@ namespace TEN::Entities::TR3
|
||||||
AI.angle < ANGLE(45.0f))
|
AI.angle < ANGLE(45.0f))
|
||||||
{
|
{
|
||||||
head = AI.angle;
|
head = AI.angle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
ShotLara(item, &AI, MPGunBite, torsoY, 32);
|
ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32);
|
||||||
SoundEffect(SFX_TR3_OIL_SMG_FIRE, &item->Pose, SoundEnvironment::Land, 1.0f, 0.7f);
|
SoundEffect(SFX_TR3_OIL_SMG_FIRE, &item->Pose, SoundEnvironment::Land, 1.0f, 0.7f);
|
||||||
creature->FiredWeapon = 1;
|
creature->FiredWeapon = 1;
|
||||||
}
|
}
|
||||||
|
@ -219,10 +219,9 @@ namespace TEN::Entities::TR3
|
||||||
item->Animation.TargetState = MPGUN_STATE_RUN;
|
item->Animation.TargetState = MPGUN_STATE_RUN;
|
||||||
else if (Targetable(item, &AI))
|
else if (Targetable(item, &AI))
|
||||||
{
|
{
|
||||||
int random = GetRandomControl();
|
if (TestProbability(0.25f))
|
||||||
if (random < 0x2000)
|
|
||||||
item->Animation.TargetState = MPGUN_STATE_SHOOT_1;
|
item->Animation.TargetState = MPGUN_STATE_SHOOT_1;
|
||||||
else if (random < 0x4000)
|
else if (TestProbability(0.5f))
|
||||||
item->Animation.TargetState = MPGUN_STATE_SHOOT_2;
|
item->Animation.TargetState = MPGUN_STATE_SHOOT_2;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = MPGUN_STATE_AIM_3;
|
item->Animation.TargetState = MPGUN_STATE_AIM_3;
|
||||||
|
@ -304,17 +303,18 @@ namespace TEN::Entities::TR3
|
||||||
case MPGUN_STATE_AIM_1:
|
case MPGUN_STATE_AIM_1:
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 12 ||
|
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;
|
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.RequiredState = MPGUN_STATE_CROUCH;
|
||||||
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
||||||
|
@ -325,8 +325,8 @@ namespace TEN::Entities::TR3
|
||||||
case MPGUN_STATE_SHOOT_1:
|
case MPGUN_STATE_SHOOT_1:
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item->Animation.RequiredState == MPGUN_STATE_WAIT)
|
if (item->Animation.RequiredState == MPGUN_STATE_WAIT)
|
||||||
|
@ -337,16 +337,16 @@ namespace TEN::Entities::TR3
|
||||||
case MPGUN_STATE_SHOOT_2:
|
case MPGUN_STATE_SHOOT_2:
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase)
|
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;
|
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.RequiredState = MPGUN_STATE_CROUCH;
|
||||||
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
||||||
|
@ -358,17 +358,17 @@ namespace TEN::Entities::TR3
|
||||||
case MPGUN_STATE_SHOOT_3B:
|
case MPGUN_STATE_SHOOT_3B:
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase ||
|
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase ||
|
||||||
item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 11)
|
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;
|
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.RequiredState = MPGUN_STATE_CROUCH;
|
||||||
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
||||||
|
@ -379,17 +379,19 @@ namespace TEN::Entities::TR3
|
||||||
case MPGUN_STATE_AIM_4:
|
case MPGUN_STATE_AIM_4:
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
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) ||
|
if ((item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 18 &&
|
||||||
(item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 19 && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 6))
|
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;
|
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.RequiredState = MPGUN_STATE_CROUCH;
|
||||||
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
item->Animation.TargetState = MPGUN_STATE_WAIT;
|
||||||
|
@ -404,8 +406,8 @@ namespace TEN::Entities::TR3
|
||||||
case MPGUN_STATE_SHOOT_4B:
|
case MPGUN_STATE_SHOOT_4B:
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item->Animation.RequiredState == MPGUN_STATE_WALK)
|
if (item->Animation.RequiredState == MPGUN_STATE_WALK)
|
||||||
|
@ -413,7 +415,7 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 16)
|
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;
|
item->Animation.TargetState = MPGUN_STATE_WALK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,7 +432,7 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (Targetable(item, &AI))
|
if (Targetable(item, &AI))
|
||||||
item->Animation.TargetState = MPGUN_STATE_CROUCH_AIM;
|
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;
|
item->Animation.TargetState = MPGUN_STATE_STAND;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = MPGUN_STATE_CROUCH_WALK;
|
item->Animation.TargetState = MPGUN_STATE_CROUCH_WALK;
|
||||||
|
@ -441,7 +443,7 @@ namespace TEN::Entities::TR3
|
||||||
creature->MaxTurn = ANGLE(1.0f);
|
creature->MaxTurn = ANGLE(1.0f);
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
|
|
||||||
if (Targetable(item, &AI))
|
if (Targetable(item, &AI))
|
||||||
item->Animation.TargetState = MPGUN_STATE_CROUCH_SHOT;
|
item->Animation.TargetState = MPGUN_STATE_CROUCH_SHOT;
|
||||||
|
@ -452,11 +454,11 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
case MPGUN_STATE_CROUCH_SHOT:
|
case MPGUN_STATE_CROUCH_SHOT:
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
|
|
||||||
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase)
|
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;
|
item->Animation.TargetState = MPGUN_STATE_CROUCHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,7 +470,7 @@ namespace TEN::Entities::TR3
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
head = AI.angle;
|
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;
|
item->Animation.TargetState = MPGUN_STATE_CROUCHED;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -476,8 +478,8 @@ namespace TEN::Entities::TR3
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureTilt(item, tilt);
|
CreatureTilt(item, tilt);
|
||||||
CreatureJoint(item, 0, torsoY);
|
CreatureJoint(item, 0, extraTorsoRot.y);
|
||||||
CreatureJoint(item, 1, torsoX);
|
CreatureJoint(item, 1, extraTorsoRot.x);
|
||||||
CreatureJoint(item, 2, head);
|
CreatureJoint(item, 2, head);
|
||||||
CreatureAnimation(itemNumber, angle, tilt);
|
CreatureAnimation(itemNumber, angle, tilt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
void MPGunControl(short itemNumber);
|
void MPGunControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,18 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.h"
|
#include "Specific/setup.h"
|
||||||
|
|
||||||
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
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 MPStickBite1 = BiteInfo(Vector3(247.0f, 10.0f, 11.0f), 13);
|
||||||
const auto MPStickBite2 = BiteInfo(Vector3(0.0f, 0.0f, 100.0f), 6);
|
const auto MPStickBite2 = BiteInfo(Vector3(0.0f, 0.0f, 100.0f), 6);
|
||||||
const vector<int> MPStickPunchAttackJoints = { 10, 13 };
|
const vector<int> MPStickPunchAttackJoints = { 10, 13 };
|
||||||
const vector<int> MPStickKickAttackJoints = { 5, 6 };
|
const vector<int> MPStickKickAttackJoints = { 5, 6 };
|
||||||
|
|
||||||
enum MPStickState
|
enum MPStickState
|
||||||
{
|
{
|
||||||
MPSTICK_STATE_NONE,
|
|
||||||
MPSTICK_STATE_STOP,
|
MPSTICK_STATE_STOP,
|
||||||
MPSTICK_STATE_WALK,
|
MPSTICK_STATE_WALK,
|
||||||
MPSTICK_STATE_PUNCH2,
|
MPSTICK_STATE_PUNCH2,
|
||||||
|
@ -54,10 +54,7 @@ namespace TEN::Entities::TR3
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
|
|
||||||
ClearItem(itemNumber);
|
ClearItem(itemNumber);
|
||||||
|
SetAnimation(item, 6);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MPStickControl(short itemNumber)
|
void MPStickControl(short itemNumber)
|
||||||
|
@ -68,11 +65,10 @@ namespace TEN::Entities::TR3
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
short head = 0;
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
short tilt = 0;
|
short tilt = 0;
|
||||||
short torsoX = 0;
|
short head = 0;
|
||||||
short torsoY = 0;
|
auto extraTorsoRot = EulerAngles::Zero;
|
||||||
|
|
||||||
if (item->BoxNumber != NO_BOX && (g_Level.Boxes[item->BoxNumber].flags & BLOCKED))
|
if (item->BoxNumber != NO_BOX && (g_Level.Boxes[item->BoxNumber].flags & BLOCKED))
|
||||||
{
|
{
|
||||||
|
@ -85,9 +81,7 @@ namespace TEN::Entities::TR3
|
||||||
{
|
{
|
||||||
if (item->Animation.ActiveState != MPSTICK_STATE_DEATH)
|
if (item->Animation.ActiveState != MPSTICK_STATE_DEATH)
|
||||||
{
|
{
|
||||||
item->Animation.AnimNumber = Objects[ID_MP_WITH_STICK].animIndex + 26;
|
SetAnimation(item, 26);
|
||||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
|
||||||
item->Animation.ActiveState = MPSTICK_STATE_DEATH;
|
|
||||||
creature->LOT.Step = 256;
|
creature->LOT.Step = 256;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +97,7 @@ namespace TEN::Entities::TR3
|
||||||
int dz = LaraItem->Pose.Position.z - item->Pose.Position.z;
|
int dz = LaraItem->Pose.Position.z - item->Pose.Position.z;
|
||||||
laraAI.distance = pow(dx, 2) + pow(dx, 2);
|
laraAI.distance = pow(dx, 2) + pow(dx, 2);
|
||||||
|
|
||||||
int bestDistance = 0x7fffffff;
|
int bestDistance = INT_MAX;
|
||||||
for (int slot = 0; slot < ActiveCreatures.size(); slot++)
|
for (int slot = 0; slot < ActiveCreatures.size(); slot++)
|
||||||
{
|
{
|
||||||
auto* currentCreature = ActiveCreatures[slot];
|
auto* currentCreature = ActiveCreatures[slot];
|
||||||
|
@ -184,7 +178,7 @@ namespace TEN::Entities::TR3
|
||||||
if (item->AIBits & GUARD)
|
if (item->AIBits & GUARD)
|
||||||
{
|
{
|
||||||
head = AIGuard(creature);
|
head = AIGuard(creature);
|
||||||
if (!(GetRandomControl() & 0xFF))
|
if (TestProbability(1.0f / 256))
|
||||||
{
|
{
|
||||||
if (item->Animation.ActiveState == MPSTICK_STATE_STOP)
|
if (item->Animation.ActiveState == MPSTICK_STATE_STOP)
|
||||||
item->Animation.TargetState = MPSTICK_STATE_WAIT;
|
item->Animation.TargetState = MPSTICK_STATE_WAIT;
|
||||||
|
@ -194,7 +188,6 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (item->AIBits & PATROL1)
|
else if (item->AIBits & PATROL1)
|
||||||
item->Animation.TargetState = MPSTICK_STATE_WALK;
|
item->Animation.TargetState = MPSTICK_STATE_WALK;
|
||||||
|
|
||||||
|
@ -240,7 +233,7 @@ namespace TEN::Entities::TR3
|
||||||
item->Animation.TargetState = MPSTICK_STATE_RUN;
|
item->Animation.TargetState = MPSTICK_STATE_RUN;
|
||||||
else if (creature->Mood == MoodType::Bored)
|
else if (creature->Mood == MoodType::Bored)
|
||||||
{
|
{
|
||||||
if (GetRandomControl() < 0x100)
|
if (TestProbability(1.0f / 128))
|
||||||
{
|
{
|
||||||
item->Animation.RequiredState = MPSTICK_STATE_WAIT;
|
item->Animation.RequiredState = MPSTICK_STATE_WAIT;
|
||||||
item->Animation.TargetState = MPSTICK_STATE_STOP;
|
item->Animation.TargetState = MPSTICK_STATE_STOP;
|
||||||
|
@ -288,8 +281,8 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AI.bite && AI.distance < pow(SECTOR(0.5f), 2))
|
if (AI.bite && AI.distance < pow(SECTOR(0.5f), 2))
|
||||||
|
@ -305,8 +298,8 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
if (AI.ahead && AI.distance < pow(SECTOR(1), 2))
|
||||||
|
@ -322,8 +315,8 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AI.bite && AI.distance < pow(SECTOR(1.25f), 2))
|
if (AI.bite && AI.distance < pow(SECTOR(1.25f), 2))
|
||||||
|
@ -338,32 +331,30 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enemy->IsLara())
|
if (creature->Enemy->IsLara())
|
||||||
{
|
{
|
||||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, MPStickPunchAttackJoints))
|
if (!creature->Flags && item->TestBits(JointBitType::Touch, MPStickPunchAttackJoints))
|
||||||
{
|
{
|
||||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
|
||||||
DoDamage(enemy, 80);
|
DoDamage(enemy, 80);
|
||||||
|
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||||
creature->Flags = 1;
|
creature->Flags = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!creature->Flags && enemy)
|
if (!creature->Flags && enemy != nullptr)
|
||||||
{
|
{
|
||||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < SECTOR(0.25f) &&
|
if (Vector3i::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= 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))
|
|
||||||
{
|
{
|
||||||
creature->Flags = 1;
|
|
||||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
|
||||||
DoDamage(enemy, 5);
|
DoDamage(enemy, 5);
|
||||||
|
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||||
|
creature->Flags = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -375,32 +366,30 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enemy->IsLara())
|
if (creature->Enemy->IsLara())
|
||||||
{
|
{
|
||||||
if (!creature->Flags && item->TestBits(JointBitType::Touch, MPStickPunchAttackJoints))
|
if (!creature->Flags && item->TestBits(JointBitType::Touch, MPStickPunchAttackJoints))
|
||||||
{
|
{
|
||||||
|
DoDamage(creature->Enemy, 80);
|
||||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||||
DoDamage(enemy, 80);
|
|
||||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||||
creature->Flags = 1;
|
creature->Flags = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!creature->Flags && enemy)
|
if (!creature->Flags && creature->Enemy != nullptr)
|
||||||
{
|
{
|
||||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < SECTOR(0.25f) &&
|
if (Vector3i::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= 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))
|
|
||||||
{
|
{
|
||||||
creature->Flags = 1;
|
DoDamage(creature->Enemy, 5);
|
||||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||||
DoDamage(enemy, 5);
|
|
||||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||||
|
creature->Flags = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -415,32 +404,30 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
torsoX = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
torsoY = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enemy->IsLara())
|
if (creature->Enemy->IsLara())
|
||||||
{
|
{
|
||||||
if (creature->Flags != 2 && item->TestBits(JointBitType::Touch, MPStickPunchAttackJoints))
|
if (creature->Flags != 2 && item->TestBits(JointBitType::Touch, MPStickPunchAttackJoints))
|
||||||
{
|
{
|
||||||
|
DoDamage(creature->Enemy, 100);
|
||||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||||
DoDamage(enemy, 100);
|
|
||||||
creature->Flags = 2;
|
|
||||||
SoundEffect(70, &item->Pose);
|
SoundEffect(70, &item->Pose);
|
||||||
|
creature->Flags = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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) &&
|
if (Vector3i::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= 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))
|
|
||||||
{
|
{
|
||||||
creature->Flags = 2;
|
DoDamage(creature->Enemy, 6);
|
||||||
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
CreatureEffect(item, MPStickBite1, DoBloodSplat);
|
||||||
DoDamage(enemy, 6);
|
|
||||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||||
|
creature->Flags = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -451,32 +438,30 @@ namespace TEN::Entities::TR3
|
||||||
creature->MaxTurn = ANGLE(6.0f);
|
creature->MaxTurn = ANGLE(6.0f);
|
||||||
|
|
||||||
if (AI.ahead)
|
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) &&
|
if (creature->Flags != 1 && item->TestBits(JointBitType::Touch, MPStickKickAttackJoints) &&
|
||||||
item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 8)
|
item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 8)
|
||||||
{
|
{
|
||||||
|
DoDamage(creature->Enemy, 150);
|
||||||
CreatureEffect(item, MPStickBite2, DoBloodSplat);
|
CreatureEffect(item, MPStickBite2, DoBloodSplat);
|
||||||
DoDamage(enemy, 150);
|
|
||||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||||
creature->Flags = 1;
|
creature->Flags = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!creature->Flags != 1 && enemy &&
|
if (!creature->Flags != 1 && creature->Enemy &&
|
||||||
item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 8)
|
item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 8)
|
||||||
{
|
{
|
||||||
if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < SECTOR(0.25f) &&
|
if (Vector3i::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= 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))
|
|
||||||
{
|
{
|
||||||
creature->Flags = 1;
|
DoDamage(creature->Enemy, 9);
|
||||||
CreatureEffect(item, MPStickBite2, DoBloodSplat);
|
CreatureEffect(item, MPStickBite2, DoBloodSplat);
|
||||||
DoDamage(enemy, 9);
|
|
||||||
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
SoundEffect(SFX_TR4_LARA_THUD, &item->Pose);
|
||||||
|
creature->Flags = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -486,8 +471,8 @@ namespace TEN::Entities::TR3
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureTilt(item, tilt);
|
CreatureTilt(item, tilt);
|
||||||
CreatureJoint(item, 0, torsoY);
|
CreatureJoint(item, 0, extraTorsoRot.y);
|
||||||
CreatureJoint(item, 1, torsoX);
|
CreatureJoint(item, 1, extraTorsoRot.x);
|
||||||
CreatureJoint(item, 2, head);
|
CreatureJoint(item, 2, head);
|
||||||
|
|
||||||
if (item->Animation.ActiveState < MPSTICK_STATE_DEATH)
|
if (item->Animation.ActiveState < MPSTICK_STATE_DEATH)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
void InitialiseMPStick(short itemNumber);
|
void InitialiseMPStick(short itemNumber);
|
||||||
void MPStickControl(short itemNumber);
|
void MPStickControl(short itemNumber);
|
||||||
|
|
|
@ -16,10 +16,17 @@
|
||||||
using namespace TEN::Math::Random;
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
constexpr auto RAPTOR_ATTACK_DAMAGE = 100;
|
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_WALK_TURN_RATE_MAX ANGLE(2.0f)
|
||||||
#define RAPTOR_RUN_TURN_RATE_MAX ANGLE(2.0f)
|
#define RAPTOR_RUN_TURN_RATE_MAX ANGLE(2.0f)
|
||||||
#define RAPTOR_ATTACK_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* item = &g_Level.Items[itemNumber];
|
||||||
auto* creature = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
short head = 0;
|
|
||||||
short neck = 0;
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
short tilt = 0;
|
short tilt = 0;
|
||||||
|
short head = 0;
|
||||||
|
short neck = 0;
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
if (item->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
|
@ -86,14 +93,13 @@ namespace TEN::Entities::TR3
|
||||||
}
|
}
|
||||||
else
|
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;
|
ItemInfo* nearestItem = nullptr;
|
||||||
int minDistance = INT_MAX;
|
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)
|
if (currentCreature->ItemNumber == NO_ITEM || currentCreature->ItemNumber == itemNumber)
|
||||||
{
|
{
|
||||||
currentCreature++;
|
currentCreature++;
|
||||||
|
@ -106,7 +112,7 @@ namespace TEN::Entities::TR3
|
||||||
int y = (targetItem->Pose.Position.y - item->Pose.Position.y) / 64;
|
int y = (targetItem->Pose.Position.y - item->Pose.Position.y) / 64;
|
||||||
int z = (targetItem->Pose.Position.z - item->Pose.Position.z) / 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)
|
if (distance < minDistance && item->HitPoints > 0)
|
||||||
{
|
{
|
||||||
nearestItem = targetItem;
|
nearestItem = targetItem;
|
||||||
|
@ -118,7 +124,7 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (nearestItem != nullptr &&
|
if (nearestItem != nullptr &&
|
||||||
(nearestItem->ObjectNumber != ID_RAPTOR ||
|
(nearestItem->ObjectNumber != ID_RAPTOR ||
|
||||||
(TestProbability(0.03f) && minDistance < pow(SECTOR(2), 2))))
|
(TestProbability(1.0f / 30) && minDistance < SQUARE(SECTOR(2)))))
|
||||||
{
|
{
|
||||||
creature->Enemy = nearestItem;
|
creature->Enemy = nearestItem;
|
||||||
}
|
}
|
||||||
|
@ -127,7 +133,7 @@ namespace TEN::Entities::TR3
|
||||||
int y = (LaraItem->Pose.Position.y - item->Pose.Position.y) / 64;
|
int y = (LaraItem->Pose.Position.y - item->Pose.Position.y) / 64;
|
||||||
int z = (LaraItem->Pose.Position.z - item->Pose.Position.z) / 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)
|
if (distance <= minDistance)
|
||||||
creature->Enemy = LaraItem;
|
creature->Enemy = LaraItem;
|
||||||
}
|
}
|
||||||
|
@ -164,11 +170,11 @@ namespace TEN::Entities::TR3
|
||||||
item->Animation.TargetState = RAPTOR_STATE_ROAR;
|
item->Animation.TargetState = RAPTOR_STATE_ROAR;
|
||||||
}
|
}
|
||||||
else if (item->TestBits(JointBitType::Touch, RaptorAttackJoints) ||
|
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;
|
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;
|
item->Animation.TargetState = RAPTOR_STATE_JUMP_ATTACK;
|
||||||
else if (creature->Mood == MoodType::Escape &&
|
else if (creature->Mood == MoodType::Escape &&
|
||||||
Lara.TargetEntity != item && AI.ahead && !item->HitStatus)
|
Lara.TargetEntity != item && AI.ahead && !item->HitStatus)
|
||||||
|
@ -188,7 +194,7 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (creature->Mood != MoodType::Bored)
|
if (creature->Mood != MoodType::Bored)
|
||||||
item->Animation.TargetState = RAPTOR_STATE_IDLE;
|
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.TargetState = RAPTOR_STATE_IDLE;
|
||||||
item->Animation.RequiredState = RAPTOR_STATE_ROAR;
|
item->Animation.RequiredState = RAPTOR_STATE_ROAR;
|
||||||
|
@ -210,7 +216,7 @@ namespace TEN::Entities::TR3
|
||||||
item->Animation.RequiredState = RAPTOR_STATE_ROAR;
|
item->Animation.RequiredState = RAPTOR_STATE_ROAR;
|
||||||
creature->Flags &= ~2;
|
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)
|
if (item->Animation.TargetState == RAPTOR_STATE_RUN_FORWARD)
|
||||||
{
|
{
|
||||||
|
@ -221,7 +227,7 @@ namespace TEN::Entities::TR3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (AI.ahead && creature->Mood != MoodType::Escape &&
|
else if (AI.ahead && creature->Mood != MoodType::Escape &&
|
||||||
TestProbability(0.004f))
|
TestProbability(RAPTOR_ROAR_CHANCE))
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = RAPTOR_STATE_IDLE;
|
item->Animation.TargetState = RAPTOR_STATE_IDLE;
|
||||||
item->Animation.RequiredState = RAPTOR_STATE_ROAR;
|
item->Animation.RequiredState = RAPTOR_STATE_ROAR;
|
||||||
|
@ -257,10 +263,7 @@ namespace TEN::Entities::TR3
|
||||||
{
|
{
|
||||||
if (!(creature->Flags & 1) && creature->Enemy != nullptr)
|
if (!(creature->Flags & 1) && creature->Enemy != nullptr)
|
||||||
{
|
{
|
||||||
auto direction = creature->Enemy->Pose.Position - item->Pose.Position;
|
if (Vector3i::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.5f))
|
||||||
if (abs(direction.x) < SECTOR(0.5f) &&
|
|
||||||
abs(direction.y) < SECTOR(0.5f) &&
|
|
||||||
abs(direction.z) < SECTOR(0.5f))
|
|
||||||
{
|
{
|
||||||
if (creature->Enemy->HitPoints <= 0)
|
if (creature->Enemy->HitPoints <= 0)
|
||||||
creature->Flags |= 2;
|
creature->Flags |= 2;
|
||||||
|
@ -297,10 +300,7 @@ namespace TEN::Entities::TR3
|
||||||
{
|
{
|
||||||
if (!(creature->Flags & 1) && creature->Enemy != nullptr)
|
if (!(creature->Flags & 1) && creature->Enemy != nullptr)
|
||||||
{
|
{
|
||||||
auto direction = creature->Enemy->Pose.Position - item->Pose.Position;
|
if (Vector3i::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.5f))
|
||||||
if (abs(direction.x) < SECTOR(0.5f) &&
|
|
||||||
abs(direction.y) < SECTOR(0.5f) &&
|
|
||||||
abs(direction.z) < SECTOR(0.5f))
|
|
||||||
{
|
{
|
||||||
if (creature->Enemy->HitPoints <= 0)
|
if (creature->Enemy->HitPoints <= 0)
|
||||||
creature->Flags |= 2;
|
creature->Flags |= 2;
|
||||||
|
@ -336,10 +336,7 @@ namespace TEN::Entities::TR3
|
||||||
{
|
{
|
||||||
if (!(creature->Flags & 1) && creature->Enemy != nullptr)
|
if (!(creature->Flags & 1) && creature->Enemy != nullptr)
|
||||||
{
|
{
|
||||||
auto direction = creature->Enemy->Pose.Position - item->Pose.Position;
|
if (Vector3i::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.5f))
|
||||||
if (abs(direction.x) < SECTOR(0.5f) &&
|
|
||||||
abs(direction.y) < SECTOR(0.5f) &&
|
|
||||||
abs(direction.z) < SECTOR(0.5f))
|
|
||||||
{
|
{
|
||||||
if (creature->Enemy->HitPoints <= 0)
|
if (creature->Enemy->HitPoints <= 0)
|
||||||
creature->Flags |= 2;
|
creature->Flags |= 2;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
void RaptorControl(short itemNumber);
|
void RaptorControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/setup.h"
|
#include "Specific/setup.h"
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
constexpr auto SCUBA_DIVER_ATTACK_DAMAGE = 50;
|
constexpr auto SCUBA_DIVER_ATTACK_DAMAGE = 50;
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
enum ScubaDiverState
|
enum ScubaDiverState
|
||||||
{
|
{
|
||||||
SDIVER_STATE_NONE = 0,
|
|
||||||
SDIVER_STATE_SWIM = 1,
|
SDIVER_STATE_SWIM = 1,
|
||||||
SDIVER_STATE_TREAD_WATER_IDLE = 2,
|
SDIVER_STATE_TREAD_WATER_IDLE = 2,
|
||||||
SDIVER_STATE_SWIM_SHOOT = 3,
|
SDIVER_STATE_SWIM_SHOOT = 3,
|
||||||
|
@ -60,23 +59,23 @@ namespace TEN::Entities::TR3
|
||||||
static void ShootHarpoon(ItemInfo* item, Vector3i pos, short velocity, short yRot, short roomNumber)
|
static void ShootHarpoon(ItemInfo* item, Vector3i pos, short velocity, short yRot, short roomNumber)
|
||||||
{
|
{
|
||||||
short harpoonItemNumber = CreateItem();
|
short harpoonItemNumber = CreateItem();
|
||||||
if (harpoonItemNumber != NO_ITEM)
|
if (harpoonItemNumber == NO_ITEM)
|
||||||
{
|
return;
|
||||||
auto* harpoonItem = &g_Level.Items[harpoonItemNumber];
|
|
||||||
|
|
||||||
harpoonItem->ObjectNumber = ID_SCUBA_HARPOON;
|
auto* harpoonItem = &g_Level.Items[harpoonItemNumber];
|
||||||
harpoonItem->RoomNumber = item->RoomNumber;
|
|
||||||
harpoonItem->Pose.Position = pos;
|
|
||||||
|
|
||||||
InitialiseItem(harpoonItemNumber);
|
harpoonItem->ObjectNumber = ID_SCUBA_HARPOON;
|
||||||
|
harpoonItem->RoomNumber = item->RoomNumber;
|
||||||
|
harpoonItem->Pose.Position = pos;
|
||||||
|
|
||||||
harpoonItem->Animation.Velocity.z = 150;
|
InitialiseItem(harpoonItemNumber);
|
||||||
harpoonItem->Pose.Orientation.x = 0;
|
|
||||||
harpoonItem->Pose.Orientation.y = yRot;
|
|
||||||
|
|
||||||
AddActiveItem(harpoonItemNumber);
|
harpoonItem->Animation.Velocity.z = 150.0f;
|
||||||
harpoonItem->Status = ITEM_ACTIVE;
|
harpoonItem->Pose.Orientation.x = 0;
|
||||||
}
|
harpoonItem->Pose.Orientation.y = yRot;
|
||||||
|
|
||||||
|
AddActiveItem(harpoonItemNumber);
|
||||||
|
harpoonItem->Status = ITEM_ACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScubaHarpoonControl(short itemNumber)
|
void ScubaHarpoonControl(short itemNumber)
|
||||||
|
@ -91,14 +90,7 @@ namespace TEN::Entities::TR3
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int ox = item->Pose.Position.x;
|
TranslateItem(item, item->Pose.Orientation, item->Animation.Velocity.z);
|
||||||
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);
|
|
||||||
|
|
||||||
auto probe = GetCollision(item);
|
auto probe = GetCollision(item);
|
||||||
|
|
||||||
|
@ -123,7 +115,8 @@ namespace TEN::Entities::TR3
|
||||||
short head = 0;
|
short head = 0;
|
||||||
short neck = 0;
|
short neck = 0;
|
||||||
|
|
||||||
int waterHeight;
|
int waterHeight = 0;
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
if (item->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
if (item->Animation.ActiveState != SDIVER_STATE_DEATH)
|
if (item->Animation.ActiveState != SDIVER_STATE_DEATH)
|
||||||
|
@ -140,22 +133,23 @@ namespace TEN::Entities::TR3
|
||||||
GetCreatureMood(item, &AI, false);
|
GetCreatureMood(item, &AI, false);
|
||||||
CreatureMood(item, &AI, false);
|
CreatureMood(item, &AI, false);
|
||||||
|
|
||||||
GameVector origin;
|
|
||||||
GameVector target;
|
|
||||||
bool shoot = false;
|
bool shoot = false;
|
||||||
|
|
||||||
if (Lara.Control.WaterStatus == WaterStatus::Dry)
|
if (Lara.Control.WaterStatus == WaterStatus::Dry)
|
||||||
{
|
{
|
||||||
origin.x = item->Pose.Position.x;
|
auto origin = GameVector(
|
||||||
origin.y = item->Pose.Position.y - CLICK(1);
|
item->Pose.Position.x,
|
||||||
origin.z = item->Pose.Position.z;
|
item->Pose.Position.y - CLICK(1),
|
||||||
origin.roomNumber = item->RoomNumber;
|
item->Pose.Position.z,
|
||||||
|
item->RoomNumber
|
||||||
target.x = LaraItem->Pose.Position.x;
|
);
|
||||||
target.y = LaraItem->Pose.Position.y - (LARA_HEIGHT - 150);
|
auto target = GameVector(
|
||||||
target.z = LaraItem->Pose.Position.z;
|
LaraItem->Pose.Position.x,
|
||||||
|
LaraItem->Pose.Position.y - (LARA_HEIGHT - 150),
|
||||||
|
LaraItem->Pose.Position.z
|
||||||
|
);
|
||||||
|
|
||||||
shoot = LOS(&origin, &target);
|
shoot = LOS(&origin, &target);
|
||||||
|
|
||||||
if (shoot)
|
if (shoot)
|
||||||
creature->Target = LaraItem->Pose.Position;
|
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))
|
else if (AI.angle > -ANGLE(45.0f) && AI.angle < ANGLE(45.0f))
|
||||||
{
|
{
|
||||||
origin.x = item->Pose.Position.x;
|
auto origin = GameVector(item->Pose.Position, item->RoomNumber);
|
||||||
origin.y = item->Pose.Position.y;
|
auto target = GameVector(LaraItem->Pose.Position);
|
||||||
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;
|
|
||||||
|
|
||||||
shoot = LOS(&origin, &target);
|
shoot = LOS(&origin, &target);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
shoot = false;
|
|
||||||
|
|
||||||
angle = CreatureTurn(item, creature->MaxTurn);
|
angle = CreatureTurn(item, creature->MaxTurn);
|
||||||
waterHeight = GetWaterSurface(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber) + SECTOR(0.5f);
|
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;
|
break;
|
||||||
|
|
||||||
case SDIVER_STATE_SWIM_AIM:
|
case SDIVER_STATE_SWIM_AIM:
|
||||||
creature->Flags = NULL;
|
creature->Flags = 0;
|
||||||
|
|
||||||
if (shoot)
|
if (shoot)
|
||||||
neck = -AI.angle;
|
neck = -AI.angle;
|
||||||
|
@ -270,7 +256,6 @@ namespace TEN::Entities::TR3
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
void ScubaHarpoonControl(short itemNumber);
|
void ScubaHarpoonControl(short itemNumber);
|
||||||
void ScubaControl(short itemNumber);
|
void ScubaControl(short itemNumber);
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
using namespace TEN::Math::Random;
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
constexpr auto SHIVA_GRAB_ATTACK_DAMAGE = 150;
|
constexpr auto SHIVA_GRAB_ATTACK_DAMAGE = 150;
|
||||||
constexpr auto SHIVA_DOWNWARD_ATTACK_DAMAGE = 180;
|
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_WALK_TURN_RATE_MAX ANGLE(4.0f)
|
||||||
#define SHIVA_ATTACK_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 ShivaBiteLeft = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 13);
|
||||||
const auto ShivaBiteRight = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 22);
|
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
|
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 dx = LaraItem->Pose.Position.x - x;
|
||||||
long dz = LaraItem->Pose.Position.z - z;
|
long dz = LaraItem->Pose.Position.z - z;
|
||||||
|
@ -169,7 +169,7 @@ namespace TEN::Entities::TR3
|
||||||
sptr->dSize = size;
|
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))
|
if (!(creature->Flags) && item->TestBits(JointBitType::Touch, ShivaAttackRightJoints))
|
||||||
{
|
{
|
||||||
|
@ -208,14 +208,15 @@ namespace TEN::Entities::TR3
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* shiva = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
bool laraAlive = LaraItem->HitPoints > 0;
|
auto pos = Vector3i(0, 0, CLICK(1));
|
||||||
|
bool isLaraAlive = LaraItem->HitPoints > 0;
|
||||||
|
|
||||||
EulerAngles extraHeadRot;
|
|
||||||
EulerAngles extraTorsoRot;
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
short tilt = 0;
|
short tilt = 0;
|
||||||
|
EulerAngles extraHeadRot = EulerAngles::Zero;
|
||||||
|
EulerAngles extraTorsoRot = EulerAngles::Zero;
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
if (item->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
|
@ -234,13 +235,13 @@ namespace TEN::Entities::TR3
|
||||||
GetCreatureMood(item, &AI, true);
|
GetCreatureMood(item, &AI, true);
|
||||||
CreatureMood(item, &AI, true);
|
CreatureMood(item, &AI, true);
|
||||||
|
|
||||||
if (shiva->Mood == MoodType::Escape)
|
if (creature->Mood == MoodType::Escape)
|
||||||
{
|
{
|
||||||
shiva->Target.x = LaraItem->Pose.Position.x;
|
creature->Target.x = LaraItem->Pose.Position.x;
|
||||||
shiva->Target.z = LaraItem->Pose.Position.z;
|
creature->Target.z = LaraItem->Pose.Position.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
angle = CreatureTurn(item, shiva->MaxTurn);
|
angle = CreatureTurn(item, creature->MaxTurn);
|
||||||
|
|
||||||
if (item->Animation.ActiveState != SHIVA_STATE_INACTIVE)
|
if (item->Animation.ActiveState != SHIVA_STATE_INACTIVE)
|
||||||
item->MeshBits = ALL_JOINT_BITS;
|
item->MeshBits = ALL_JOINT_BITS;
|
||||||
|
@ -251,15 +252,15 @@ namespace TEN::Entities::TR3
|
||||||
switch (item->Animation.ActiveState)
|
switch (item->Animation.ActiveState)
|
||||||
{
|
{
|
||||||
case SHIVA_STATE_INACTIVE:
|
case SHIVA_STATE_INACTIVE:
|
||||||
shiva->MaxTurn = 0;
|
creature->MaxTurn = 0;
|
||||||
|
|
||||||
if (!shiva->Flags)
|
if (!creature->Flags)
|
||||||
{
|
{
|
||||||
if (!item->MeshBits)
|
if (!item->MeshBits)
|
||||||
effectMesh = 0;
|
effectMesh = 0;
|
||||||
|
|
||||||
item->MeshBits = (item->MeshBits * 2) + 1;
|
item->MeshBits = (item->MeshBits * 2) + 1;
|
||||||
shiva->Flags = 1;
|
creature->Flags = 1;
|
||||||
|
|
||||||
pos = GetJointPosition(item, effectMesh++, pos);
|
pos = GetJointPosition(item, effectMesh++, pos);
|
||||||
TriggerExplosionSparks(pos.x, pos.y, pos.z, 2, 0, 0, item->RoomNumber);
|
TriggerExplosionSparks(pos.x, pos.y, pos.z, 2, 0, 0, item->RoomNumber);
|
||||||
|
@ -267,45 +268,45 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
shiva->Flags--;
|
creature->Flags--;
|
||||||
|
|
||||||
if (item->MeshBits == 0x7FFFFFFF)
|
if (item->MeshBits == 0x7FFFFFFF)
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
||||||
shiva->Flags = -45;
|
creature->Flags = -45;
|
||||||
effectMesh = 0;
|
effectMesh = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SHIVA_STATE_IDLE:
|
case SHIVA_STATE_IDLE:
|
||||||
shiva->MaxTurn = 0;
|
creature->MaxTurn = 0;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
extraHeadRot.y = AI.angle;
|
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);
|
TriggerShivaSmoke(item->Pose.Position.x + (GetRandomControl() & 0x5FF) - 0x300, pos.y - (GetRandomControl() & 0x5FF), item->Pose.Position.z + (GetRandomControl() & 0x5FF) - 0x300, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shiva->Flags == 1)
|
if (creature->Flags == 1)
|
||||||
shiva->Flags = 0;
|
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 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));
|
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;
|
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;
|
item->Animation.TargetState = SHIVA_STATE_WALK_BACK;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = SHIVA_STATE_GUARD_IDLE;
|
item->Animation.TargetState = SHIVA_STATE_GUARD_IDLE;
|
||||||
}
|
}
|
||||||
else if (shiva->Mood == MoodType::Bored)
|
else if (creature->Mood == MoodType::Bored)
|
||||||
{
|
{
|
||||||
if (TestProbability(0.0325f))
|
if (TestProbability(0.0325f))
|
||||||
item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD;
|
item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD;
|
||||||
|
@ -313,17 +314,17 @@ namespace TEN::Entities::TR3
|
||||||
else if (AI.bite && AI.distance < pow(SECTOR(1.25f), 2))
|
else if (AI.bite && AI.distance < pow(SECTOR(1.25f), 2))
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = SHIVA_STATE_GRAB_ATTACK;
|
item->Animation.TargetState = SHIVA_STATE_GRAB_ATTACK;
|
||||||
shiva->Flags = 0;
|
creature->Flags = 0;
|
||||||
}
|
}
|
||||||
else if (AI.bite && AI.distance < pow(SECTOR(4) / 3, 2))
|
else if (AI.bite && AI.distance < pow(SECTOR(4) / 3, 2))
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = SHIVA_STATE_DOWNWARD_ATTACK;
|
item->Animation.TargetState = SHIVA_STATE_DOWNWARD_ATTACK;
|
||||||
shiva->Flags = 0;
|
creature->Flags = 0;
|
||||||
}
|
}
|
||||||
else if (item->HitStatus && AI.ahead)
|
else if (item->HitStatus && AI.ahead)
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = SHIVA_STATE_GUARD_IDLE;
|
item->Animation.TargetState = SHIVA_STATE_GUARD_IDLE;
|
||||||
shiva->Flags = 4;
|
creature->Flags = 4;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD;
|
item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD;
|
||||||
|
@ -331,103 +332,103 @@ namespace TEN::Entities::TR3
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SHIVA_STATE_GUARD_IDLE:
|
case SHIVA_STATE_GUARD_IDLE:
|
||||||
shiva->MaxTurn = 0;
|
creature->MaxTurn = 0;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
extraHeadRot.y = AI.angle;
|
extraHeadRot.y = AI.angle;
|
||||||
|
|
||||||
if (item->HitStatus || shiva->Mood == MoodType::Escape)
|
if (item->HitStatus || creature->Mood == MoodType::Escape)
|
||||||
shiva->Flags = 4;
|
creature->Flags = 4;
|
||||||
|
|
||||||
if (AI.bite && AI.distance < pow(SECTOR(4) / 3, 2) ||
|
if (AI.bite && AI.distance < pow(SECTOR(4) / 3, 2) ||
|
||||||
(item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase &&
|
(item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase &&
|
||||||
!shiva->Flags) ||
|
!creature->Flags) ||
|
||||||
!AI.ahead)
|
!AI.ahead)
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
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;
|
item->Animation.TargetState = SHIVA_STATE_GUARD_IDLE;
|
||||||
|
|
||||||
|
|
||||||
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase &&
|
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase &&
|
||||||
shiva->Flags > 1)
|
creature->Flags > 1)
|
||||||
{
|
{
|
||||||
shiva->Flags -= 2;
|
creature->Flags -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SHIVA_STATE_WALK_FORWARD:
|
case SHIVA_STATE_WALK_FORWARD:
|
||||||
shiva->MaxTurn = SHIVA_WALK_TURN_RATE_MAX;
|
creature->MaxTurn = SHIVA_WALK_TURN_RATE_MAX;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
extraHeadRot.y = AI.angle;
|
extraHeadRot.y = AI.angle;
|
||||||
|
|
||||||
if (shiva->Mood == MoodType::Escape)
|
if (creature->Mood == MoodType::Escape)
|
||||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
||||||
else if (shiva->Mood == MoodType::Bored)
|
else if (creature->Mood == MoodType::Bored)
|
||||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
||||||
else if (AI.bite && AI.distance < pow(SECTOR(4) / 3, 2))
|
else if (AI.bite && AI.distance < pow(SECTOR(4) / 3, 2))
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
||||||
shiva->Flags = 0;
|
creature->Flags = 0;
|
||||||
}
|
}
|
||||||
else if (item->HitStatus)
|
else if (item->HitStatus)
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD_GUARDING;
|
item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD_GUARDING;
|
||||||
shiva->Flags = 4;
|
creature->Flags = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SHIVA_STATE_WALK_FORWARD_GUARDING:
|
case SHIVA_STATE_WALK_FORWARD_GUARDING:
|
||||||
shiva->MaxTurn = SHIVA_WALK_TURN_RATE_MAX;
|
creature->MaxTurn = SHIVA_WALK_TURN_RATE_MAX;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
extraHeadRot.y = AI.angle;
|
extraHeadRot.y = AI.angle;
|
||||||
|
|
||||||
if (item->HitStatus)
|
if (item->HitStatus)
|
||||||
shiva->Flags = 4;
|
creature->Flags = 4;
|
||||||
|
|
||||||
if (AI.bite && AI.distance < pow(SECTOR(1.25f), 2) ||
|
if (AI.bite && AI.distance < pow(SECTOR(1.25f), 2) ||
|
||||||
(item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase &&
|
(item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase &&
|
||||||
!shiva->Flags))
|
!creature->Flags))
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD;
|
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;
|
item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD_GUARDING;
|
||||||
|
|
||||||
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase)
|
if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase)
|
||||||
shiva->Flags = 0;
|
creature->Flags = 0;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SHIVA_STATE_WALK_BACK:
|
case SHIVA_STATE_WALK_BACK:
|
||||||
shiva->MaxTurn = SHIVA_WALK_TURN_RATE_MAX;
|
creature->MaxTurn = SHIVA_WALK_TURN_RATE_MAX;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
extraHeadRot.y = AI.angle;
|
extraHeadRot.y = AI.angle;
|
||||||
|
|
||||||
if (AI.ahead && AI.distance < pow(SECTOR(4) / 3, 2) ||
|
if (AI.ahead && AI.distance < pow(SECTOR(4) / 3, 2) ||
|
||||||
(item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase &&
|
(item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase &&
|
||||||
!shiva->Flags))
|
!creature->Flags))
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
||||||
}
|
}
|
||||||
else if (item->HitStatus)
|
else if (item->HitStatus)
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
item->Animation.TargetState = SHIVA_STATE_IDLE;
|
||||||
shiva->Flags = 4;
|
creature->Flags = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SHIVA_STATE_GRAB_ATTACK:
|
case SHIVA_STATE_GRAB_ATTACK:
|
||||||
shiva->MaxTurn = SHIVA_ATTACK_TURN_RATE_MAX;
|
creature->MaxTurn = SHIVA_ATTACK_TURN_RATE_MAX;
|
||||||
|
|
||||||
if (AI.ahead)
|
if (AI.ahead)
|
||||||
{
|
{
|
||||||
|
@ -435,22 +436,22 @@ namespace TEN::Entities::TR3
|
||||||
extraTorsoRot = EulerAngles(AI.xAngle, AI.angle, 0);
|
extraTorsoRot = EulerAngles(AI.xAngle, AI.angle, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShivaDamage(item, shiva, SHIVA_GRAB_ATTACK_DAMAGE);
|
ShivaDamage(item, creature, SHIVA_GRAB_ATTACK_DAMAGE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SHIVA_STATE_DOWNWARD_ATTACK:
|
case SHIVA_STATE_DOWNWARD_ATTACK:
|
||||||
shiva->MaxTurn = SHIVA_ATTACK_TURN_RATE_MAX;
|
creature->MaxTurn = SHIVA_ATTACK_TURN_RATE_MAX;
|
||||||
extraHeadRot.y = AI.angle;
|
extraHeadRot.y = AI.angle;
|
||||||
extraTorsoRot.y = AI.angle;
|
extraTorsoRot.y = AI.angle;
|
||||||
|
|
||||||
if (AI.xAngle > 0)
|
if (AI.xAngle > 0)
|
||||||
extraTorsoRot.x = AI.xAngle;
|
extraTorsoRot.x = AI.xAngle;
|
||||||
|
|
||||||
ShivaDamage(item, shiva, SHIVA_DOWNWARD_ATTACK_DAMAGE);
|
ShivaDamage(item, creature, SHIVA_DOWNWARD_ATTACK_DAMAGE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SHIVA_STATE_KILL:
|
case SHIVA_STATE_KILL:
|
||||||
shiva->MaxTurn = 0;
|
creature->MaxTurn = 0;
|
||||||
extraHeadRot = EulerAngles::Zero;
|
extraHeadRot = EulerAngles::Zero;
|
||||||
extraTorsoRot = EulerAngles::Zero;
|
extraTorsoRot = EulerAngles::Zero;
|
||||||
|
|
||||||
|
@ -466,23 +467,21 @@ namespace TEN::Entities::TR3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch kill animation
|
// Dispatch kill animation.
|
||||||
if (laraAlive && LaraItem->HitPoints <= 0)
|
if (isLaraAlive && LaraItem->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = SHIVA_STATE_KILL;
|
item->Animation.TargetState = SHIVA_STATE_KILL;
|
||||||
|
|
||||||
if (LaraItem->RoomNumber != item->RoomNumber)
|
if (LaraItem->RoomNumber != item->RoomNumber)
|
||||||
ItemNewRoom(Lara.ItemNumber, item->RoomNumber);
|
ItemNewRoom(Lara.ItemNumber, item->RoomNumber);
|
||||||
|
|
||||||
LaraItem->Pose.Position = item->Pose.Position;
|
|
||||||
LaraItem->Pose.Orientation = EulerAngles(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.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + LARA_ANIM_SHIVA_DEATH;
|
||||||
LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase;
|
LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase;
|
||||||
LaraItem->Animation.ActiveState = LS_DEATH;
|
LaraItem->Animation.ActiveState = LS_DEATH;
|
||||||
LaraItem->Animation.TargetState = LS_DEATH;
|
LaraItem->Animation.TargetState = LS_DEATH;
|
||||||
|
LaraItem->Animation.IsAirborne = false;
|
||||||
|
LaraItem->Pose.Position = item->Pose.Position;
|
||||||
|
LaraItem->Pose.Orientation = EulerAngles(0, item->Pose.Orientation.y, 0);
|
||||||
LaraItem->HitPoints = NOT_TARGETABLE;
|
LaraItem->HitPoints = NOT_TARGETABLE;
|
||||||
Lara.Air = -1;
|
Lara.Air = -1;
|
||||||
Lara.Control.HandStatus = HandStatus::Special;
|
Lara.Control.HandStatus = HandStatus::Special;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
void InitialiseShiva(short itemNumber);
|
void InitialiseShiva(short itemNumber);
|
||||||
void ShivaControl(short itemNumber);
|
void ShivaControl(short itemNumber);
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "Sound/sound.h"
|
#include "Sound/sound.h"
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
static BOSS_STRUCT BossData;
|
static BOSS_STRUCT BossData;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Game/items.h"
|
#include "Game/items.h"
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
void ControlLaserBolts(short itemNumber);
|
void ControlLaserBolts(short itemNumber);
|
||||||
void ControlLondBossPlasmaBall(short fxNumber);
|
void ControlLondBossPlasmaBall(short fxNumber);
|
||||||
|
|
|
@ -15,23 +15,52 @@
|
||||||
using namespace TEN::Math::Random;
|
using namespace TEN::Math::Random;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
constexpr auto TIGER_ATTACK_DAMAGE = 90;
|
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 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 };
|
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
|
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
|
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)
|
void TigerControl(short itemNumber)
|
||||||
|
@ -40,20 +69,16 @@ namespace TEN::Entities::TR3
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto* item = &g_Level.Items[itemNumber];
|
auto* item = &g_Level.Items[itemNumber];
|
||||||
auto* info = GetCreatureInfo(item);
|
auto* creature = GetCreatureInfo(item);
|
||||||
|
|
||||||
short head = 0;
|
|
||||||
short angle = 0;
|
short angle = 0;
|
||||||
short tilt = 0;
|
short tilt = 0;
|
||||||
|
auto extraHeadRot = EulerAngles::Zero;
|
||||||
|
|
||||||
if (item->HitPoints <= 0)
|
if (item->HitPoints <= 0)
|
||||||
{
|
{
|
||||||
if (item->Animation.ActiveState != 9)
|
if (item->Animation.ActiveState != TIGER_STATE_DEATH)
|
||||||
{
|
SetAnimation(item, TIGER_ANIM_DEATH);
|
||||||
item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 11;
|
|
||||||
item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase;
|
|
||||||
item->Animation.ActiveState = 9;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -61,99 +86,105 @@ namespace TEN::Entities::TR3
|
||||||
CreatureAIInfo(item, &AI);
|
CreatureAIInfo(item, &AI);
|
||||||
|
|
||||||
if (AI.ahead)
|
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)
|
if (creature->Alerted && AI.zoneNumber != AI.enemyZone)
|
||||||
info->Mood = MoodType::Escape;
|
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)
|
switch (item->Animation.ActiveState)
|
||||||
{
|
{
|
||||||
case 1:
|
case TIGER_STATE_IDLE:
|
||||||
info->MaxTurn = 0;
|
creature->MaxTurn = 0;
|
||||||
info->Flags = 0;
|
creature->Flags = 0;
|
||||||
|
|
||||||
if (info->Mood == MoodType::Escape)
|
if (creature->Mood == MoodType::Escape)
|
||||||
{
|
{
|
||||||
if (Lara.TargetEntity != item && AI.ahead)
|
if (Lara.TargetEntity != item && AI.ahead)
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = TIGER_STATE_IDLE;
|
||||||
else
|
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))
|
if (TestProbability(TIGER_ROAR_CHANCE))
|
||||||
item->Animation.TargetState = 5;
|
item->Animation.TargetState = TIGER_STATE_ROAR;
|
||||||
else if (TestProbability(0.035f))
|
else if (TestProbability(TIGER_WALK_CHANCE))
|
||||||
item->Animation.TargetState = 2;
|
item->Animation.TargetState = TIGER_STATE_WALK_FORWARD;
|
||||||
}
|
}
|
||||||
else if (AI.bite && AI.distance < pow(340, 2))
|
else if (AI.bite && AI.distance < TIGER_BITE_ATTACK_RANGE)
|
||||||
item->Animation.TargetState = 6;
|
item->Animation.TargetState = TIGER_STATE_BITE_ATTACK;
|
||||||
else if (AI.bite && AI.distance < pow(SECTOR(1), 2))
|
else if (AI.bite && AI.distance < TIGER_POUNCE_ATTACK_RANGE)
|
||||||
{
|
{
|
||||||
info->MaxTurn = ANGLE(3.0f);
|
item->Animation.TargetState = TIGER_STATE_POUNCE_ATTACK;
|
||||||
item->Animation.TargetState = 8;
|
creature->MaxTurn = TIGER_POUNCE_ATTACK_TURN_RATE_MAX;
|
||||||
}
|
}
|
||||||
else if (item->Animation.RequiredState)
|
else if (item->Animation.RequiredState)
|
||||||
item->Animation.TargetState = item->Animation.RequiredState;
|
item->Animation.TargetState = item->Animation.RequiredState;
|
||||||
else if (info->Mood != MoodType::Attack && TestProbability(0.003f))
|
else if (creature->Mood != MoodType::Attack && TestProbability(TIGER_ROAR_CHANCE))
|
||||||
item->Animation.TargetState = 5;
|
item->Animation.TargetState = TIGER_STATE_ROAR;
|
||||||
else
|
else
|
||||||
item->Animation.TargetState = 3;
|
item->Animation.TargetState = TIGER_STATE_RUN_FORWARD;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case TIGER_STATE_WALK_FORWARD:
|
||||||
info->MaxTurn = ANGLE(3.0f);
|
creature->MaxTurn = TIGER_WALK_TURN_RATE_MAX;
|
||||||
|
|
||||||
if (info->Mood == MoodType::Escape || info->Mood == MoodType::Attack)
|
if (creature->Mood == MoodType::Escape ||
|
||||||
item->Animation.TargetState = 3;
|
creature->Mood == MoodType::Attack)
|
||||||
else if (TestProbability(0.003f))
|
|
||||||
{
|
{
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = TIGER_STATE_RUN_FORWARD;
|
||||||
item->Animation.RequiredState = 5;
|
}
|
||||||
|
else if (TestProbability(TIGER_ROAR_CHANCE))
|
||||||
|
{
|
||||||
|
item->Animation.TargetState = TIGER_STATE_IDLE;
|
||||||
|
item->Animation.RequiredState = TIGER_STATE_ROAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case TIGER_STATE_RUN_FORWARD:
|
||||||
info->MaxTurn = ANGLE(6.0f);
|
creature->MaxTurn = TIGER_RUN_TURN_RATE_MAX;
|
||||||
|
|
||||||
if (info->Mood == MoodType::Bored)
|
if (creature->Mood == MoodType::Bored)
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = TIGER_STATE_IDLE;
|
||||||
else if (info->Flags && AI.ahead)
|
else if (creature->Flags && AI.ahead)
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = TIGER_STATE_IDLE;
|
||||||
else if (AI.bite && AI.distance < pow(SECTOR(1.5f), 2))
|
else if (AI.bite && AI.distance < TIGER_RUN_ATTACK_RANGE)
|
||||||
{
|
{
|
||||||
if (LaraItem->Animation.Velocity.z == 0)
|
if (LaraItem->Animation.Velocity.z == 0.0f)
|
||||||
item->Animation.TargetState = 1;
|
item->Animation.TargetState = TIGER_STATE_IDLE;
|
||||||
else
|
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.TargetState = TIGER_STATE_IDLE;
|
||||||
item->Animation.RequiredState = 5;
|
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;
|
break;
|
||||||
|
|
||||||
case 6:
|
case TIGER_STATE_BITE_ATTACK:
|
||||||
case 7:
|
case TIGER_STATE_RUN_SWIPE_ATTACK:
|
||||||
case 8:
|
case TIGER_STATE_POUNCE_ATTACK:
|
||||||
if (!info->Flags && item->TestBits(JointBitType::Touch, TigerAttackJoints))
|
if (!creature->Flags && item->TestBits(JointBitType::Touch, TigerAttackJoints))
|
||||||
{
|
{
|
||||||
DoDamage(info->Enemy, TIGER_ATTACK_DAMAGE);
|
DoDamage(creature->Enemy, TIGER_ATTACK_DAMAGE);
|
||||||
CreatureEffect(item, TigerBite, DoBloodSplat);
|
CreatureEffect(item, TigerBite, DoBloodSplat);
|
||||||
info->Flags = 1;
|
creature->Flags = 1; // 1 = is attacking.
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -161,7 +192,7 @@ namespace TEN::Entities::TR3
|
||||||
}
|
}
|
||||||
|
|
||||||
CreatureTilt(item, tilt);
|
CreatureTilt(item, tilt);
|
||||||
CreatureJoint(item, 0, head);
|
CreatureJoint(item, 0, extraHeadRot.y);
|
||||||
CreatureAnimation(itemNumber, angle, tilt);
|
CreatureAnimation(itemNumber, angle, tilt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
void TigerControl(short itemNumber);
|
void TigerControl(short itemNumber);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
using namespace TEN::Effects::Lara;
|
using namespace TEN::Effects::Lara;
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
static BOSS_STRUCT BossData;
|
static BOSS_STRUCT BossData;
|
||||||
|
|
||||||
|
@ -452,7 +452,7 @@ namespace TEN::Entities::TR3
|
||||||
|
|
||||||
if (!Lara.Burn)
|
if (!Lara.Burn)
|
||||||
{
|
{
|
||||||
if (ItemNearLara(&fx->pos, 200))
|
if (ItemNearLara(&fx->pos.Position, 200))
|
||||||
{
|
{
|
||||||
LaraItem->HitStatus = true;
|
LaraItem->HitStatus = true;
|
||||||
KillEffect(fxNumber);
|
KillEffect(fxNumber);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Game/items.h"
|
#include "Game/items.h"
|
||||||
|
|
||||||
namespace TEN::Entities::TR3
|
namespace TEN::Entities::Creatures::TR3
|
||||||
{
|
{
|
||||||
void InitialiseTony(short itemNumber);
|
void InitialiseTony(short itemNumber);
|
||||||
void TonyControl(short itemNumber);
|
void TonyControl(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