TombEngine/TR5Main/Specific/level.cpp

1331 lines
32 KiB
C++
Raw Normal View History

#include "framework.h"
2021-09-19 18:29:25 +03:00
#include "animation.h"
#include "level.h"
#include "setup.h"
#include "animation.h"
2021-09-19 23:41:26 +03:00
#include "control/lot.h"
#include "Lara.h"
#include "savegame.h"
#include "spotcam.h"
#include "camera.h"
2021-09-19 23:41:26 +03:00
#include "control/control.h"
#include "pickup.h"
2021-09-19 23:41:26 +03:00
#include "control/volume.h"
2021-09-05 05:52:50 +02:00
#include "generic_doors.h"
2021-09-19 23:41:26 +03:00
#include "control/box.h"
#include "sound.h"
#include "GameFlowScript.h"
#include <process.h>
#include <zlib.h>
2021-08-28 13:27:58 +02:00
#include "Renderer11.h"
2021-09-24 07:53:42 +02:00
#include "item.h"
2021-09-25 11:27:47 +02:00
2021-08-30 18:03:21 +03:00
using TEN::Renderer::g_Renderer;
using std::vector;
using std::string;
2021-09-05 05:52:50 +02:00
using namespace TEN::Entities::Doors;
2020-07-03 07:05:33 +02:00
FILE* LevelFilePtr;
uintptr_t hLoadLevel;
unsigned int ThreadId;
int IsLevelLoading;
bool g_FirstLevel = true;
vector<int> MoveablesIds;
vector<int> StaticObjectsIds;
char* LevelDataPtr;
ChunkReader* g_levelChunkIO;
LEVEL g_Level;
short ReadInt8()
{
byte value = *(byte*)LevelDataPtr;
LevelDataPtr += 1;
return value;
}
short ReadInt16()
{
short value = *(short*)LevelDataPtr;
LevelDataPtr += 2;
return value;
}
unsigned short ReadUInt16()
{
unsigned short value = *(unsigned short*)LevelDataPtr;
LevelDataPtr += 2;
return value;
}
int ReadInt32()
{
int value = *(int*)LevelDataPtr;
LevelDataPtr += 4;
return value;
}
2020-06-20 21:20:36 +02:00
float ReadFloat()
{
float value = *(float*)LevelDataPtr;
LevelDataPtr += 4;
return value;
}
Vector2 ReadVector2()
{
Vector2 value;
value.x = ReadFloat();
value.y = ReadFloat();
return value;
}
Vector3 ReadVector3()
{
Vector3 value;
value.x = ReadFloat();
value.y = ReadFloat();
value.z = ReadFloat();
return value;
}
void ReadBytes(void* dest, int count)
{
memcpy(dest, LevelDataPtr, count);
LevelDataPtr += count;
}
int LoadItems()
{
g_Level.NumItems = ReadInt32();
if (g_Level.NumItems == 0)
return false;
g_Level.Items.resize(NUM_ITEMS);
InitialiseItemArray(NUM_ITEMS);
if (g_Level.NumItems > 0)
{
for (int i = 0; i < g_Level.NumItems; i++)
{
ITEM_INFO* item = &g_Level.Items[i];
item->objectNumber = from_underlying(ReadInt16());
item->roomNumber = ReadInt16();
item->pos.xPos = ReadInt32();
item->pos.yPos = ReadInt32();
item->pos.zPos = ReadInt32();
item->pos.yRot = ReadInt16();
item->shade = ReadInt16();
item->triggerFlags = ReadInt16();
item->flags = ReadInt16();
byte numBytes = ReadInt8();
char buffer[255];
ReadBytes(buffer, numBytes);
item->luaName = std::string(buffer, buffer + numBytes);
2021-08-27 18:49:14 +01:00
g_GameScript->AddName(item->luaName, i);
memcpy(&item->startPos, &item->pos, sizeof(PHD_3DPOS));
}
for (int i = 0; i < g_Level.NumItems; i++)
InitialiseItem(i);
}
2021-01-17 20:29:37 -03:00
for (auto& r : g_Level.Rooms)
{
2021-01-17 20:29:37 -03:00
for (const auto& mesh : r.mesh)
{
FLOOR_INFO* floor = &r.floor[((mesh.pos.zPos - r.z) / 1024) + r.xSize * ((mesh.pos.xPos - r.x) / 1024)];
2021-09-13 02:46:48 +03:00
if (floor->Box == NO_BOX)
2021-01-17 20:29:37 -03:00
continue;
2021-09-13 02:46:48 +03:00
if (!(g_Level.Boxes[floor->Box].flags & BLOCKED))
{
int fl = floor->FloorHeight(mesh.pos.xPos, mesh.pos.zPos);
2021-01-17 20:29:37 -03:00
STATIC_INFO* st = &StaticObjects[mesh.staticNumber];
if (fl <= mesh.pos.yPos - st->collisionBox.Y2 + 512 && fl < mesh.pos.yPos - st->collisionBox.Y1)
{
2020-07-25 18:02:35 +02:00
if (st->collisionBox.X1 == 0 || st->collisionBox.X2 == 0 ||
st->collisionBox.Z1 == 0 || st->collisionBox.Z2 == 0 ||
2020-07-26 16:17:54 +02:00
((st->collisionBox.X1 < 0) ^ (st->collisionBox.X2 < 0)) &&
((st->collisionBox.Z1 < 0) ^ (st->collisionBox.Z2 < 0)))
{
floor->Stopper = true;
}
}
}
}
}
return true;
}
void LoadObjects()
{
std::memset(Objects, 0, sizeof(OBJECT_INFO) * ID_NUMBER_OBJECTS);
std::memset(StaticObjects, 0, sizeof(STATIC_INFO) * MAX_STATICS);
2020-07-03 07:05:33 +02:00
int numMeshes = ReadInt32();
g_Level.Meshes.reserve(numMeshes);
2020-07-03 07:05:33 +02:00
for (int i = 0; i < numMeshes; i++)
{
MESH mesh;
mesh.sphere.Center.x = ReadFloat();
mesh.sphere.Center.y = ReadFloat();
mesh.sphere.Center.z = ReadFloat();
mesh.sphere.Radius = ReadFloat();
2020-07-03 07:05:33 +02:00
int numVertices = ReadInt32();
mesh.positions.resize(numVertices);
ReadBytes(mesh.positions.data(), 12 * numVertices);
mesh.colors.resize(numVertices);
ReadBytes(mesh.colors.data(), 12 * numVertices);
mesh.effects.resize(numVertices);
ReadBytes(mesh.effects.data(), 12 * numVertices);
mesh.bones.resize(numVertices);
ReadBytes(mesh.bones.data(), 4 * numVertices);
2020-07-03 07:05:33 +02:00
int numBuckets = ReadInt32();
mesh.buckets.reserve(numBuckets);
for (int j = 0; j < numBuckets; j++)
{
BUCKET bucket;
2020-07-03 07:05:33 +02:00
bucket.texture = ReadInt32();
bucket.blendMode = ReadInt8();
bucket.animated = ReadInt8();
bucket.numQuads = 0;
bucket.numTriangles = 0;
int numPolygons = ReadInt32();
bucket.polygons.reserve(numPolygons);
for (int k = 0; k < numPolygons; k++)
{
POLYGON poly;
poly.shape = ReadInt32();
poly.animatedSequence = ReadInt32();
poly.animatedFrame = ReadInt32();
int count = (poly.shape == 0 ? 4 : 3);
poly.indices.resize(count);
poly.textureCoordinates.resize(count);
poly.normals.resize(count);
poly.tangents.resize(count);
poly.bitangents.resize(count);
for (int n = 0; n < count; n++)
poly.indices[n] = ReadInt32();
for (int n = 0; n < count; n++)
poly.textureCoordinates[n] = ReadVector2();
for (int n = 0; n < count; n++)
poly.normals[n] = ReadVector3();
for (int n = 0; n < count; n++)
poly.tangents[n] = ReadVector3();
for (int n = 0; n < count; n++)
poly.bitangents[n] = ReadVector3();
bucket.polygons.push_back(poly);
if (poly.shape == 0)
bucket.numQuads++;
else
bucket.numTriangles++;
}
2020-07-03 07:05:33 +02:00
mesh.buckets.push_back(bucket);
}
g_Level.Meshes.push_back(mesh);
2020-07-03 07:05:33 +02:00
}
int numAnimations = ReadInt32();
g_Level.Anims.resize(numAnimations);
2020-07-25 18:02:35 +02:00
for (int i = 0; i < numAnimations; i++)
{
ANIM_STRUCT* anim = &g_Level.Anims[i];
2020-07-25 18:02:35 +02:00
anim->framePtr = ReadInt32();
anim->interpolation = ReadInt16();
anim->currentAnimState = ReadInt16();
anim->velocity = ReadInt32();
anim->acceleration = ReadInt32();
anim->Xvelocity = ReadInt32();
anim->Xacceleration = ReadInt32();
anim->frameBase = ReadInt16();
anim->frameEnd = ReadInt16();
anim->jumpAnimNum = ReadInt16();
anim->jumpFrameNum = ReadInt16();
anim->numberChanges = ReadInt16();
anim->changeIndex = ReadInt16();
anim->numberCommands = ReadInt16();
anim->commandIndex = ReadInt16();
}
int numChanges = ReadInt32();
g_Level.Changes.resize(numChanges);
ReadBytes(g_Level.Changes.data(), sizeof(CHANGE_STRUCT) * numChanges);
int numRanges = ReadInt32();
g_Level.Ranges.resize(numRanges);
ReadBytes(g_Level.Ranges.data(), sizeof(RANGE_STRUCT) * numRanges);
int numCommands = ReadInt32();
g_Level.Commands.resize(numCommands);
ReadBytes(g_Level.Commands.data(), sizeof(short) * numCommands);
int numBones = ReadInt32();
g_Level.Bones.resize(numBones);
ReadBytes(g_Level.Bones.data(), 4 * numBones);
int numFrames = ReadInt32();
g_Level.Frames.resize(numFrames);
2020-07-25 18:02:35 +02:00
for (int i = 0; i < numFrames; i++)
{
ANIM_FRAME* frame = &g_Level.Frames[i];
frame->boundingBox.X1 = ReadInt16();
frame->boundingBox.X2 = ReadInt16();
frame->boundingBox.Y1 = ReadInt16();
frame->boundingBox.Y2 = ReadInt16();
frame->boundingBox.Z1 = ReadInt16();
frame->boundingBox.Z2 = ReadInt16();
2020-07-26 07:21:38 +02:00
frame->offsetX = ReadInt16();
frame->offsetY = ReadInt16();
frame->offsetZ = ReadInt16();
2020-07-25 18:02:35 +02:00
int numAngles = ReadInt16();
frame->angles.resize(numAngles);
for (int j = 0; j < numAngles; j++)
{
Quaternion* q = &frame->angles[j];
q->x = ReadFloat();
q->y = ReadFloat();
q->z = ReadFloat();
q->w = ReadFloat();
}
}
//ReadBytes(g_Level.Frames.data(), sizeof(ANIM_FRAME) * numFrames);
int numModels = ReadInt32();
for (int i = 0; i < numModels; i++)
{
int objNum = ReadInt32();
MoveablesIds.push_back(objNum);
Objects[objNum].loaded = true;
2020-07-03 07:05:33 +02:00
Objects[objNum].nmeshes = (short)ReadInt16();
Objects[objNum].meshIndex = (short)ReadInt16();
Objects[objNum].boneIndex = ReadInt32();
2020-07-25 18:02:35 +02:00
Objects[objNum].frameBase = ReadInt32();
2020-07-03 07:05:33 +02:00
Objects[objNum].animIndex = (short)ReadInt16();
ReadInt16();
Objects[objNum].loaded = true;
}
InitialiseObjects();
int numStatics = ReadInt32();
for (int i = 0; i < numStatics; i++)
{
int meshID = ReadInt32();
StaticObjectsIds.push_back(meshID);
2020-07-26 07:21:38 +02:00
StaticObjects[meshID].meshNumber = (short)ReadInt32();
2020-07-25 18:02:35 +02:00
StaticObjects[meshID].visibilityBox.X1 = ReadInt16();
StaticObjects[meshID].visibilityBox.X2 = ReadInt16();
StaticObjects[meshID].visibilityBox.Y1 = ReadInt16();
StaticObjects[meshID].visibilityBox.Y2 = ReadInt16();
StaticObjects[meshID].visibilityBox.Z1 = ReadInt16();
StaticObjects[meshID].visibilityBox.Z2 = ReadInt16();
StaticObjects[meshID].collisionBox.X1 = ReadInt16();
StaticObjects[meshID].collisionBox.X2 = ReadInt16();
StaticObjects[meshID].collisionBox.Y1 = ReadInt16();
StaticObjects[meshID].collisionBox.Y2 = ReadInt16();
StaticObjects[meshID].collisionBox.Z1 = ReadInt16();
StaticObjects[meshID].collisionBox.Z2 = ReadInt16();
2020-07-03 07:05:33 +02:00
StaticObjects[meshID].flags = (short)ReadInt16();
StaticObjects[meshID].shatterType = (short)ReadInt16();
StaticObjects[meshID].shatterSound = (short)ReadInt16();
}
// HACK: to remove after decompiling LoadSprites
MoveablesIds.push_back(ID_DEFAULT_SPRITES);
}
void LoadCameras()
{
2020-07-11 21:16:04 +02:00
int numCameras = ReadInt32();
g_Level.Cameras.reserve(numCameras);
for (int i = 0; i < numCameras; i++)
{
auto & camera = g_Level.Cameras.emplace_back();
camera.x = ReadInt32();
camera.y = ReadInt32();
camera.z = ReadInt32();
camera.roomNumber = ReadInt32();
camera.flags = ReadInt32();
2021-09-11 18:59:14 +03:00
camera.speed = ReadInt32();
byte numBytes = ReadInt8();
char buffer[255];
ReadBytes(buffer, numBytes);
camera.luaName = std::string(buffer, buffer + numBytes);
2021-08-27 18:49:14 +01:00
g_GameScript->AddName(camera.luaName, camera);
}
NumberSpotcams = ReadInt32();
if (NumberSpotcams != 0)
{
ReadBytes(SpotCam, NumberSpotcams * sizeof(SPOTCAM));
}
int numSinks = ReadInt32();
g_Level.Sinks.reserve(numSinks);
for (int i = 0; i < numSinks; i++)
{
auto & sink = g_Level.Sinks.emplace_back();
sink.x = ReadInt32();
sink.y = ReadInt32();
sink.z = ReadInt32();
sink.strength = ReadInt32();
sink.boxIndex = ReadInt32();
byte numBytes = ReadInt8();
char buffer[255];
ReadBytes(buffer, numBytes);
sink.luaName = std::string(buffer, buffer+numBytes);
2021-08-27 18:49:14 +01:00
g_GameScript->AddName(sink.luaName, sink);
}
}
void LoadTextures()
{
printf("LoadTextures\n");
int size;
2020-07-26 07:21:38 +02:00
int numTextures = ReadInt32();
g_Level.RoomTextures.reserve(numTextures);
2020-06-20 21:20:36 +02:00
for (int i = 0; i < numTextures; i++)
{
TEXTURE texture;
2020-07-26 07:21:38 +02:00
texture.width = ReadInt32();
texture.height = ReadInt32();
2020-07-26 07:21:38 +02:00
size = ReadInt32();
texture.colorMapData.resize(size);
2020-07-26 07:21:38 +02:00
ReadBytes(texture.colorMapData.data(), size);
2020-07-26 07:21:38 +02:00
byte hasNormalMap = ReadInt8();
if (hasNormalMap)
{
2020-07-26 07:21:38 +02:00
size = ReadInt32();
texture.normalMapData.resize(size);
2020-07-26 07:21:38 +02:00
ReadBytes(texture.normalMapData.data(), size);
}
2020-06-20 21:20:36 +02:00
g_Level.RoomTextures.push_back(texture);
2020-06-20 21:20:36 +02:00
}
2020-07-26 07:21:38 +02:00
numTextures = ReadInt32();
g_Level.MoveablesTextures.reserve(numTextures);
2020-06-20 21:20:36 +02:00
for (int i = 0; i < numTextures; i++)
{
TEXTURE texture;
2020-07-26 07:21:38 +02:00
texture.width = ReadInt32();
texture.height = ReadInt32();
2020-07-26 07:21:38 +02:00
size = ReadInt32();
texture.colorMapData.resize(size);
2020-07-26 07:21:38 +02:00
ReadBytes(texture.colorMapData.data(), size);
2020-07-26 07:21:38 +02:00
bool hasNormalMap = ReadInt8();
if (hasNormalMap)
{
2020-07-26 07:21:38 +02:00
size = ReadInt32();
texture.normalMapData.resize(size);
2020-07-26 07:21:38 +02:00
ReadBytes(texture.normalMapData.data(), size);
}
2020-06-20 21:20:36 +02:00
g_Level.MoveablesTextures.push_back(texture);
2020-06-20 21:20:36 +02:00
}
2020-07-26 07:21:38 +02:00
numTextures = ReadInt32();
g_Level.StaticsTextures.reserve(numTextures);
2020-06-20 21:20:36 +02:00
for (int i = 0; i < numTextures; i++)
{
TEXTURE texture;
2020-07-26 07:21:38 +02:00
texture.width = ReadInt32();
texture.height = ReadInt32();
size = ReadInt32();
texture.colorMapData.resize(size);
2020-07-26 07:21:38 +02:00
ReadBytes(texture.colorMapData.data(), size);
2020-07-26 07:21:38 +02:00
bool hasNormalMap = ReadInt8();
if (hasNormalMap)
{
2020-07-26 07:21:38 +02:00
size = ReadInt32();
texture.normalMapData.resize(size);
2020-07-26 07:21:38 +02:00
ReadBytes(texture.normalMapData.data(), size);
}
2020-06-20 21:20:36 +02:00
g_Level.StaticsTextures.push_back(texture);
2020-06-20 21:20:36 +02:00
}
numTextures = ReadInt32();
g_Level.AnimatedTextures.reserve(numTextures);
for (int i = 0; i < numTextures; i++)
{
TEXTURE texture;
texture.width = ReadInt32();
texture.height = ReadInt32();
size = ReadInt32();
texture.colorMapData.resize(size);
ReadBytes(texture.colorMapData.data(), size);
bool hasNormalMap = ReadInt8();
if (hasNormalMap)
{
size = ReadInt32();
texture.normalMapData.resize(size);
ReadBytes(texture.normalMapData.data(), size);
}
g_Level.AnimatedTextures.push_back(texture);
}
2020-07-26 07:21:38 +02:00
numTextures = ReadInt32();
g_Level.SpritesTextures.reserve(numTextures);
2020-06-20 21:20:36 +02:00
for (int i = 0; i < numTextures; i++)
{
TEXTURE texture;
2020-07-26 07:21:38 +02:00
texture.width = ReadInt32();
texture.height = ReadInt32();
size = ReadInt32();
texture.colorMapData.resize(size);
2020-07-26 07:21:38 +02:00
ReadBytes(texture.colorMapData.data(), size);
2020-06-20 21:20:36 +02:00
g_Level.SpritesTextures.push_back(texture);
2020-06-20 21:20:36 +02:00
}
2021-09-09 13:39:25 +03:00
g_Level.SkyTexture.width = ReadInt32();
g_Level.SkyTexture.height = ReadInt32();
2020-07-26 07:21:38 +02:00
size = ReadInt32();
2021-09-09 13:39:25 +03:00
g_Level.SkyTexture.colorMapData.resize(size);
ReadBytes(g_Level.SkyTexture.colorMapData.data(), size);
}
void ReadRooms()
{
int numRooms = ReadInt32();
printf("NumRooms: %d\n", numRooms);
for (int i = 0; i < numRooms; i++)
{
auto & room = g_Level.Rooms.emplace_back();
room.x = ReadInt32();
room.y = 0;
room.z = ReadInt32();
room.minfloor = ReadInt32();
room.maxceiling = ReadInt32();
2020-06-28 15:51:33 +02:00
int numVertices = ReadInt32();
room.positions.reserve(numVertices);
for (int j = 0; j < numVertices; j++)
room.positions.push_back(ReadVector3());
room.colors.reserve(numVertices);
for (int j = 0; j < numVertices; j++)
room.colors.push_back(ReadVector3());
room.effects.reserve(numVertices);
for (int j = 0; j < numVertices; j++)
room.effects.push_back(ReadVector3());
2020-06-28 15:51:33 +02:00
int numBuckets = ReadInt32();
room.buckets.reserve(numBuckets);
for (int j = 0; j < numBuckets; j++)
{
2020-06-28 15:51:33 +02:00
BUCKET bucket;
2020-06-28 15:51:33 +02:00
bucket.texture = ReadInt32();
bucket.blendMode = ReadInt8();
bucket.animated = ReadInt8();
bucket.numQuads = 0;
bucket.numTriangles = 0;
2020-06-28 15:51:33 +02:00
int numPolygons = ReadInt32();
bucket.polygons.reserve(numPolygons);
for (int k = 0; k < numPolygons; k++)
{
POLYGON poly;
2020-10-07 09:08:23 +02:00
poly.shape = ReadInt32();
poly.animatedSequence = ReadInt32();
poly.animatedFrame = ReadInt32();
int count = (poly.shape == 0 ? 4 : 3);
poly.indices.resize(count);
poly.textureCoordinates.resize(count);
poly.normals.resize(count);
poly.tangents.resize(count);
poly.bitangents.resize(count);
for (int n = 0; n < count; n++)
poly.indices[n] = ReadInt32();
for (int n = 0; n < count; n++)
poly.textureCoordinates[n] = ReadVector2();
for (int n = 0; n < count; n++)
poly.normals[n] = ReadVector3();
for (int n = 0; n < count; n++)
poly.tangents[n] = ReadVector3();
for (int n = 0; n < count; n++)
poly.bitangents[n] = ReadVector3();
bucket.polygons.push_back(poly);
if (poly.shape == 0)
bucket.numQuads++;
else
bucket.numTriangles++;
}
2020-06-28 15:51:33 +02:00
room.buckets.push_back(bucket);
}
2020-06-28 15:51:33 +02:00
int numPortals = ReadInt32();
for (int j = 0; j < numPortals; j++)
{
ROOM_DOOR door;
door.room = ReadInt16();
door.normal.x = ReadInt16();
door.normal.y = ReadInt16();
door.normal.z = ReadInt16();
for (int k = 0; k < 4; k++)
{
door.vertices[k].x = ReadInt16();
door.vertices[k].y = ReadInt16();
door.vertices[k].z = ReadInt16();
}
room.doors.push_back(door);
}
2020-06-28 15:51:33 +02:00
room.xSize = ReadInt32();
room.ySize = ReadInt32();
room.floor.reserve(room.xSize * room.ySize);
for (int j = 0; j < room.xSize * room.ySize; j++)
{
FLOOR_INFO floor;
floor.TriggerIndex = ReadInt32();
2021-09-13 02:46:48 +03:00
floor.Box = ReadInt32();
floor.Material = ReadInt32();
floor.Stopper = ReadInt32();
2021-08-20 02:01:50 +03:00
2020-09-14 00:19:05 -03:00
floor.FloorCollision.SplitAngle = ReadFloat();
floor.FloorCollision.Portals[0] = ReadInt32();
floor.FloorCollision.Portals[1] = ReadInt32();
floor.FloorCollision.Planes[0].x = ReadFloat();
floor.FloorCollision.Planes[0].y = ReadFloat();
floor.FloorCollision.Planes[0].z = ReadFloat();
floor.FloorCollision.Planes[1].x = ReadFloat();
floor.FloorCollision.Planes[1].y = ReadFloat();
floor.FloorCollision.Planes[1].z = ReadFloat();
floor.CeilingCollision.SplitAngle = ReadFloat();
floor.CeilingCollision.Portals[0] = ReadInt32();
floor.CeilingCollision.Portals[1] = ReadInt32();
floor.CeilingCollision.Planes[0].x = ReadFloat();
floor.CeilingCollision.Planes[0].y = ReadFloat();
floor.CeilingCollision.Planes[0].z = ReadFloat();
floor.CeilingCollision.Planes[1].x = ReadFloat();
floor.CeilingCollision.Planes[1].y = ReadFloat();
floor.CeilingCollision.Planes[1].z = ReadFloat();
floor.WallPortal = ReadInt32();
2021-08-20 02:01:50 +03:00
floor.Flags.Death = ReadInt8();
floor.Flags.Monkeyswing = ReadInt8();
floor.Flags.ClimbNorth = ReadInt8();
floor.Flags.ClimbSouth = ReadInt8();
floor.Flags.ClimbEast = ReadInt8();
floor.Flags.ClimbWest = ReadInt8();
floor.Flags.MarkTriggerer = ReadInt8();
floor.Flags.MarkTriggererActive = 0; // TODO: IT NEEDS TO BE WRITTEN/READ FROM SAVEGAMES!
floor.Flags.MarkBeetle = ReadInt8();
2020-09-19 20:09:57 -03:00
floor.Room = i;
room.floor.push_back(floor);
}
room.ambient.x = ReadFloat();
room.ambient.y = ReadFloat();
room.ambient.z = ReadFloat();
2020-06-28 15:51:33 +02:00
int numLights = ReadInt32();
room.lights.reserve(numLights);
for (int j = 0; j < numLights; j++)
{
ROOM_LIGHT light;
2020-07-26 16:17:54 +02:00
light.x = ReadInt32();
light.y = ReadInt32();
light.z = ReadInt32();
light.dx = ReadFloat();
light.dy = ReadFloat();
light.dz = ReadFloat();
light.r = ReadFloat();
light.g = ReadFloat();
light.b = ReadFloat();
2020-07-26 16:17:54 +02:00
light.intensity = ReadFloat();
light.in = ReadFloat();
light.out = ReadFloat();
2020-07-26 16:17:54 +02:00
light.length = ReadFloat();
light.cutoff = ReadFloat();
light.type = ReadInt8();
2020-07-26 16:17:54 +02:00
light.castShadows = ReadInt8();
room.lights.push_back(light);
}
2020-06-28 15:51:33 +02:00
int numStatics = ReadInt32();
room.mesh.reserve(numStatics);
for (int j = 0; j < numStatics; j++)
{
auto & mesh = room.mesh.emplace_back();
mesh.pos.xPos = ReadInt32();
mesh.pos.yPos = ReadInt32();
mesh.pos.zPos = ReadInt32();
mesh.pos.xRot = 0;
mesh.pos.yRot = ReadUInt16();
mesh.pos.zRot = 0;
mesh.flags = ReadUInt16();
2020-10-07 09:08:23 +02:00
Vector3 rgb = ReadVector3();
float a = ReadFloat();
mesh.staticNumber = ReadUInt16();
2020-10-07 09:08:23 +02:00
mesh.color = Vector4(rgb.x, rgb.y, rgb.z, a);
mesh.hitPoints = ReadInt16();
byte numBytes = ReadInt8();
char buffer[255];
ReadBytes(buffer, numBytes);
mesh.luaName = std::string(buffer, buffer + numBytes);
2021-08-27 18:49:14 +01:00
g_GameScript->AddName(mesh.luaName, mesh);
}
2021-07-05 16:33:50 +02:00
int numTriggerVolumes = ReadInt32();
for (int j = 0; j < numTriggerVolumes; j++)
{
TRIGGER_VOLUME volume;
2021-08-05 15:46:03 +03:00
volume.type = (TriggerVolumeType)ReadInt32();
2021-07-15 10:56:03 +02:00
volume.position.x = ReadFloat();
volume.position.y = ReadFloat();
volume.position.z = ReadFloat();
volume.rotation.x = ReadFloat();
volume.rotation.y = ReadFloat();
volume.rotation.z = ReadFloat();
volume.rotation.w = ReadFloat();
volume.scale.x = ReadFloat();
volume.scale.y = ReadFloat();
volume.scale.z = ReadFloat();
2021-07-05 16:33:50 +02:00
volume.activators = ReadInt32();
byte numBytes = ReadInt8();
char buffer[255];
2021-07-05 16:33:50 +02:00
ReadBytes(buffer, numBytes);
volume.onEnter = std::string(buffer, buffer+numBytes);
2021-07-05 16:33:50 +02:00
numBytes = ReadInt8();
ReadBytes(buffer, numBytes);
volume.onInside = std::string(buffer, buffer+numBytes);
2021-07-05 16:33:50 +02:00
numBytes = ReadInt8();
ReadBytes(buffer, numBytes);
volume.onLeave = std::string(buffer, buffer+numBytes);
2021-07-05 16:33:50 +02:00
volume.oneShot = ReadInt8();
volume.status = TS_OUTSIDE;
2021-08-05 15:46:03 +03:00
volume.box = BoundingOrientedBox(volume.position, volume.scale, volume.rotation);
volume.sphere = BoundingSphere(volume.position, volume.scale.x);
2021-07-05 16:33:50 +02:00
room.triggerVolumes.push_back(volume);
}
2020-06-28 15:51:33 +02:00
room.flippedRoom = ReadInt32();
room.flags = ReadInt32();
room.meshEffect = ReadInt32();
room.reverbType = ReadInt32();
room.flipNumber = ReadInt32();
room.itemNumber = NO_ITEM;
room.fxNumber = NO_ITEM;
}
}
void LoadRooms()
{
printf("LoadRooms\n");
Wibble = 0;
//RoomLightsCount = 0;
2020-04-24 19:15:05 +02:00
//Unk_007E7FE8 = 0;
ReadRooms();
BuildOutsideRoomsTable();
int numFloorData = ReadInt32();
g_Level.FloorData.resize(numFloorData);
ReadBytes(g_Level.FloorData.data(), numFloorData * sizeof(short));
}
void FreeLevel()
{
g_Level.RoomTextures.resize(0);
g_Level.MoveablesTextures.resize(0);
g_Level.StaticsTextures.resize(0);
g_Level.AnimatedTextures.resize(0);
g_Level.SpritesTextures.resize(0);
g_Level.AnimatedTexturesSequences.resize(0);
g_Level.Rooms.resize(0);
g_Level.ObjectTextures.resize(0);
g_Level.Bones.resize(0);
g_Level.Meshes.resize(0);
MoveablesIds.resize(0);
g_Level.Boxes.resize(0);
g_Level.Overlaps.resize(0);
g_Level.Anims.resize(0);
g_Level.Changes.resize(0);
g_Level.Ranges.resize(0);
g_Level.Commands.resize(0);
g_Level.Frames.resize(0);
g_Level.Sprites.resize(0);
g_Level.SoundDetails.resize(0);
g_Level.SoundMap.resize(0);
g_Level.FloorData.resize(0);
g_Level.Cameras.resize(0);
g_Level.Sinks.resize(0);
g_Level.SoundSources.resize(0);
g_Level.AIObjects.resize(0);
2020-07-11 21:16:04 +02:00
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 4; j++)
{
g_Level.Zones[j][i].clear();
2020-07-11 21:16:04 +02:00
}
}
2020-08-09 15:25:56 +02:00
g_Renderer.freeRendererData();
g_GameScript->FreeLevelScripts();
}
size_t ReadFileEx(void* ptr, size_t size, size_t count, FILE* stream)
{
_lock_file(stream);
size_t result = fread(ptr, size, count, stream);
_unlock_file(stream);
return result;
}
void LoadSoundEffects()
{
int numSoundSources = ReadInt32();
g_Level.SoundSources.reserve(numSoundSources);
for (int i = 0; i < numSoundSources; i++)
{
auto & source = g_Level.SoundSources.emplace_back(SOUND_SOURCE_INFO{});
source.x = ReadInt32();
source.y = ReadInt32();
source.z = ReadInt32();
source.soundId = ReadInt32();
source.flags = ReadInt32();
byte numBytes = ReadInt8();
char buffer[255];
ReadBytes(buffer, numBytes);
source.luaName = std::string(buffer, buffer+numBytes);
2021-08-27 18:49:14 +01:00
g_GameScript->AddName(source.luaName, source);
}
}
void LoadAnimatedTextures()
{
int numAnimatedTextures = ReadInt32();
for (int i = 0; i < numAnimatedTextures; i++)
{
ANIMATED_TEXTURES_SEQUENCE sequence;
sequence.atlas = ReadInt32();
sequence.numFrames = ReadInt32();
for (int j = 0; j < sequence.numFrames; j++)
{
ANIMATED_TEXTURES_FRAME frame;
frame.x1 = ReadFloat();
frame.y1 = ReadFloat();
frame.x2 = ReadFloat();
frame.y2 = ReadFloat();
frame.x3 = ReadFloat();
frame.y3 = ReadFloat();
frame.x4 = ReadFloat();
frame.y4 = ReadFloat();
sequence.frames.push_back(frame);
}
g_Level.AnimatedTexturesSequences.push_back(sequence);
}
// Unused for now
int nAnimUVRanges = ReadInt8();
}
void LoadTextureInfos()
{
ReadInt32(); // TEX/0
int numObjectTextures = ReadInt32();
for (int i = 0; i < numObjectTextures; i++)
{
OBJECT_TEXTURE texture;
texture.attribute = ReadInt32();
texture.tileAndFlag = ReadInt32();
texture.newFlags = ReadInt32();
for (int j = 0; j < 4; j++)
{
texture.vertices[j].x = ReadFloat();
texture.vertices[j].y = ReadFloat();
}
texture.destination = ReadInt32();
g_Level.ObjectTextures.push_back(texture);
}
}
void LoadAIObjects()
{
2020-07-11 21:16:04 +02:00
int nAIObjects = ReadInt32();
g_Level.AIObjects.reserve(nAIObjects);
for (int i = 0; i < nAIObjects; i++)
{
auto & obj = g_Level.AIObjects.emplace_back();
obj.objectNumber = (GAME_OBJECT_ID)ReadInt16();
obj.roomNumber = ReadInt16();
obj.x = ReadInt32();
obj.y = ReadInt32();
obj.z = ReadInt32();
obj.triggerFlags = ReadInt16();
obj.flags = ReadInt16();
obj.yRot = ReadInt16();
obj.boxNumber = ReadInt16();
byte numBytes = ReadInt8();
char buffer[255];
ReadBytes(buffer, numBytes);
obj.luaName = std::string(buffer, buffer+numBytes);
2021-08-27 18:49:14 +01:00
g_GameScript->AddName(obj.luaName, obj);
}
}
FILE* FileOpen(const char* fileName)
{
FILE* ptr = fopen(fileName, "rb");
return ptr;
}
void FileClose(FILE* ptr)
{
fclose(ptr);
}
2020-07-26 07:21:38 +02:00
bool Decompress(byte* dest, byte* src, unsigned long compressedSize, unsigned long uncompressedSize)
{
2020-07-26 07:21:38 +02:00
z_stream strm;
ZeroMemory(&strm, sizeof(z_stream));
strm.avail_in = compressedSize;
strm.avail_out = uncompressedSize;
strm.next_out = (BYTE*)dest;
strm.next_in = (BYTE*)src;
2020-07-26 07:21:38 +02:00
inflateInit(&strm);
inflate(&strm, Z_FULL_FLUSH);
2020-07-26 07:21:38 +02:00
if (strm.total_out == uncompressedSize)
{
2020-07-26 07:21:38 +02:00
inflateEnd(&strm);
return true;
}
else
{
return false;
}
}
2020-05-31 09:39:32 +02:00
bool replace(std::string& str, const std::string& from, const std::string& to) {
size_t start_pos = str.find(from);
if (start_pos == std::string::npos)
return false;
str.replace(start_pos, from.length(), to);
return true;
}
unsigned CALLBACK LoadLevel(void* data)
{
printf("LoadLevel\n");
char* filename = (char*)data;
LevelDataPtr = NULL;
LevelFilePtr = NULL;
2020-07-26 07:21:38 +02:00
char* baseLevelDataPtr = NULL;
2020-08-09 15:25:56 +02:00
g_Renderer.updateProgress(0);
LevelFilePtr = FileOpen(filename);
if (LevelFilePtr)
{
int version;
int uncompressedSize;
int compressedSize;
2020-07-26 07:21:38 +02:00
char* compressedBuffer;
2020-07-26 07:21:38 +02:00
// Read file header
ReadFileEx(&version, 1, 4, LevelFilePtr);
ReadFileEx(&uncompressedSize, 1, 4, LevelFilePtr);
ReadFileEx(&compressedSize, 1, 4, LevelFilePtr);
2020-07-26 07:21:38 +02:00
// The entire level is ZLIB compressed
compressedBuffer = (char*)malloc(compressedSize);
LevelDataPtr = (char*)malloc(uncompressedSize);
2020-07-26 07:21:38 +02:00
baseLevelDataPtr = LevelDataPtr;
ReadFileEx(compressedBuffer, compressedSize, 1, LevelFilePtr);
Decompress((byte*)LevelDataPtr, (byte*)compressedBuffer, compressedSize, uncompressedSize);
// Now the entire level is decompressed
free(compressedBuffer);
LoadTextures();
2020-08-09 15:25:56 +02:00
g_Renderer.updateProgress(20);
2020-07-26 07:21:38 +02:00
WeatherType = ReadInt8();
LaraDrawType = ReadInt8();
LoadRooms();
2020-08-09 15:25:56 +02:00
g_Renderer.updateProgress(40);
LoadObjects();
2020-08-09 15:25:56 +02:00
g_Renderer.updateProgress(50);
LoadSprites();
LoadCameras();
LoadSoundEffects();
2020-08-09 15:25:56 +02:00
g_Renderer.updateProgress(60);
LoadBoxes();
2020-07-11 21:16:04 +02:00
//InitialiseLOTarray(true);
2020-07-11 21:16:04 +02:00
LoadAnimatedTextures();
LoadTextureInfos();
2020-08-09 15:25:56 +02:00
g_Renderer.updateProgress(70);
LoadItems();
LoadAIObjects();
LoadSamples();
2020-08-09 15:25:56 +02:00
g_Renderer.updateProgress(80);
2020-07-26 07:21:38 +02:00
free(baseLevelDataPtr);
LevelDataPtr = NULL;
FileClose(LevelFilePtr);
}
else
{
return false;
}
2020-07-26 07:21:38 +02:00
2020-08-09 15:25:56 +02:00
g_Renderer.updateProgress(90);
g_Renderer.PrepareDataForTheRenderer();
// Initialise the game
GameScriptLevel* level = g_GameFlow->GetLevel(CurrentLevel);
InitialiseGameFlags();
InitialiseLara(!(InitialiseGame || CurrentLevel == 1));
GetCarriedItems();
GetAIPickups();
Lara.Vehicle = -1;
g_GameScript->AssignItemsAndLara();
// Level loaded
IsLevelLoading = false;
2020-08-09 15:25:56 +02:00
g_Renderer.updateProgress(100);
_endthreadex(1);
return true;
}
void LoadSamples()
{
int SoundMapSize = ReadInt16();
g_Level.SoundMap.resize(SoundMapSize);
ReadBytes(g_Level.SoundMap.data(), SoundMapSize * sizeof(short));
int numSamplesInfos = ReadInt32();
if (numSamplesInfos)
{
g_Level.SoundDetails.resize(numSamplesInfos);
ReadBytes(g_Level.SoundDetails.data(), numSamplesInfos * sizeof(SAMPLE_INFO));
2020-09-08 19:50:19 +02:00
int numSamples = ReadInt32();
if (numSamples <= 0)
return;
2020-09-08 19:50:19 +02:00
int uncompressedSize;
int compressedSize;
char* buffer = (char*)malloc(2 * 1048576);
2020-09-08 19:50:19 +02:00
for (int i = 0; i < numSamples; i++)
{
uncompressedSize = ReadInt32();
compressedSize = ReadInt32();
ReadBytes(buffer, compressedSize);
Sound_LoadSample(buffer, compressedSize, uncompressedSize, i);
}
2020-09-08 19:50:19 +02:00
free(buffer);
}
else
{
//Log(1, aNog_Level.SoundDetailss);
}
}
void LoadBoxes()
{
// Read boxes
2020-07-11 21:16:04 +02:00
int numBoxes = ReadInt32();
g_Level.Boxes.resize(numBoxes);
ReadBytes(g_Level.Boxes.data(), numBoxes * sizeof(BOX_INFO));
// Read overlaps
2020-07-11 21:16:04 +02:00
int numOverlaps = ReadInt32();
g_Level.Overlaps.resize(numOverlaps);
ReadBytes(g_Level.Overlaps.data(), numOverlaps * sizeof(OVERLAP));
// Read zones
for (int i = 0; i < 2; i++)
{
// Ground zones
for (int j = 0; j < MAX_ZONES - 1; j++)
{
g_Level.Zones[j][i].resize(numBoxes * sizeof(int));
ReadBytes(g_Level.Zones[j][i].data(), numBoxes * sizeof(int));
}
// Fly zone
g_Level.Zones[MAX_ZONES - 1][i].resize(numBoxes * sizeof(int));
ReadBytes(g_Level.Zones[MAX_ZONES - 1][i].data(), numBoxes * sizeof(int));
}
// By default all blockable boxes are blocked
2020-07-11 21:16:04 +02:00
for (int i = 0; i < numBoxes; i++)
{
if (g_Level.Boxes[i].flags & BLOCKABLE)
{
g_Level.Boxes[i].flags |= BLOCKED;
}
}
}
int S_LoadLevelFile(int levelIndex)
{
//DB_Log(2, "S_LoadLevelFile - DLL");
printf("S_LoadLevelFile\n");
Sound_Stop();
Sound_FreeSamples();
if (!g_FirstLevel)
FreeLevel();
g_FirstLevel = false;
char filename[80];
GameScriptLevel* level = g_GameFlow->Levels[levelIndex];
strcpy_s(filename, level->FileName.c_str());
// Loading level is done is two threads, one for loading level and one for drawing loading screen
IsLevelLoading = true;
hLoadLevel = _beginthreadex(0, 0, LoadLevel, filename, 0, &ThreadId);
// This function loops until progress is 100%. Not very thread safe, but behaviour should be predictable.
2020-06-21 11:51:46 +02:00
wchar_t loadscreenFileName[80];
std::mbstowcs(loadscreenFileName, level->LoadScreenFileName.c_str(),80);
std::wstring loadScreenFile = std::wstring(loadscreenFileName);
2020-08-09 15:25:56 +02:00
g_Renderer.renderLoadingScreen(loadScreenFile);
while (IsLevelLoading);
return true;
}
void LoadSprites()
{
//DB_Log(2, "LoadSprites");
ReadInt32(); // SPR\0
2020-07-11 21:16:04 +02:00
int numSprites = ReadInt32();
g_Level.Sprites.resize(numSprites);
2020-07-11 21:16:04 +02:00
for (int i = 0; i < numSprites; i++)
{
SPRITE* spr = &g_Level.Sprites[i];
spr->tile = ReadInt32();
spr->x1 = ReadFloat();
spr->y1 = ReadFloat();
spr->x2 = ReadFloat();
spr->y2 = ReadFloat();
spr->x3 = ReadFloat();
spr->y3 = ReadFloat();
spr->x4 = ReadFloat();
spr->y4 = ReadFloat();
}
g_Level.NumSpritesSequences = ReadInt32();
for (int i = 0; i < g_Level.NumSpritesSequences; i++)
{
int spriteID = ReadInt32();
short negLength = ReadInt16();
short offset = ReadInt16();
if (spriteID >= ID_NUMBER_OBJECTS)
{
StaticObjects[spriteID - ID_NUMBER_OBJECTS].meshNumber = offset;
}
else
{
Objects[spriteID].nmeshes = negLength;
Objects[spriteID].meshIndex = offset;
Objects[spriteID].loaded = true;
}
}
}
void GetCarriedItems()
{
int i;
ITEM_INFO* item, *item2;
short linknum;
for (i = 0; i < g_Level.NumItems; ++i)
g_Level.Items[i].carriedItem = NO_ITEM;
2020-07-11 21:16:04 +02:00
for (i = 0; i < g_Level.NumItems; ++i)
{
item = &g_Level.Items[i];
if (Objects[item->objectNumber].intelligent || item->objectNumber >= ID_SEARCH_OBJECT1 && item->objectNumber <= ID_SEARCH_OBJECT3)
{
for (linknum = g_Level.Rooms[item->roomNumber].itemNumber; linknum != NO_ITEM; linknum = g_Level.Items[linknum].nextItem)
{
item2 = &g_Level.Items[linknum];
if (abs(item2->pos.xPos - item->pos.xPos) < 512
&& abs(item2->pos.zPos - item->pos.zPos) < 512
&& abs(item2->pos.yPos - item->pos.yPos) < 256
&& Objects[item2->objectNumber].isPickup)
{
item2->carriedItem = item->carriedItem;
item->carriedItem = linknum;
RemoveDrawnItem(linknum);
item2->roomNumber = NO_ROOM;
}
}
}
}
}
void GetAIPickups()
{
int i, num;
ITEM_INFO* item;
AI_OBJECT* object;
for (i = 0; i < g_Level.NumItems; ++i)
{
item = &g_Level.Items[i];
if (Objects[item->objectNumber].intelligent)
{
item->aiBits = 0;
for (num = 0; num < g_Level.AIObjects.size(); ++num)
{
object = &g_Level.AIObjects[num];
if (abs(object->x - item->pos.xPos) < 512
&& abs(object->z - item->pos.zPos) < 512
&& object->roomNumber == item->roomNumber
&& object->objectNumber < ID_AI_PATROL2)
{
item->aiBits = (1 << object->objectNumber - ID_AI_GUARD) & 0x1F;
item->itemFlags[3] = object->triggerFlags;
if (object->objectNumber != ID_AI_GUARD)
object->roomNumber = NO_ROOM;
}
}
item->TOSSPAD |= item->aiBits << 8 | (char) item->itemFlags[3];
}
}
}
void BuildOutsideRoomsTable()
{
for (int x = 0; x < OUTSIDE_SIZE; x++)
for (int z = 0; z < OUTSIDE_SIZE; z++)
OutsideRoomTable[x][z].clear();
for (int x = 0; x < OUTSIDE_SIZE; x++)
{
for (int z = 0; z < OUTSIDE_SIZE; z++)
{
for (int i = 0; i < g_Level.Rooms.size(); i++)
{
ROOM_INFO* r = &g_Level.Rooms[i];
int rx = (r->x / 1024);
int rz = (r->z / 1024);
if (x >= rx + 1 && z >= rz + 1 && x <= (rx + r->ySize - 2) && z <= (rz + r->xSize - 2))
OutsideRoomTable[x][z].push_back(i);
}
}
}
}