TombEngine/TR5Main/Specific/level.cpp

1143 lines
27 KiB
C++
Raw Normal View History

#include "framework.h"
#include "level.h"
#include "setup.h"
#include "draw.h"
#include "lot.h"
#include "Lara.h"
#include "savegame.h"
#include "spotcam.h"
#include "camera.h"
#include "control.h"
#include "pickup.h"
#include "door.h"
#include "box.h"
#include "sound.h"
#include "GameFlowScript.h"
2020-07-03 07:05:33 +02:00
using T5M::Renderer::g_Renderer;
using std::vector;
using std::string;
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;
extern GameFlow* g_GameFlow;
extern GameScript* g_GameScript;
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);
InitialiseClosedDoors();
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 = 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();
memcpy(&item->startPos, &item->pos, sizeof(PHD_3DPOS));
}
for (int i = 0; i < g_Level.NumItems; i++)
InitialiseItem(i);
}
for (int r = 0; r < g_Level.Rooms.size(); r++)
{
MESH_INFO* mesh = g_Level.Rooms[r].mesh.data();
for (int m = 0; m < g_Level.Rooms[r].mesh.size(); m++)
{
2020-07-26 16:17:54 +02:00
FLOOR_INFO* floor = &g_Level.Rooms[r].floor[((mesh->z - g_Level.Rooms[r].z) / 1024) +
g_Level.Rooms[r].xSize * ((mesh->x - g_Level.Rooms[r].x) / 1024)];
if (!(g_Level.Boxes[floor->box].flags & BLOCKED)
&& !(CurrentLevel == 5 && (r == 19 || r == 23 || r == 16)))
{
2020-07-26 16:17:54 +02:00
int fl = floor->floor * 4;
2020-07-25 18:02:35 +02:00
STATIC_INFO* st = &StaticObjects[mesh->staticNumber];
if (fl <= mesh->y - st->collisionBox.Y2 + 512 && fl < mesh->y - 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)))
{
2020-07-26 16:17:54 +02:00
floor->stopper = true;
}
}
}
}
}
return true;
}
void LoadObjects()
{
2020-07-07 07:32:33 +02:00
memset(Objects, 0, sizeof(OBJECT_INFO) * ID_NUMBER_OBJECTS);
2020-07-25 18:02:35 +02:00
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.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();
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];;
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();
InitialiseClosedDoors();
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].shatterDamage = (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();
FixedCameras.resize(numCameras);
ReadBytes(FixedCameras.data(), numCameras * sizeof(OBJECT_VECTOR));
NumberSpotcams = ReadInt32();
if (NumberSpotcams != 0)
{
ReadBytes(SpotCam, NumberSpotcams * sizeof(SPOTCAM));
}
}
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
}
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
}
2020-07-26 07:21:38 +02:00
g_Level.MiscTextures.width = ReadInt32();
g_Level.MiscTextures.height = ReadInt32();
size = ReadInt32();
g_Level.MiscTextures.colorMapData.resize(size);
2020-07-26 07:21:38 +02:00
ReadBytes(g_Level.MiscTextures.colorMapData.data(), size);
}
void ReadRooms()
{
int numRooms = ReadInt32();
printf("NumRooms: %d\n", numRooms);
for (int i = 0; i < numRooms; i++)
{
ROOM_INFO room;
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());
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;
poly.shape = 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.index = ReadInt32();
floor.box = ReadInt32();
floor.fx = ReadInt32();
floor.stopper = ReadInt32();
floor.pitRoom = ReadInt32();
floor.floor = ReadInt32();
floor.skyRoom = ReadInt32();
floor.ceiling = ReadInt32();
floor.floorCollision.split = ReadInt32();
floor.floorCollision.noCollision = ReadInt32();
floor.floorCollision.planes[0].a = ReadFloat();
floor.floorCollision.planes[0].b = ReadFloat();
floor.floorCollision.planes[0].c = ReadFloat();
floor.floorCollision.planes[1].a = ReadFloat();
floor.floorCollision.planes[1].b = ReadFloat();
floor.floorCollision.planes[1].c = ReadFloat();
floor.ceilingCollision.split = ReadInt32();
floor.ceilingCollision.noCollision = ReadInt32();
floor.ceilingCollision.planes[0].a = ReadFloat();
floor.ceilingCollision.planes[0].b = ReadFloat();
floor.ceilingCollision.planes[0].c = ReadFloat();
floor.ceilingCollision.planes[1].a = ReadFloat();
floor.ceilingCollision.planes[1].b = ReadFloat();
floor.ceilingCollision.planes[1].c = ReadFloat();
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++)
{
MESH_INFO mesh;
mesh.x = ReadInt32();
mesh.y = ReadInt32();
mesh.z = ReadInt32();
mesh.yRot = ReadUInt16();
mesh.shade = ReadUInt16();
mesh.flags = ReadUInt16();
mesh.staticNumber = ReadUInt16();
mesh.hitPoints = ReadInt16();
room.mesh.push_back(mesh);
}
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;
g_Level.Rooms.push_back(room);
}
}
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()
{
2020-04-24 19:15:05 +02:00
malloc_ptr = malloc_buffer;
malloc_free = malloc_size;
g_Level.RoomTextures.clear();
g_Level.MoveablesTextures.clear();
g_Level.StaticsTextures.clear();
g_Level.SpritesTextures.clear();
g_Level.Rooms.clear();
g_Level.ObjectTextures.clear();
g_Level.Bones.clear();
g_Level.Meshes.clear();
2020-07-03 07:05:33 +02:00
MoveablesIds.clear();
g_Level.Boxes.clear();
g_Level.Overlaps.clear();
g_Level.Anims.clear();
g_Level.Changes.clear();
g_Level.Ranges.clear();
g_Level.Commands.clear();
g_Level.Frames.clear();
g_Level.Sprites.clear();
g_Level.SoundDetails.clear();
g_Level.SoundMap.clear();
g_Level.FloorData.clear();
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()
{
NumberSoundSources = ReadInt32();
if (NumberSoundSources)
{
SoundSources = game_malloc<OBJECT_VECTOR>(NumberSoundSources);
ReadBytes(SoundSources, NumberSoundSources * sizeof(OBJECT_VECTOR));
}
}
void LoadAnimatedTextures()
{
NumAnimatedTextures = ReadInt32();
AnimTextureRanges = game_malloc<short>(NumAnimatedTextures);
ReadBytes(AnimTextureRanges, NumAnimatedTextures * sizeof(short));
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.resize(nAIObjects);
ReadBytes(g_Level.AIObjects.data(), nAIObjects * sizeof(AIOBJECT));
}
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);
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);
SeedRandomDraw(0xD371F947);
SeedRandomControl(0xD371F947);
Wibble = 0;
TorchRoom = -1;
InitialiseGameFlags();
InitialiseLara(!(InitialiseGame || CurrentLevel == 1 || level->ResetHub));
GetCarriedItems();
GetAIPickups();
SeedRandomDraw(0xD371F947);
SeedRandomControl(0xD371F947);
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));
int numSampleIndices = ReadInt32();
if (numSampleIndices)
{
2020-07-26 07:21:38 +02:00
int numSamples = ReadInt32();
if (numSamples <= 0)
return;
int uncompressedSize;
int compressedSize;
2020-07-26 07:21:38 +02:00
char* buffer = (char*)malloc(2 * 1048576);
for (int i = 0; i < numSamples; i++)
{
2020-07-26 07:21:38 +02:00
uncompressedSize = ReadInt32();
compressedSize = ReadInt32();
ReadBytes(buffer, compressedSize);
Sound_LoadSample(buffer, compressedSize, uncompressedSize, i);
}
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 < 4; 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[4][i].resize(numBoxes * sizeof(int));
ReadBytes(g_Level.Zones[4][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;
AIOBJECT* 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);
}
}
}
}