mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-14 00:54:05 +02:00
@@ -184,7 +184,7 @@ struct Camera : ICamera {
|
||||
int indexB = min((indexA + 1), level->cameraFramesCount - 1);
|
||||
|
||||
if (indexA == level->cameraFramesCount - 1) {
|
||||
if (level->cutEntity != -1)
|
||||
if (level->isCutsceneLevel())
|
||||
game->loadNextLevel();
|
||||
else {
|
||||
Character *lara = (Character*)game->getLara();
|
||||
@@ -372,8 +372,8 @@ struct Camera : ICamera {
|
||||
advTimer = 0.0f;
|
||||
|
||||
fov = firstPerson ? 90.0f : 65.0f;
|
||||
znear = firstPerson ? 8.0f : 128.0f;
|
||||
zfar = 40.0f * 1024.0f * 1024.0f;
|
||||
znear = firstPerson ? 8.0f : 32.0f;
|
||||
zfar = 40.0f * 1024.0f;
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -371,6 +371,10 @@ struct Controller {
|
||||
info.lava = true;
|
||||
break;
|
||||
|
||||
case TR::FloorData::CLIMB :
|
||||
info.climb = (*fd++).data; // climb mask
|
||||
break;
|
||||
|
||||
default : LOG("unknown func: %d\n", cmd.func);
|
||||
}
|
||||
|
||||
@@ -771,14 +775,21 @@ struct Controller {
|
||||
virtual void cmdEmpty() {}
|
||||
|
||||
virtual void cmdEffect(int fx) {
|
||||
if (fx == 18) return; // TR2 TODO MESH_SWAP1
|
||||
if (fx == 19) return; // TR2 TODO MESH_SWAP2
|
||||
if (fx == 20) return; // TR2 TODO MESH_SWAP3
|
||||
if (fx == 21) flags.invisible = true; return; // TR2 TODO INV_ON visible = false
|
||||
if (fx == 22) flags.invisible = false; return; // TR2 TODO INV_OFF visible = true
|
||||
if (fx == 23) return; // TR2 TODO DYN_ON
|
||||
if (fx == 24) return; // TR2 TODO DYN_OFF
|
||||
ASSERT(false); // not implemented
|
||||
switch (fx) {
|
||||
case TR::Effect::MESH_SWAP_1 :
|
||||
case TR::Effect::MESH_SWAP_2 :
|
||||
case TR::Effect::MESH_SWAP_3 : {
|
||||
if (!layers) initMeshOverrides();
|
||||
uint32 mask = (layers[1].mask == 0xFFFFFFFF) ? 0 : 0xFFFFFFFF;
|
||||
meshSwap(1, level->extra.meshSwap[fx - TR::Effect::MESH_SWAP_1], mask);
|
||||
break;
|
||||
}
|
||||
case TR::Effect::INV_ON : flags.invisible = true; break;
|
||||
case TR::Effect::INV_OFF : flags.invisible = false; break;
|
||||
case TR::Effect::DYN_ON : break; // TODO TR2
|
||||
case TR::Effect::DYN_OFF : break; // TODO TR2
|
||||
default : ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void updateAnimation(bool commands) {
|
||||
@@ -896,22 +907,31 @@ struct Controller {
|
||||
}
|
||||
|
||||
void updateLights(bool lerp = true) {
|
||||
TR::Room::Light sunLight;
|
||||
|
||||
if (getModel()) {
|
||||
const TR::Room &room = getRoom();
|
||||
|
||||
vec3 center = getBoundingBox().center();
|
||||
float maxAtt = 0.0f;
|
||||
|
||||
for (int i = 0; i < room.lightsCount; i++) {
|
||||
TR::Room::Light &light = room.lights[i];
|
||||
if (light.intensity > 0x1FFF) continue;
|
||||
if (room.flags.sky) { // TODO trace rooms up for sun light, add direct light projection
|
||||
sunLight.x = int32(center.x);
|
||||
sunLight.y = int32(center.y) - 8192;
|
||||
sunLight.z = int32(center.z);
|
||||
targetLight = &sunLight;
|
||||
} else {
|
||||
for (int i = 0; i < room.lightsCount; i++) {
|
||||
TR::Room::Light &light = room.lights[i];
|
||||
if (light.intensity > 0x1FFF) continue;
|
||||
|
||||
vec3 dir = vec3(float(light.x), float(light.y), float(light.z)) - center;
|
||||
float att = max(0.0f, 1.0f - dir.length2() / float(light.radius) / float(light.radius)) * (1.0f - intensityf(light.intensity));
|
||||
vec3 dir = vec3(float(light.x), float(light.y), float(light.z)) - center;
|
||||
float att = max(0.0f, 1.0f - dir.length2() / float(light.radius) / float(light.radius)) * (1.0f - intensityf(light.intensity));
|
||||
|
||||
if (att > maxAtt) {
|
||||
maxAtt = att;
|
||||
targetLight = &light;
|
||||
if (att > maxAtt) {
|
||||
maxAtt = att;
|
||||
targetLight = &light;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
|
24
src/enemy.h
24
src/enemy.h
@@ -1463,4 +1463,28 @@ struct Natla : Human {
|
||||
Natla(IGame *game, int entity) : Human(game, entity, 400) {}
|
||||
};
|
||||
|
||||
struct Dog : Enemy {
|
||||
|
||||
enum {
|
||||
ANIM_SLEEP = 5,
|
||||
ANIM_DEATH = 13,
|
||||
};
|
||||
|
||||
enum {
|
||||
STATE_DEATH = 10,
|
||||
};
|
||||
|
||||
Dog(IGame *game, int entity) : Enemy(game, entity, 6, 10, 0.0f, 0.0f) {
|
||||
jointChest = 19;
|
||||
jointHead = 20;
|
||||
animation.setAnim(ANIM_SLEEP);
|
||||
}
|
||||
|
||||
virtual int getStateDeath() {
|
||||
if (state != STATE_DEATH)
|
||||
return animation.setAnim(ANIM_DEATH);
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
456
src/format.h
456
src/format.h
@@ -473,14 +473,24 @@
|
||||
namespace TR {
|
||||
|
||||
enum Version : uint32 {
|
||||
VER_UNKNOWN = 0,
|
||||
VER_TR1_PC = 1,
|
||||
VER_TR1_PSX = 2,
|
||||
VER_TR1_SAT = 4,
|
||||
VER_TR1 = VER_TR1_PC | VER_TR1_PSX | VER_TR1_SAT,
|
||||
VER_TR2_PC = 16,
|
||||
VER_TR2_PSX = 32,
|
||||
VER_TR2 = VER_TR2_PC | VER_TR2_PSX,
|
||||
VER_UNKNOWN = 0,
|
||||
|
||||
VER_PC = 256,
|
||||
VER_PSX = 512,
|
||||
VER_SEGA = 1024,
|
||||
|
||||
VER_TR1 = 1,
|
||||
VER_TR2 = 2,
|
||||
VER_TR3 = 4,
|
||||
VER_TR4 = 8,
|
||||
VER_TR5 = 16,
|
||||
|
||||
VER_TR1_PC = VER_TR1 | VER_PC,
|
||||
VER_TR1_PSX = VER_TR1 | VER_PSX,
|
||||
VER_TR1_SEGA = VER_TR1 | VER_SEGA,
|
||||
|
||||
VER_TR2_PC = VER_TR2 | VER_PC,
|
||||
VER_TR2_PSX = VER_TR2 | VER_PSX,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -518,8 +528,16 @@ namespace TR {
|
||||
LARA_HANDSFREE ,
|
||||
FLIP_MAP ,
|
||||
DRAW_RIGHTGUN ,
|
||||
UNK5 ,
|
||||
DRAW_LEFTGUN ,
|
||||
FLICKER ,
|
||||
UNKNOWN ,
|
||||
MESH_SWAP_1 ,
|
||||
MESH_SWAP_2 ,
|
||||
MESH_SWAP_3 ,
|
||||
INV_ON ,
|
||||
INV_OFF ,
|
||||
DYN_ON ,
|
||||
DYN_OFF ,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -814,7 +832,7 @@ namespace TR {
|
||||
uint16 meshesCount;
|
||||
int16 alternateRoom;
|
||||
struct {
|
||||
uint16 water:1, unused:14, visible:1;
|
||||
uint16 water:1, :2, sky:1, :1, wind:1, unused:9, visible:1;
|
||||
} flags;
|
||||
|
||||
struct Portal {
|
||||
@@ -896,6 +914,7 @@ namespace TR {
|
||||
CEILING ,
|
||||
TRIGGER ,
|
||||
LAVA ,
|
||||
CLIMB ,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1082,19 +1101,26 @@ namespace TR {
|
||||
|
||||
|
||||
bool isEnemy() const {
|
||||
return (type >= ENEMY_DOPPELGANGER && type <= ENEMY_GIANT_MUTANT) || type == SCION_TARGET;
|
||||
return (type >= ENEMY_DOPPELGANGER && type <= ENEMY_GIANT_MUTANT) || type == SCION_TARGET ||
|
||||
(type >= ENEMY_DOG && type <= ENEMY_DRAGON_BACK) ||
|
||||
(type >= ENEMY_SHARK && type <= ENEMY_MONK_2);
|
||||
}
|
||||
|
||||
bool isBigEnemy() const {
|
||||
return type == ENEMY_REX || type == ENEMY_MUTANT_1 || type == ENEMY_CENTAUR;
|
||||
}
|
||||
|
||||
bool isVehicle() const {
|
||||
return type == VEHICLE_BOAT || type == VEHICLE_SNOWMOBILE_RED || type == VEHICLE_SNOWMOBILE_BLACK;
|
||||
}
|
||||
|
||||
bool isDoor() const {
|
||||
return type >= DOOR_1 && type <= DOOR_6;
|
||||
}
|
||||
|
||||
bool isCollider() const {
|
||||
return isEnemy() ||
|
||||
isVehicle() ||
|
||||
isDoor() ||
|
||||
(type == DRAWBRIDGE && flags.active != ACTIVE) ||
|
||||
((type == HAMMER_HANDLE || type == HAMMER_BLOCK) && flags.collision) ||
|
||||
@@ -1137,7 +1163,7 @@ namespace TR {
|
||||
}
|
||||
|
||||
bool castShadow() const {
|
||||
return isLara() || isEnemy() || isActor() || type == DART || type == TRAP_SWORD;
|
||||
return isLara() || isEnemy() || isVehicle() || isActor() || type == DART || type == TRAP_SWORD;
|
||||
}
|
||||
|
||||
void getAxis(int &dx, int &dz) {
|
||||
@@ -1573,10 +1599,10 @@ namespace TR {
|
||||
{ "BOAT" , "Venice", NO_TRACK },
|
||||
{ "VENICE" , "Bartoli's Hideout", NO_TRACK },
|
||||
{ "OPERA" , "Opera House", NO_TRACK },
|
||||
{ "CUT2" , "", 3 },
|
||||
{ "CUT2" , "", 4 },
|
||||
{ "RIG" , "Offshore Rig", NO_TRACK },
|
||||
{ "PLATFORM" , "Diving Area", NO_TRACK },
|
||||
{ "CUT3" , "", 4 },
|
||||
{ "CUT3" , "", 5 },
|
||||
{ "UNWATER" , "40 Fathoms", NO_TRACK },
|
||||
{ "KEEL" , "Wreck of the Maria Doria", NO_TRACK },
|
||||
{ "LIVING" , "Living Quarters", NO_TRACK },
|
||||
@@ -1587,7 +1613,7 @@ namespace TR {
|
||||
{ "ICECAVE" , "Ice Palace", NO_TRACK },
|
||||
{ "EMPRTOMB" , "Temple of Xian", NO_TRACK },
|
||||
{ "FLOATING" , "Floating Islands", NO_TRACK },
|
||||
{ "CUT4" , "", 2 },
|
||||
{ "CUT4" , "", 30 },
|
||||
{ "XIAN" , "The Dragon's Lair", NO_TRACK },
|
||||
{ "HOUSE" , "Home Sweet Home", NO_TRACK },
|
||||
};
|
||||
@@ -1783,6 +1809,7 @@ namespace TR {
|
||||
};
|
||||
|
||||
struct FloorInfo {
|
||||
// TODO reduse type sizes
|
||||
float roomFloor, roomCeiling;
|
||||
int roomNext, roomBelow, roomAbove;
|
||||
float floor, ceiling;
|
||||
@@ -1791,6 +1818,7 @@ namespace TR {
|
||||
int boxIndex;
|
||||
int lava;
|
||||
int trigCmdCount;
|
||||
int climb;
|
||||
Trigger trigger;
|
||||
FloorData::TriggerInfo trigInfo;
|
||||
FloorData::TriggerCommand trigCmd[MAX_TRIGGER_COMMANDS];
|
||||
@@ -1822,6 +1850,8 @@ namespace TR {
|
||||
int16 puzzleDone[4];
|
||||
int16 weapons[4];
|
||||
int16 braid;
|
||||
int16 laraSpec;
|
||||
int16 meshSwap[3];
|
||||
int16 sky;
|
||||
int16 smoke;
|
||||
int16 glyphs;
|
||||
@@ -1831,6 +1861,7 @@ namespace TR {
|
||||
int16 passport_closed;
|
||||
int16 map;
|
||||
int16 compass;
|
||||
int16 stopwatch;
|
||||
int16 home;
|
||||
int16 detail;
|
||||
int16 sound;
|
||||
@@ -1868,7 +1899,7 @@ namespace TR {
|
||||
|
||||
id = getLevelID(stream.size);
|
||||
|
||||
if (version == VER_UNKNOWN) {
|
||||
if (version == VER_UNKNOWN || version == VER_TR1_PSX) {
|
||||
stream.read(magic);
|
||||
if (magic != MAGIC_TR1_PC && magic != MAGIC_TR2_PC) {
|
||||
soundOffset = magic;
|
||||
@@ -1879,7 +1910,7 @@ namespace TR {
|
||||
case MAGIC_TR1_PC : version = VER_TR1_PC; break;
|
||||
case MAGIC_TR1_PSX : version = VER_TR1_PSX; break;
|
||||
case MAGIC_TR2_PC : version = VER_TR2_PC; break;
|
||||
default : version = VER_UNKNOWN;
|
||||
default : ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1907,7 +1938,7 @@ namespace TR {
|
||||
stream.read(palette32, 256);
|
||||
}
|
||||
|
||||
if (version == VER_TR1_PSX) {
|
||||
if (version == VER_TR1_PSX && !isCutsceneLevel()) {
|
||||
uint32 offsetTexTiles;
|
||||
stream.seek(8);
|
||||
stream.read(offsetTexTiles);
|
||||
@@ -1938,8 +1969,7 @@ namespace TR {
|
||||
if (version == VER_TR2_PC)
|
||||
stream.read(tiles16, tilesCount);
|
||||
|
||||
if (!version /*PSX cutscene*/ || version == VER_TR1_PSX) {
|
||||
version = VER_TR1_PSX;
|
||||
if (version == VER_TR1_PSX) {
|
||||
// tiles
|
||||
stream.read(tiles4, tilesCount = 13);
|
||||
stream.read(cluts, clutsCount = 512);
|
||||
@@ -1949,146 +1979,8 @@ namespace TR {
|
||||
|
||||
// rooms
|
||||
rooms = new Room[stream.read(roomsCount)];
|
||||
for (int i = 0; i < roomsCount; i++) {
|
||||
Room &r = rooms[i];
|
||||
Room::Data &d = r.data;
|
||||
// room info
|
||||
stream.read(r.info);
|
||||
// room data
|
||||
stream.read(d.size);
|
||||
int startOffset = stream.pos;
|
||||
if (version == VER_TR1_PSX) stream.seek(2);
|
||||
d.vertices = stream.read(d.vCount) ? new Room::Data::Vertex[d.vCount] : NULL;
|
||||
for (int i = 0; i < d.vCount; i++) {
|
||||
Room::Data::Vertex &v = d.vertices[i];
|
||||
|
||||
if (version == VER_TR2_PSX) {
|
||||
union Compressed {
|
||||
struct { uint32 lighting:8, attributes:8, z:5, y:5, x:5, w:1; };
|
||||
uint32 value;
|
||||
} comp;
|
||||
stream.read(comp.value);
|
||||
v.vertex.x = (comp.x << 10);
|
||||
v.vertex.y = (comp.y << 8) + r.info.yTop;
|
||||
v.vertex.z = (comp.z << 10);
|
||||
v.lighting = comp.lighting;
|
||||
v.attributes = comp.attributes;
|
||||
ASSERT(comp.w == 0);
|
||||
} else {
|
||||
stream.read(v.vertex.x);
|
||||
stream.read(v.vertex.y);
|
||||
stream.read(v.vertex.z);
|
||||
stream.read(v.lighting);
|
||||
if (version == VER_TR2_PC) {
|
||||
stream.read(v.attributes);
|
||||
stream.read(v.lighting2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (version == VER_TR2_PSX)
|
||||
stream.seek(2);
|
||||
|
||||
if (version == VER_TR1_PSX || version == VER_TR2_PSX)
|
||||
for (int j = 0; j < d.vCount; j++) // convert vertex luminance from PSX to PC format
|
||||
d.vertices[j].lighting = 0x1FFF - (d.vertices[j].lighting << 5);
|
||||
|
||||
d.rectangles = stream.read(d.rCount) ? new Rectangle[d.rCount] : NULL;
|
||||
if (version == VER_TR2_PSX) {
|
||||
for (int i = 0; i < d.rCount; i++)
|
||||
stream.raw(&d.rectangles[i].flags.value, sizeof(uint16)); // hack to read texture:15 color:1
|
||||
if ((stream.pos - startOffset) % 4) stream.seek(2);
|
||||
for (int i = 0; i < d.rCount; i++) {
|
||||
stream.raw(d.rectangles[i].vertices, sizeof(d.rectangles[i].vertices));
|
||||
d.rectangles[i].vertices[0] >>= 2;
|
||||
d.rectangles[i].vertices[1] >>= 2;
|
||||
d.rectangles[i].vertices[2] >>= 2;
|
||||
d.rectangles[i].vertices[3] >>= 2;
|
||||
}
|
||||
} else
|
||||
stream.raw(d.rectangles, sizeof(Rectangle) * d.rCount);
|
||||
|
||||
if (version == VER_TR1_PSX || version == VER_TR2_PSX)
|
||||
for (int j = 0; j < d.rCount; j++) // swap indices (quad strip -> quad list)
|
||||
swap(d.rectangles[j].vertices[2], d.rectangles[j].vertices[3]);
|
||||
|
||||
d.triangles = stream.read(d.tCount) ? new Triangle[d.tCount] : NULL;
|
||||
if (version == VER_TR2_PSX) {
|
||||
stream.seek(2);
|
||||
for (int i = 0; i < d.tCount; i++) {
|
||||
stream.raw(&d.triangles[i].flags.value, sizeof(uint16)); // hack to read texture:15 color:1
|
||||
stream.raw(d.triangles[i].vertices, sizeof(d.triangles[i].vertices));
|
||||
d.triangles[i].vertices[0] >>= 2;
|
||||
d.triangles[i].vertices[1] >>= 2;
|
||||
d.triangles[i].vertices[2] >>= 2;
|
||||
}
|
||||
} else
|
||||
stream.raw(d.triangles, sizeof(Triangle) * d.tCount);
|
||||
|
||||
// room sprites
|
||||
if (version == VER_TR2_PSX) { // there is no room sprites
|
||||
d.sprites = NULL;
|
||||
d.sCount = NULL;
|
||||
} else
|
||||
stream.read(d.sprites, stream.read(d.sCount));
|
||||
|
||||
// portals
|
||||
stream.read(r.portals, stream.read(r.portalsCount));
|
||||
// sectors
|
||||
stream.read(r.zSectors);
|
||||
stream.read(r.xSectors);
|
||||
stream.read(r.sectors, r.zSectors * r.xSectors);
|
||||
// ambient light luminance
|
||||
stream.read(r.ambient);
|
||||
if (version & VER_TR2) {
|
||||
stream.read(r.ambient2);
|
||||
stream.read(r.lightMode);
|
||||
}
|
||||
|
||||
// lights
|
||||
r.lights = stream.read(r.lightsCount) ? new Room::Light[r.lightsCount] : NULL;
|
||||
for (int i = 0; i < r.lightsCount; i++) {
|
||||
Room::Light &light = r.lights[i];
|
||||
stream.read(light.x);
|
||||
stream.read(light.y);
|
||||
stream.read(light.z);
|
||||
if (version == VER_TR1_PSX) {
|
||||
uint32 intensity;
|
||||
light.intensity = stream.read(intensity);
|
||||
} else
|
||||
stream.read(light.intensity);
|
||||
|
||||
if (version & VER_TR2)
|
||||
stream.read(light.intensity2);
|
||||
|
||||
stream.read(light.radius);
|
||||
|
||||
if (version & VER_TR2)
|
||||
stream.read(light.radius2);
|
||||
|
||||
light.radius *= 2;
|
||||
}
|
||||
// meshes
|
||||
stream.read(r.meshesCount);
|
||||
r.meshes = r.meshesCount ? new Room::Mesh[r.meshesCount] : NULL;
|
||||
for (int i = 0; i < r.meshesCount; i++) {
|
||||
Room::Mesh &m = r.meshes[i];
|
||||
stream.read(m.x);
|
||||
stream.read(m.y);
|
||||
stream.read(m.z);
|
||||
stream.read(m.rotation);
|
||||
stream.read(m.intensity);
|
||||
if (version & VER_TR2)
|
||||
stream.read(m.intensity2);
|
||||
stream.read(m.meshID);
|
||||
if (version == VER_TR1_PSX)
|
||||
stream.seek(2); // just an align for PSX version
|
||||
}
|
||||
|
||||
// misc flags
|
||||
stream.read(r.alternateRoom);
|
||||
stream.read(r.flags);
|
||||
}
|
||||
for (int i = 0; i < roomsCount; i++)
|
||||
readRoom(stream, rooms[i]);
|
||||
|
||||
// floors
|
||||
stream.read(floors, stream.read(floorsCount));
|
||||
@@ -2113,7 +2005,7 @@ namespace TR {
|
||||
stream.read(m.node);
|
||||
stream.read(m.frame);
|
||||
stream.read(m.animation);
|
||||
if (version == VER_TR1_PSX || version == VER_TR2_PSX)
|
||||
if (version & VER_PSX)
|
||||
stream.seek(2);
|
||||
m.type = Entity::remap(version, m.type);
|
||||
}
|
||||
@@ -2139,7 +2031,7 @@ namespace TR {
|
||||
boxes = stream.read(boxesCount) ? new Box[boxesCount] : NULL;
|
||||
for (int i = 0; i < boxesCount; i++) {
|
||||
Box &b = boxes[i];
|
||||
if (version == VER_TR1_PC || version == VER_TR1_PSX) {
|
||||
if (version & VER_TR1) {
|
||||
stream.read(b.minZ);
|
||||
stream.read(b.maxZ);
|
||||
stream.read(b.minX);
|
||||
@@ -2162,14 +2054,14 @@ namespace TR {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
stream.read(zones[i].ground1, boxesCount);
|
||||
stream.read(zones[i].ground2, boxesCount);
|
||||
if (version & VER_TR2) {
|
||||
if (!(version & VER_TR1)) {
|
||||
stream.read(zones[i].ground3, boxesCount);
|
||||
stream.read(zones[i].ground4, boxesCount);
|
||||
} else {
|
||||
zones[i].ground3 = NULL;
|
||||
zones[i].ground4 = NULL;
|
||||
}
|
||||
stream.read(zones[i].fly, boxesCount);
|
||||
stream.read(zones[i].fly, boxesCount);
|
||||
}
|
||||
// animated textures
|
||||
stream.read(animTexturesData, stream.read(animTexturesDataSize));
|
||||
@@ -2200,7 +2092,8 @@ namespace TR {
|
||||
if (isCutsceneLevel()) {
|
||||
for (int i = 0; i < entitiesBaseCount; i++) {
|
||||
TR::Entity &e = entities[i];
|
||||
if (e.isActor()) {
|
||||
if ((((version & VER_TR1)) && e.isActor()) ||
|
||||
(((version & VER_TR2)) && e.isLara())) {
|
||||
cutEntity = i;
|
||||
break;
|
||||
}
|
||||
@@ -2251,7 +2144,7 @@ namespace TR {
|
||||
}
|
||||
|
||||
// cinematic frames for cameras (PSX)
|
||||
if (version == VER_TR1_PSX || version == VER_TR2_PSX) {
|
||||
if (version & VER_PSX) {
|
||||
stream.seek(4);
|
||||
stream.read(cameraFrames, stream.read(cameraFramesCount));
|
||||
}
|
||||
@@ -2329,68 +2222,68 @@ namespace TR {
|
||||
isHomeLevel = false;
|
||||
switch (size) {
|
||||
// TR1
|
||||
case 508614 :
|
||||
case 508614 : version = VER_TR1_PSX;
|
||||
case 316138 :
|
||||
case 316460 : return LVL_TR1_TITLE;
|
||||
case 1074234 :
|
||||
case 1074234 : version = VER_TR1_PSX;
|
||||
case 3236806 :
|
||||
case 3237128 : isHomeLevel = true; return LVL_TR1_GYM;
|
||||
case 1448896 :
|
||||
case 1448896 : version = VER_TR1_PSX;
|
||||
case 2533312 :
|
||||
case 2533634 : return LVL_TR1_1;
|
||||
case 2873406 : isDemoLevel = true; return LVL_TR1_2;
|
||||
case 1535734 :
|
||||
case 1535734 : version = VER_TR1_PSX;
|
||||
case 2873128 :
|
||||
case 2873450 : return LVL_TR1_2;
|
||||
case 1630560 :
|
||||
case 1630560 : version = VER_TR1_PSX;
|
||||
case 2934408 :
|
||||
case 2934730 : return LVL_TR1_3A;
|
||||
case 1506614 :
|
||||
case 1506614 : version = VER_TR1_PSX;
|
||||
case 2737936 :
|
||||
case 2738258 : return LVL_TR1_3B;
|
||||
case 722402 :
|
||||
case 722402 : version = VER_TR1_PSX;
|
||||
case 599840 : return LVL_TR1_CUT_1;
|
||||
case 1621970 :
|
||||
case 1621970 : version = VER_TR1_PSX;
|
||||
case 3030550 :
|
||||
case 3030872 : return LVL_TR1_4;
|
||||
case 1585942 :
|
||||
case 1585942 : version = VER_TR1_PSX;
|
||||
case 2718218 :
|
||||
case 2718540 : return LVL_TR1_5;
|
||||
case 1708464 :
|
||||
case 1708464 : version = VER_TR1_PSX;
|
||||
case 3139590 :
|
||||
case 3074376 : return LVL_TR1_6;
|
||||
case 1696664 :
|
||||
case 1696664 : version = VER_TR1_PSX;
|
||||
case 2817290 :
|
||||
case 2817612 : return LVL_TR1_7A;
|
||||
case 1733274 :
|
||||
case 1733274 : version = VER_TR1_PSX;
|
||||
case 3388774 :
|
||||
case 3389096 : return LVL_TR1_7B;
|
||||
case 542960 :
|
||||
case 542960 : version = VER_TR1_PSX;
|
||||
case 354320 : return LVL_TR1_CUT_2;
|
||||
case 1563356 :
|
||||
case 1563356 : version = VER_TR1_PSX;
|
||||
case 2880242 :
|
||||
case 2880564 : return LVL_TR1_8A;
|
||||
case 1565630 :
|
||||
case 1565630 : version = VER_TR1_PSX;
|
||||
case 2886434 :
|
||||
case 2886756 : return LVL_TR1_8B;
|
||||
case 1619360 :
|
||||
case 1619360 : version = VER_TR1_PSX;
|
||||
case 3105128 :
|
||||
case 3105450 : return LVL_TR1_8C;
|
||||
case 1678018 :
|
||||
case 1678018 : version = VER_TR1_PSX;
|
||||
case 3223816 :
|
||||
case 3224138 : return LVL_TR1_10A;
|
||||
case 636660 :
|
||||
case 636660 : version = VER_TR1_PSX;
|
||||
case 512104 : return LVL_TR1_CUT_3;
|
||||
case 1686748 :
|
||||
case 1686748 : version = VER_TR1_PSX;
|
||||
case 3094020 : return LVL_TR1_10B;
|
||||
case 940398 :
|
||||
case 940398 : version = VER_TR1_PSX;
|
||||
case 879582 : return LVL_TR1_CUT_4;
|
||||
case 1814278 :
|
||||
case 1814278 : version = VER_TR1_PSX;
|
||||
case 3531702 :
|
||||
case 3532024 : return LVL_TR1_10C;
|
||||
case 3278614 :
|
||||
case 3278614 : version = VER_TR1_PSX;
|
||||
case 3279242 : return LVL_TR1_EGYPT;
|
||||
case 3270370 :
|
||||
case 3270370 : version = VER_TR1_PSX;
|
||||
case 3270998 : return LVL_TR1_CAT;
|
||||
case 3208018 : return LVL_TR1_END;
|
||||
case 3153300 : return LVL_TR1_END2;
|
||||
@@ -2454,13 +2347,11 @@ namespace TR {
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61
|
||||
};
|
||||
|
||||
switch (version) {
|
||||
case VER_TR2_PC :
|
||||
ASSERT(track < COUNT(TR2_TRACK_MAPPING));
|
||||
return TR2_TRACK_MAPPING[track];
|
||||
default :
|
||||
return track;
|
||||
if (version & VER_TR2) {
|
||||
ASSERT(track < COUNT(TR2_TRACK_MAPPING));
|
||||
return TR2_TRACK_MAPPING[track];
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
void initExtra() {
|
||||
@@ -2479,11 +2370,16 @@ namespace TR {
|
||||
case Entity::LARA_MAGNUMS : extra.weapons[2] = i; break;
|
||||
case Entity::LARA_UZIS : extra.weapons[3] = i; break;
|
||||
case Entity::LARA_BRAID : extra.braid = i; break;
|
||||
case Entity::LARA_SPEC : extra.laraSpec = i; break;
|
||||
case Entity::CUT_1 : extra.meshSwap[0] = i; break;
|
||||
case Entity::CUT_2 : extra.meshSwap[1] = i; break;
|
||||
case Entity::CUT_3 : extra.meshSwap[2] = i; break;
|
||||
|
||||
case Entity::INV_PASSPORT : extra.inv.passport = i; break;
|
||||
case Entity::INV_PASSPORT_CLOSED : extra.inv.passport_closed = i; break;
|
||||
case Entity::INV_MAP : extra.inv.map = i; break;
|
||||
case Entity::INV_COMPASS : extra.inv.compass = i; break;
|
||||
case Entity::INV_STOPWATCH : extra.inv.stopwatch = i; break;
|
||||
case Entity::INV_HOME : extra.inv.home = i; break;
|
||||
case Entity::INV_DETAIL : extra.inv.detail = i; break;
|
||||
case Entity::INV_SOUND : extra.inv.sound = i; break;
|
||||
@@ -2551,7 +2447,9 @@ namespace TR {
|
||||
cutMatrix.translate(vec3(float(e.x), float(e.y), float(e.z)));
|
||||
cutMatrix.rotateY(PI * 0.5f);
|
||||
break;
|
||||
default : cutMatrix.translate(vec3(float(e.x), float(e.y), float(e.z)));;
|
||||
default :
|
||||
cutMatrix.translate(vec3(float(e.x), float(e.y), float(e.z)));
|
||||
cutMatrix.rotateY(e.rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2559,6 +2457,7 @@ namespace TR {
|
||||
LevelID titleId() {
|
||||
if (version & VER_TR1) return LVL_TR1_TITLE;
|
||||
if (version & VER_TR2) return LVL_TR2_TITLE;
|
||||
return LVL_TR1_TITLE;
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
@@ -2571,6 +2470,155 @@ namespace TR {
|
||||
id == LVL_TR2_CUT_1 || id == LVL_TR2_CUT_2 || id == LVL_TR2_CUT_3 || id == LVL_TR2_CUT_4;
|
||||
}
|
||||
|
||||
void readRoom(Stream &stream, Room &r) {
|
||||
Room::Data &d = r.data;
|
||||
// room info
|
||||
stream.read(r.info);
|
||||
// room data
|
||||
stream.read(d.size);
|
||||
int startOffset = stream.pos;
|
||||
if (version == VER_TR1_PSX) stream.seek(2);
|
||||
d.vertices = stream.read(d.vCount) ? new Room::Data::Vertex[d.vCount] : NULL;
|
||||
for (int i = 0; i < d.vCount; i++) {
|
||||
Room::Data::Vertex &v = d.vertices[i];
|
||||
|
||||
if (version == VER_TR2_PSX) {
|
||||
union Compressed {
|
||||
struct { uint32 lighting:8, attributes:8, z:5, y:5, x:5, w:1; };
|
||||
uint32 value;
|
||||
} comp;
|
||||
stream.read(comp.value);
|
||||
v.vertex.x = (comp.x << 10);
|
||||
v.vertex.y = (comp.y << 8) + r.info.yTop;
|
||||
v.vertex.z = (comp.z << 10);
|
||||
v.lighting = comp.lighting;
|
||||
v.attributes = comp.attributes;
|
||||
ASSERT(comp.w == 0);
|
||||
} else {
|
||||
stream.read(v.vertex.x);
|
||||
stream.read(v.vertex.y);
|
||||
stream.read(v.vertex.z);
|
||||
stream.read(v.lighting);
|
||||
if (version == VER_TR2_PC) {
|
||||
stream.read(v.attributes);
|
||||
stream.read(v.lighting2);
|
||||
}
|
||||
}
|
||||
|
||||
if (version & VER_PSX)
|
||||
v.lighting = 0x1FFF - (v.lighting << 5); // convert vertex luminance from PSX to PC format
|
||||
}
|
||||
|
||||
if (version == VER_TR2_PSX)
|
||||
stream.seek(2);
|
||||
|
||||
d.rectangles = stream.read(d.rCount) ? new Rectangle[d.rCount] : NULL;
|
||||
if (version == VER_TR2_PSX) {
|
||||
for (int i = 0; i < d.rCount; i++)
|
||||
stream.raw(&d.rectangles[i].flags.value, sizeof(uint16)); // hack to read texture:15 color:1
|
||||
if ((stream.pos - startOffset) % 4) stream.seek(2);
|
||||
for (int i = 0; i < d.rCount; i++) {
|
||||
stream.raw(d.rectangles[i].vertices, sizeof(d.rectangles[i].vertices));
|
||||
d.rectangles[i].vertices[0] >>= 2;
|
||||
d.rectangles[i].vertices[1] >>= 2;
|
||||
d.rectangles[i].vertices[2] >>= 2;
|
||||
d.rectangles[i].vertices[3] >>= 2;
|
||||
}
|
||||
} else
|
||||
stream.raw(d.rectangles, sizeof(Rectangle) * d.rCount);
|
||||
|
||||
if (version == VER_TR1_PSX || version == VER_TR2_PSX)
|
||||
for (int j = 0; j < d.rCount; j++) // swap indices (quad strip -> quad list)
|
||||
swap(d.rectangles[j].vertices[2], d.rectangles[j].vertices[3]);
|
||||
|
||||
d.triangles = stream.read(d.tCount) ? new Triangle[d.tCount] : NULL;
|
||||
if (version == VER_TR2_PSX) {
|
||||
stream.seek(2);
|
||||
for (int i = 0; i < d.tCount; i++) {
|
||||
stream.raw(&d.triangles[i].flags.value, sizeof(uint16)); // hack to read texture:15 color:1
|
||||
stream.raw(d.triangles[i].vertices, sizeof(d.triangles[i].vertices));
|
||||
d.triangles[i].vertices[0] >>= 2;
|
||||
d.triangles[i].vertices[1] >>= 2;
|
||||
d.triangles[i].vertices[2] >>= 2;
|
||||
}
|
||||
} else
|
||||
stream.raw(d.triangles, sizeof(Triangle) * d.tCount);
|
||||
|
||||
// room sprites
|
||||
if (version == VER_TR2_PSX) { // there is no room sprites
|
||||
d.sprites = NULL;
|
||||
d.sCount = NULL;
|
||||
} else
|
||||
stream.read(d.sprites, stream.read(d.sCount));
|
||||
|
||||
// portals
|
||||
stream.read(r.portals, stream.read(r.portalsCount));
|
||||
|
||||
if (version == VER_TR2_PSX)
|
||||
for (int i = 0; i < r.portalsCount; i++) {
|
||||
r.portals[i].vertices[0].y += r.info.yTop;
|
||||
r.portals[i].vertices[1].y += r.info.yTop;
|
||||
r.portals[i].vertices[2].y += r.info.yTop;
|
||||
r.portals[i].vertices[3].y += r.info.yTop;
|
||||
}
|
||||
|
||||
// sectors
|
||||
stream.read(r.zSectors);
|
||||
stream.read(r.xSectors);
|
||||
stream.read(r.sectors, r.zSectors * r.xSectors);
|
||||
// ambient light luminance
|
||||
stream.read(r.ambient);
|
||||
if (version & VER_TR2) {
|
||||
stream.read(r.ambient2);
|
||||
stream.read(r.lightMode);
|
||||
}
|
||||
|
||||
// lights
|
||||
r.lights = stream.read(r.lightsCount) ? new Room::Light[r.lightsCount] : NULL;
|
||||
for (int i = 0; i < r.lightsCount; i++) {
|
||||
Room::Light &light = r.lights[i];
|
||||
stream.read(light.x);
|
||||
stream.read(light.y);
|
||||
stream.read(light.z);
|
||||
if (version == VER_TR1_PSX) {
|
||||
uint32 intensity;
|
||||
light.intensity = stream.read(intensity);
|
||||
} else
|
||||
stream.read(light.intensity);
|
||||
|
||||
if (version & VER_TR2)
|
||||
stream.read(light.intensity2);
|
||||
|
||||
stream.read(light.radius);
|
||||
|
||||
if (version & VER_TR2)
|
||||
stream.read(light.radius2);
|
||||
|
||||
light.radius *= 2;
|
||||
}
|
||||
// meshes
|
||||
stream.read(r.meshesCount);
|
||||
r.meshes = r.meshesCount ? new Room::Mesh[r.meshesCount] : NULL;
|
||||
for (int i = 0; i < r.meshesCount; i++) {
|
||||
Room::Mesh &m = r.meshes[i];
|
||||
stream.read(m.x);
|
||||
stream.read(m.y);
|
||||
stream.read(m.z);
|
||||
stream.read(m.rotation);
|
||||
stream.read(m.intensity);
|
||||
if (version & VER_TR2)
|
||||
stream.read(m.intensity2);
|
||||
stream.read(m.meshID);
|
||||
if (version == VER_TR1_PSX)
|
||||
stream.seek(2); // just an align for PSX version
|
||||
}
|
||||
|
||||
// misc flags
|
||||
stream.read(r.alternateRoom);
|
||||
stream.read(r.flags);
|
||||
}
|
||||
|
||||
|
||||
void readMeshes(Stream &stream) {
|
||||
uint32 meshDataSize;
|
||||
stream.read(meshDataSize);
|
||||
@@ -2689,9 +2737,8 @@ namespace TR {
|
||||
}
|
||||
}
|
||||
|
||||
uint16 crCount = 0, ctCount = 0, rCount = 0, tCount = 0;
|
||||
|
||||
if (version == VER_TR2_PSX && nCount > 0) {
|
||||
if (version == VER_TR2_PSX && nCount > 0) { // TODO probably for unused meshes only but need to check
|
||||
uint16 crCount = 0, ctCount = 0;
|
||||
stream.read(crCount);
|
||||
stream.seek((sizeof(Rectangle) + 2) * crCount);
|
||||
stream.read(ctCount);
|
||||
@@ -2700,8 +2747,9 @@ namespace TR {
|
||||
|
||||
stream.read(mesh.rectangles, stream.read(mesh.rCount));
|
||||
stream.read(mesh.triangles, stream.read(mesh.tCount));
|
||||
|
||||
ASSERT(mesh.rCount != 0 || mesh.tCount != 0);
|
||||
if (mesh.rCount != 0 || mesh.tCount != 0)
|
||||
LOG("! warning: mesh %d has no geometry with %d vertices\n", meshesCount - 1, mesh.vCount);
|
||||
//ASSERT(mesh.rCount != 0 || mesh.tCount != 0);
|
||||
|
||||
for (int i = 0; i < mesh.rCount; i++) {
|
||||
if (mesh.rectangles[i].flags.texture < 256)
|
||||
@@ -2728,6 +2776,7 @@ namespace TR {
|
||||
|
||||
break;
|
||||
}
|
||||
default : ASSERT(false);
|
||||
}
|
||||
|
||||
#define RECALC_ZERO_NORMALS(mesh, face, count)\
|
||||
@@ -2839,6 +2888,7 @@ namespace TR {
|
||||
SET_PARAMS(t, d, d.clut);
|
||||
break;
|
||||
}
|
||||
default : ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2889,6 +2939,7 @@ namespace TR {
|
||||
t.texCoord[1] = { d.u1, d.v1 };
|
||||
break;
|
||||
}
|
||||
default : ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2999,6 +3050,7 @@ namespace TR {
|
||||
}
|
||||
break;
|
||||
}
|
||||
default : ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3017,6 +3069,7 @@ namespace TR {
|
||||
CLUT &clut = cluts[t.clut];
|
||||
return clut.color[part ? tile.index[idx].b : tile.index[idx].a];
|
||||
}
|
||||
default : ASSERT(false);
|
||||
}
|
||||
return Color32(255, 0, 255, 255);
|
||||
}
|
||||
@@ -3030,6 +3083,7 @@ namespace TR {
|
||||
case VER_TR2_PC : size = FOURCC(data + 4) + 8; break; // read size from wave header
|
||||
case VER_TR1_PSX :
|
||||
case VER_TR2_PSX : size = soundSize[index]; break;
|
||||
default : ASSERT(false);
|
||||
}
|
||||
return new Stream(data, size);
|
||||
}
|
||||
|
@@ -55,6 +55,7 @@ struct Inventory {
|
||||
case TR::Entity::INV_PASSPORT_CLOSED : desc = { STR_GAME, PAGE_OPTION, level->extra.inv.passport_closed }; break;
|
||||
case TR::Entity::INV_MAP : desc = { STR_MAP, PAGE_INVENTORY, level->extra.inv.map }; break;
|
||||
case TR::Entity::INV_COMPASS : desc = { STR_COMPASS, PAGE_INVENTORY, level->extra.inv.compass }; break;
|
||||
case TR::Entity::INV_STOPWATCH : desc = { STR_STOPWATCH, PAGE_INVENTORY, level->extra.inv.stopwatch }; break;
|
||||
case TR::Entity::INV_HOME : desc = { STR_HOME, PAGE_OPTION, level->extra.inv.home }; break;
|
||||
case TR::Entity::INV_DETAIL : desc = { STR_DETAIL, PAGE_OPTION, level->extra.inv.detail }; break;
|
||||
case TR::Entity::INV_SOUND : desc = { STR_SOUND, PAGE_OPTION, level->extra.inv.sound }; break;
|
||||
@@ -89,7 +90,7 @@ struct Inventory {
|
||||
default : desc = { STR_UNKNOWN, PAGE_ITEMS, -1 }; break;
|
||||
}
|
||||
|
||||
if (desc.model > -1) {
|
||||
if (desc.model > -1 && level->models[desc.model].animation != 0xFFFF) {
|
||||
anim = new Animation(level, &level->models[desc.model]);
|
||||
anim->isEnded = true;
|
||||
} else
|
||||
@@ -203,6 +204,7 @@ struct Inventory {
|
||||
new Stream("level/TITLEH.PCX", loadTitleBG, this);
|
||||
} else {
|
||||
add(TR::Entity::INV_COMPASS);
|
||||
add(TR::Entity::INV_STOPWATCH);
|
||||
|
||||
for (int i = 0; i < COUNT(background); i++)
|
||||
background[i] = new Texture(INVENTORY_BG_SIZE, INVENTORY_BG_SIZE, Texture::RGBA, false);
|
||||
@@ -647,6 +649,7 @@ struct Inventory {
|
||||
case TR::Entity::INV_PASSPORT_CLOSED :
|
||||
case TR::Entity::INV_MAP :
|
||||
case TR::Entity::INV_COMPASS :
|
||||
case TR::Entity::INV_STOPWATCH :
|
||||
case TR::Entity::INV_HOME :
|
||||
case TR::Entity::INV_DETAIL :
|
||||
case TR::Entity::INV_SOUND :
|
||||
@@ -894,7 +897,8 @@ struct Inventory {
|
||||
renderPassport(item);
|
||||
break;
|
||||
case TR::Entity::INV_HOME :
|
||||
case TR::Entity::INV_COMPASS :
|
||||
case TR::Entity::INV_COMPASS :
|
||||
case TR::Entity::INV_STOPWATCH :
|
||||
case TR::Entity::INV_MAP :
|
||||
break;
|
||||
case TR::Entity::INV_DETAIL :
|
||||
|
12
src/lara.h
12
src/lara.h
@@ -500,7 +500,7 @@ struct Lara : Character {
|
||||
dbgBoxes = NULL;
|
||||
#endif
|
||||
|
||||
if (getEntity().isLara()) {
|
||||
if (getEntity().isLara() && !level->isCutsceneLevel()) {
|
||||
if (getRoom().flags.water)
|
||||
animation.setAnim(ANIM_UNDERWATER);
|
||||
else
|
||||
@@ -1353,9 +1353,11 @@ struct Lara : Character {
|
||||
}
|
||||
|
||||
void drawGun(int right) {
|
||||
int mask = right ? BODY_ARM_R3 : BODY_ARM_L3; // unholster
|
||||
int mask = (right ? BODY_ARM_R3 : BODY_ARM_L3); // unholster
|
||||
if (layers[1].mask & mask)
|
||||
mask = right ? BODY_LEG_R1 : BODY_LEG_L1; // holster
|
||||
mask = (layers[1].mask & ~mask) | (right ? BODY_LEG_R1 : BODY_LEG_L1); // holster
|
||||
else
|
||||
mask |= layers[1].mask;
|
||||
meshSwap(1, level->extra.weapons[wpnCurrent], mask);
|
||||
}
|
||||
|
||||
@@ -1374,6 +1376,10 @@ struct Lara : Character {
|
||||
case TR::Effect::LARA_BUBBLES : doBubbles(); break;
|
||||
case TR::Effect::LARA_HANDSFREE : break;//meshSwap(1, level->extra.weapons[wpnCurrent], BODY_LEG_L1 | BODY_LEG_R1); break;
|
||||
case TR::Effect::DRAW_RIGHTGUN : drawGun(true); break;
|
||||
case TR::Effect::DRAW_LEFTGUN : drawGun(false); break;
|
||||
case TR::Effect::MESH_SWAP_1 :
|
||||
case TR::Effect::MESH_SWAP_2 :
|
||||
case TR::Effect::MESH_SWAP_3 : Character::cmdEffect(fx);
|
||||
case 26 : break; // TR2 TODO reset_hair
|
||||
default : LOG("unknown effect command %d (anim %d)\n", fx, animation.index); ASSERT(false);
|
||||
}
|
||||
|
73
src/level.h
73
src/level.h
@@ -80,8 +80,8 @@ struct Level : IGame {
|
||||
|
||||
virtual void loadNextLevel() {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if (level.id == TR::LEVEL_2 && level.version != TR::VER_TR1_PC) {
|
||||
loadLevel(TR::TITLE);
|
||||
if (level.id == TR::LVL_TR1_2 && level.version != TR::VER_TR1_PC) {
|
||||
loadLevel(TR::LVL_TR1_TITLE);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -133,7 +133,7 @@ struct Level : IGame {
|
||||
ptr += sizeof(*state);
|
||||
}
|
||||
|
||||
Stream::write("savegame.dat", data, int(ptr - data));
|
||||
//Stream::write("savegame.dat", data, int(ptr - data));
|
||||
delete[] data;
|
||||
|
||||
LOG("Ok\n");
|
||||
@@ -472,9 +472,10 @@ struct Level : IGame {
|
||||
float pitch = b.flags.pitch ? (0.9f + randf() * 0.2f) : 1.0f;
|
||||
if (!(flags & Sound::MUSIC)) {
|
||||
switch (b.flags.mode) {
|
||||
case 0 : flags |= Sound::UNIQUE; break;
|
||||
case 0 : if (level.version & TR::VER_TR1) flags |= Sound::UNIQUE; break; // TODO check this
|
||||
case 1 : flags |= Sound::REPLAY; break;
|
||||
case 2 : flags |= Sound::FLIPPED | Sound::UNFLIPPED | Sound::LOOP; break;
|
||||
case 2 : if (level.version & TR::VER_TR1) flags |= Sound::FLIPPED | Sound::UNFLIPPED | Sound::LOOP | Sound::UNIQUE; break;
|
||||
case 3 : if (!(level.version & TR::VER_TR1)) flags |= Sound::FLIPPED | Sound::UNFLIPPED | Sound::LOOP | Sound::UNIQUE; break;
|
||||
}
|
||||
}
|
||||
if (b.flags.gain) volume = max(0.0f, volume - randf() * 0.25f);
|
||||
@@ -560,7 +561,7 @@ struct Level : IGame {
|
||||
for (int i = 0; i < level.entitiesBaseCount; i++) {
|
||||
TR::Entity &e = level.entities[i];
|
||||
e.controller = initController(i);
|
||||
if (e.type == TR::Entity::LARA || e.type == TR::Entity::CUT_1)
|
||||
if (e.type == TR::Entity::LARA || ((level.version & TR::VER_TR1) && e.type == TR::Entity::CUT_1))
|
||||
lara = (Lara*)e.controller;
|
||||
}
|
||||
|
||||
@@ -684,7 +685,8 @@ struct Level : IGame {
|
||||
case TR::Entity::CRYSTAL : return new Crystal(this, index);
|
||||
case TR::Entity::TRAP_SWING_BLADE : return new TrapSwingBlade(this, index);
|
||||
case TR::Entity::TRAP_SPIKES : return new TrapSpikes(this, index);
|
||||
case TR::Entity::TRAP_BOULDER : return new TrapBoulder(this, index);
|
||||
case TR::Entity::TRAP_BOULDER :
|
||||
case TR::Entity::TRAP_BOULDERS : return new TrapBoulder(this, index);
|
||||
case TR::Entity::DART : return new Dart(this, index);
|
||||
case TR::Entity::TRAP_DART_EMITTER : return new TrapDartEmitter(this, index);
|
||||
case TR::Entity::DRAWBRIDGE : return new Drawbridge(this, index);
|
||||
@@ -732,7 +734,47 @@ struct Level : IGame {
|
||||
case TR::Entity::EARTHQUAKE : return new Earthquake(this, index);
|
||||
case TR::Entity::MUTANT_EGG_SMALL :
|
||||
case TR::Entity::MUTANT_EGG_BIG : return new MutantEgg(this, index);
|
||||
default : return (level.entities[index].modelIndex > 0) ? new Controller(this, index) : new Sprite(this, index, 0);
|
||||
|
||||
case TR::Entity::ENEMY_DOG : return new Dog(this, index);
|
||||
case TR::Entity::ENEMY_GOON_MASK_1 :
|
||||
case TR::Entity::ENEMY_GOON_MASK_2 :
|
||||
case TR::Entity::ENEMY_GOON_MASK_3 :
|
||||
case TR::Entity::ENEMY_GOON_KNIFE :
|
||||
case TR::Entity::ENEMY_GOON_SHOTGUN :
|
||||
case TR::Entity::ENEMY_RAT :
|
||||
case TR::Entity::ENEMY_DRAGON_FRONT :
|
||||
case TR::Entity::ENEMY_DRAGON_BACK :
|
||||
case TR::Entity::ENEMY_SHARK :
|
||||
case TR::Entity::ENEMY_MORAY_1 :
|
||||
case TR::Entity::ENEMY_MORAY_2 :
|
||||
case TR::Entity::ENEMY_BARACUDA :
|
||||
case TR::Entity::ENEMY_DIVER :
|
||||
case TR::Entity::ENEMY_GUNMAN_1 :
|
||||
case TR::Entity::ENEMY_GUNMAN_2 :
|
||||
case TR::Entity::ENEMY_GOON_STICK_1 :
|
||||
case TR::Entity::ENEMY_GOON_STICK_2 :
|
||||
case TR::Entity::ENEMY_GOON_FLAME :
|
||||
case TR::Entity::UNUSED_23 :
|
||||
case TR::Entity::ENEMY_SPIDER :
|
||||
case TR::Entity::ENEMY_SPIDER_GIANT :
|
||||
case TR::Entity::ENEMY_CROW :
|
||||
case TR::Entity::ENEMY_TIGER :
|
||||
case TR::Entity::ENEMY_MARCO :
|
||||
case TR::Entity::ENEMY_GUARD_SPEAR :
|
||||
case TR::Entity::ENEMY_GUARD_SPEAR_STATUE :
|
||||
case TR::Entity::ENEMY_GUARD_SWORD :
|
||||
case TR::Entity::ENEMY_GUARD_SWORD_STATUE :
|
||||
case TR::Entity::ENEMY_YETI :
|
||||
case TR::Entity::ENEMY_BIRD_MONSTER :
|
||||
case TR::Entity::ENEMY_EAGLE :
|
||||
case TR::Entity::ENEMY_MERCENARY_1 :
|
||||
case TR::Entity::ENEMY_MERCENARY_2 :
|
||||
case TR::Entity::ENEMY_MERCENARY_3 :
|
||||
case TR::Entity::ENEMY_MERCENARY_SNOWMOBILE :
|
||||
case TR::Entity::ENEMY_MONK_1 :
|
||||
case TR::Entity::ENEMY_MONK_2 : return new Enemy(this, index, 100, 10, 0.0f, 0.0f);
|
||||
|
||||
default : return (level.entities[index].modelIndex > 0) ? new Controller(this, index) : new Sprite(this, index, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1000,11 +1042,16 @@ struct Level : IGame {
|
||||
void renderRooms(int *roomsList, int roomsCount) {
|
||||
PROFILE_MARKER("ROOMS");
|
||||
|
||||
bool hasSky = false;
|
||||
|
||||
for (int i = 0; i < level.roomsCount; i++)
|
||||
level.rooms[i].flags.visible = false;
|
||||
|
||||
for (int i = 0; i < roomsCount; i++)
|
||||
level.rooms[roomsList[i]].flags.visible = true;
|
||||
for (int i = 0; i < roomsCount; i++) {
|
||||
TR::Room &r = level.rooms[roomsList[i]];
|
||||
hasSky |= r.flags.sky;
|
||||
r.flags.visible = true;
|
||||
}
|
||||
|
||||
if (Core::pass == Core::passShadow)
|
||||
return;
|
||||
@@ -1031,7 +1078,8 @@ struct Level : IGame {
|
||||
|
||||
Core::setBlending(bmNone);
|
||||
|
||||
renderSky();
|
||||
if (hasSky)
|
||||
renderSky();
|
||||
|
||||
for (int transp = 0; transp < 2; transp++) {
|
||||
for (int i = 0; i < roomsCount; i++) {
|
||||
@@ -1395,7 +1443,8 @@ struct Level : IGame {
|
||||
int roomsCount = 0;
|
||||
|
||||
getVisibleRooms(roomsList, roomsCount, TR::NO_ROOM, roomIndex, vec4(-1.0f, -1.0f, 1.0f, 1.0f), water);
|
||||
/* // show all rooms
|
||||
// show all rooms
|
||||
/*
|
||||
for (int i = 0; i < level.roomsCount; i++)
|
||||
roomsList[i] = i;
|
||||
roomsCount = level.roomsCount;
|
||||
|
25
src/mesh.h
25
src/mesh.h
@@ -90,6 +90,7 @@ struct Mesh {
|
||||
|
||||
void initRange(MeshRange &range) {
|
||||
if (Core::support.VAO) {
|
||||
ASSERT(aIndex < aCount);
|
||||
range.aIndex = aIndex++;
|
||||
range.bind(VAO);
|
||||
bind(true);
|
||||
@@ -158,6 +159,7 @@ struct MeshBuilder {
|
||||
struct RoomRange {
|
||||
MeshRange geometry[2]; // opaque & transparent
|
||||
MeshRange sprites;
|
||||
int split;
|
||||
MeshRange **meshes;
|
||||
} *rooms;
|
||||
struct ModelRange {
|
||||
@@ -192,10 +194,14 @@ struct MeshBuilder {
|
||||
int iCount = 0, vCount = 0;
|
||||
|
||||
// get size of mesh for rooms (geometry & sprites)
|
||||
int vStartRoom = vCount;
|
||||
|
||||
for (int i = 0; i < level.roomsCount; i++) {
|
||||
TR::Room &r = level.rooms[i];
|
||||
TR::Room::Data &d = r.data;
|
||||
|
||||
int vStartCount = vCount;
|
||||
|
||||
iCount += d.rCount * 6 + d.tCount * 3;
|
||||
vCount += d.rCount * 4 + d.tCount * 3;
|
||||
|
||||
@@ -214,6 +220,12 @@ struct MeshBuilder {
|
||||
|
||||
iCount += d.sCount * 6;
|
||||
vCount += d.sCount * 4;
|
||||
|
||||
if (vCount - vStartRoom > 0xFFFF) {
|
||||
vStartRoom = vStartCount;
|
||||
rooms[i].split = true;
|
||||
} else
|
||||
rooms[i].split = false;
|
||||
}
|
||||
|
||||
// get models info
|
||||
@@ -260,7 +272,7 @@ struct MeshBuilder {
|
||||
int aCount = 0;
|
||||
|
||||
// build rooms
|
||||
int vStartRoom = vCount;
|
||||
vStartRoom = vCount;
|
||||
aCount++;
|
||||
|
||||
for (int i = 0; i < level.roomsCount; i++) {
|
||||
@@ -268,6 +280,11 @@ struct MeshBuilder {
|
||||
TR::Room::Data &d = room.data;
|
||||
RoomRange &range = rooms[i];
|
||||
|
||||
if (range.split) {
|
||||
vStartRoom = vCount;
|
||||
aCount++;
|
||||
}
|
||||
|
||||
for (int transp = 0; transp < 2; transp++) { // opaque, opacity
|
||||
range.geometry[transp].vStart = vStartRoom;
|
||||
range.geometry[transp].iStart = iCount;
|
||||
@@ -478,9 +495,13 @@ struct MeshBuilder {
|
||||
|
||||
// initialize Vertex Arrays
|
||||
MeshRange rangeRoom;
|
||||
rangeRoom.vStart = vStartRoom;
|
||||
rangeRoom.vStart = 0;
|
||||
mesh->initRange(rangeRoom);
|
||||
for (int i = 0; i < level.roomsCount; i++) {
|
||||
if (rooms[i].split) {
|
||||
rangeRoom.vStart = rooms[i].geometry[0].vStart;
|
||||
mesh->initRange(rangeRoom);
|
||||
}
|
||||
RoomRange &r = rooms[i];
|
||||
r.geometry[0].aIndex = rangeRoom.aIndex;
|
||||
r.geometry[1].aIndex = rangeRoom.aIndex;
|
||||
|
@@ -348,8 +348,9 @@ struct Atlas {
|
||||
}
|
||||
|
||||
Texture* pack() {
|
||||
width = nextPow2(int(sqrtf(float(size))));
|
||||
height = (width * width / 2 > size) ? (width / 2) : width;
|
||||
// TODO TR2 fix CUT2 AV
|
||||
width = 2048;//nextPow2(int(sqrtf(float(size))));
|
||||
height = 2048;//(width * width / 2 > size) ? (width / 2) : width;
|
||||
// sort
|
||||
int *indices = new int[tilesCount];
|
||||
for (int i = 0; i < tilesCount; i++)
|
||||
|
Reference in New Issue
Block a user