diff --git a/README.md b/README.md index bc59cb8..82fda24 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,7 @@ Classic Tomb Raider open-source engine inspired by OpenTomb project http://opentomb.github.io/ [![License](https://img.shields.io/badge/License-BSD%202--Clause-orange.svg)](https://opensource.org/licenses/BSD-2-Clause) + +## Links +* [Discord channel](https://discord.gg/EF8JaQB) +* [Tomb Raider Forums thread](http://www.tombraiderforums.com/showthread.php?t=216618) diff --git a/src/camera.h b/src/camera.h index d9be322..393eb62 100644 --- a/src/camera.h +++ b/src/camera.h @@ -142,11 +142,13 @@ struct Camera : Controller { if (owner->velocity != 0.0f && advTimer < 0.0f && !Input::down[ikMouseL]) advTimer = -advTimer; + #ifndef LEVEL_EDITOR if (advTimer == 0.0f && advAngle != 0.0f) { float t = 10.0f * Core::deltaTime; advAngle.x = lerp(clampAngle(advAngle.x), 0.0f, t); advAngle.y = lerp(clampAngle(advAngle.y), 0.0f, t); } + #endif angle = owner->angle + advAngle; angle.z = 0.0f; diff --git a/src/character.h b/src/character.h index 2fb10ed..593c317 100644 --- a/src/character.h +++ b/src/character.h @@ -31,11 +31,26 @@ struct Character : Controller { float angleExt; float speed; + int zone; + int box; + + bool flying; + Collision collision; Character(IGame *game, int entity, int health) : Controller(game, entity), target(-1), health(health), tilt(0.0f), stand(STAND_GROUND), lastInput(0), velocity(0.0f), angleExt(0.0f) { animation.initOverrides(); rotHead = rotChest = quat(0, 0, 0, 1); + + flying = getEntity().type == TR::Entity::ENEMY_BAT; + updateZone(); + } + + void updateZone() { + TR::Level *level = game->getLevel(); + int dx, dz; + box = level->getSector(getRoomIndex(), int(pos.x), int(pos.z), dx, dz).boxIndex; + zone = flying ? level->zones[0].fly[box] : level->zones[0].ground1[box]; } void rotateY(float delta) { @@ -141,8 +156,10 @@ struct Character : Controller { Controller::update(); updateVelocity(); updatePosition(); - if (p != pos) + if (p != pos) { updateLights(); + updateZone(); + } } virtual void cmdJump(const vec3 &vel) { diff --git a/src/controller.h b/src/controller.h index 60a4e73..f0a8914 100644 --- a/src/controller.h +++ b/src/controller.h @@ -192,7 +192,7 @@ struct Controller { int floor = NO_OVERLAP; int delta = NO_OVERLAP; - TR::Overlap *o = &level->overlaps[b.overlap & 0x7FFF]; + TR::Overlap *o = &level->overlaps[b.overlap.index]; do { TR::Box &ob = level->boxes[o->boxIndex]; if (ob.contains(toX, toZ)) { // get min delta diff --git a/src/debug.h b/src/debug.h index 69ed5d6..6e64a10 100644 --- a/src/debug.h +++ b/src/debug.h @@ -185,7 +185,14 @@ namespace Debug { namespace Level { - void debugFloor(const TR::Level &level, int roomIndex, int x, int y, int z) { + void debugFloor(const TR::Level &level, int roomIndex, int x, int y, int z, int zone = -1) { + if (zone != -1) { + int dx, dz; + TR::Room::Sector &s = level.getSector(roomIndex, x, z, dx, dz); + if (zone != level.zones[0].ground1[s.boxIndex]) + return; + } + TR::Level::FloorInfo info; vec3 rf[4], rc[4], f[4], c[4]; @@ -270,23 +277,47 @@ namespace Debug { glEnd(); } + void blocks(const TR::Level &level) { + Core::setDepthTest(false); + char buf[64]; + + for (int j = 0; j < level.roomsCount; j++) { + TR::Room &r = level.rooms[j]; + + for (int z = 0; z < r.zSectors; z++) + for (int x = 0; x < r.xSectors; x++) { + TR::Room::Sector &s = r.sectors[x * r.zSectors + z]; + if (s.boxIndex != 0xFFFF) { + bool blockable = level.boxes[s.boxIndex].overlap.value & 0x8000; + bool block = level.boxes[s.boxIndex].overlap.value & 0x4000; + int floor = level.boxes[s.boxIndex].floor; + + if (blockable || block) { + sprintf(buf, "blocked: %s", block ? "true" : "false"); + Debug::Draw::text(vec3(r.info.x + x * 1024 + 512, floor, r.info.z + z * 1024 + 512), vec4(1, 1, 0, 1), buf); + } + } + } + } + + Core::setDepthTest(true); + } + void debugOverlaps(const TR::Level &level, int boxIndex) { glColor4f(1.0f, 1.0f, 0.0f, 0.25f); - TR::Overlap *o = &level.overlaps[level.boxes[boxIndex].overlap & 0x7FFF]; + TR::Overlap *o = &level.overlaps[level.boxes[boxIndex].overlap.index]; do { TR::Box &b = level.boxes[o->boxIndex]; debugBox(b); } while (!(o++)->end); } - void sectors(const TR::Level &level, int roomIndex, int y) { + void sectors(const TR::Level &level, int roomIndex, int y, int zone = -1) { TR::Room &room = level.rooms[roomIndex]; - // glDisable(GL_DEPTH_TEST); for (int z = 0; z < room.zSectors; z++) for (int x = 0; x < room.xSectors; x++) - debugFloor(level, roomIndex, room.info.x + x * 1024, y, room.info.z + z * 1024); - // glEnable(GL_DEPTH_TEST); + debugFloor(level, roomIndex, room.info.x + x * 1024, y, room.info.z + z * 1024, zone); } void rooms(const TR::Level &level, const vec3 &pos, int roomIndex) { @@ -350,6 +381,24 @@ namespace Debug { } } + void zones(const TR::Level &level, Lara *lara) { + Core::setDepthTest(false); + for (int i = 0; i < level.roomsCount; i++) + sectors(level, i, int(lara->pos.y), lara->zone); + Core::setDepthTest(true); + + char buf[64]; + for (int i = 0; i < level.entitiesCount; i++) { + TR::Entity &e = level.entities[i]; + + if (e.type < TR::Entity::LARA || e.type > TR::Entity::ENEMY_GIANT_MUTANT) + continue; + + sprintf(buf, "zone: %d", ((Character*)e.controller)->zone ); + Debug::Draw::text(vec3(e.x, e.y - 128, e.z), vec4(0, 1.0, 0.8, 1), buf); + } + } + void lights(const TR::Level &level, int room, Controller *lara) { // int roomIndex = level.entities[lara->entity].room; // int lightIndex = getLightIndex(lara->pos, roomIndex); @@ -394,14 +443,16 @@ namespace Debug { sm->getBox(true, m.rotation, box); Debug::Draw::box(offset + box.min - vec3(10.0f), offset + box.max + vec3(10.0f), vec4(1, 0, 0, 0.50)); } - /* - TR::Mesh *mesh = (TR::Mesh*)&level.meshData[level.meshOffsets[sm->mesh] / 2]; - { //if (mesh->collider.info || mesh->collider.flags) { + + if (!level.meshOffsets[sm->mesh]) continue; + const TR::Mesh &mesh = level.meshes[level.meshOffsets[sm->mesh]]; + + { char buf[255]; - sprintf(buf, "radius %d info %d flags %d", (int)mesh->collider.radius, (int)mesh->collider.info, (int)mesh->collider.flags); - Debug::Draw::text(offset + (min + max) * 0.5f, vec4(0.5, 0.5, 0.5, 1), buf); + sprintf(buf, "flags %d", (int)mesh.flags.value); + Debug::Draw::text(offset + (box.min + box.max) * 0.5f, vec4(0.5, 0.5, 1.0, 1), buf); } - */ + } } // dynamic objects @@ -466,13 +517,13 @@ namespace Debug { TR::Mesh *mesh = (TR::Mesh*)&level.meshes[offset]; //if (!mesh->flags) continue; Debug::Draw::sphere(matrix * joint * mesh->center, mesh->radius, vec4(0, 1, 1, 0.5f)); - /* + { //if (e.id != 0) { char buf[255]; - sprintf(buf, "(%d) radius %d flags %d", (int)e.type, (int)mesh->radius, (int)mesh->flags); + sprintf(buf, "(%d) radius %d flags %d", (int)e.type, (int)mesh->radius, (int)mesh->flags.value); Debug::Draw::text(matrix * joint * mesh->center, vec4(0.5, 1, 0.5, 1), buf); } - */ + } Debug::Draw::box(matrix, frame->box.min(), frame->box.max(), vec4(1.0)); @@ -587,7 +638,7 @@ namespace Debug { case_name(TR::Entity, ENEMY_RAT_WATER ); case_name(TR::Entity, ENEMY_REX ); case_name(TR::Entity, ENEMY_RAPTOR ); - case_name(TR::Entity, ENEMY_MUTANT ); + case_name(TR::Entity, ENEMY_MUTANT_1 ); case_name(TR::Entity, ENEMY_CENTAUR ); case_name(TR::Entity, ENEMY_MUMMY ); case_name(TR::Entity, ENEMY_LARSON ); diff --git a/src/format.h b/src/format.h index 9a7a8e4..e39e762 100644 --- a/src/format.h +++ b/src/format.h @@ -348,7 +348,12 @@ namespace TR { TR::Vertex center; uint16 radius; - uint16 flags; + union { + struct { + uint16 transparent:1, reserved:15; + }; + uint16 value; + } flags; int16 vCount; int16 rCount; int16 tCount; @@ -374,7 +379,7 @@ namespace TR { LARA_SHOTGUN = 2, LARA_MAGNUMS = 3, LARA_UZIS = 4, - + LARA_SPEC = 5, ENEMY_TWIN = 6, ENEMY_WOLF = 7, ENEMY_BEAR = 8, @@ -389,12 +394,19 @@ namespace TR { ENEMY_RAT_WATER = 17, ENEMY_REX = 18, ENEMY_RAPTOR = 19, - ENEMY_MUTANT = 20, - + ENEMY_MUTANT_1 = 20, + ENEMY_MUTANT_2 = 21, + ENEMY_MUTANT_3 = 22, ENEMY_CENTAUR = 23, ENEMY_MUMMY = 24, ENEMY_LARSON = 27, - + ENEMY_PIERRE = 28, + ENEMY_SKATEBOARD = 29, + ENEMY_SKATEBOY = 30, + ENEMY_COWBOY = 31, + ENEMY_MR_T = 32, + ENEMY_NATLA = 33, + ENEMY_GIANT_MUTANT = 34, TRAP_FLOOR = 35, TRAP_BLADE = 36, TRAP_SPIKES = 37, @@ -512,6 +524,13 @@ namespace TR { bool isBlock() { return type >= TR::Entity::BLOCK_1 && type <= TR::Entity::BLOCK_2; } + + static void fixOpaque(Type type, bool &opaque) { + if (type >= LARA && type <= ENEMY_GIANT_MUTANT && type != ENEMY_MUMMY && type != ENEMY_CENTAUR && type != ENEMY_MUTANT_1 && type != ENEMY_NATLA && type != DOOR_BIG_1 && type != DOOR_BIG_2) + opaque = true; + if (type == SWITCH || type == SWITCH_WATER) + opaque = true; + } }; struct Animation { @@ -668,7 +687,12 @@ namespace TR { uint32 minZ, maxZ; // Horizontal dimensions in global units uint32 minX, maxX; int16 floor; // Height value in global units - uint16 overlap; // Index into Overlaps[]. + union { + struct { + uint16 index:14, block:1, blockable:1; // Index into Overlaps[]. + }; + uint16 value; + } overlap; bool contains(uint32 x, uint32 z) { return x >= minX && x <= maxX && z >= minZ && z <= maxZ; @@ -676,11 +700,9 @@ namespace TR { }; struct Zone { - struct { - uint16 groundZone1; - uint16 groundZone2; - uint16 flyZone; - } normal, alternate; + uint16 *ground1; + uint16 *ground2; + uint16 *fly; }; struct SoundInfo { @@ -761,7 +783,7 @@ namespace TR { Box *boxes; int32 overlapsCount; Overlap *overlaps; - Zone *zones; + Zone zones[2]; // default and alternative int32 animTexturesDataSize; uint16 *animTexturesData; @@ -989,8 +1011,13 @@ namespace TR { stream.read(soundSources, stream.read(soundSourcesCount)); // AI stream.read(boxes, stream.read(boxesCount)); + stream.read(overlaps, stream.read(overlapsCount)); - stream.read(zones, boxesCount); + for (int i = 0; i < 2; i++) { + stream.read(zones[i].ground1, boxesCount); + stream.read(zones[i].ground2, boxesCount); + stream.read(zones[i].fly, boxesCount); + } // animated textures stream.read(animTexturesData, stream.read(animTexturesDataSize)); // entities (enemies, items, lara etc.) @@ -1112,7 +1139,11 @@ namespace TR { delete[] soundSources; delete[] boxes; delete[] overlaps; - delete[] zones; + for (int i = 0; i < 2; i++) { + delete[] zones[i].ground1; + delete[] zones[i].ground2; + delete[] zones[i].fly; + } delete[] animTexturesData; delete[] entities; delete[] palette; diff --git a/src/lara.h b/src/lara.h index cd75d33..788d28a 100644 --- a/src/lara.h +++ b/src/lara.h @@ -435,7 +435,35 @@ struct Lara : Character { delete braid; } + int getRoomByPos(const vec3 &pos) { + int x = int(pos.x), + y = int(pos.y), + z = int(pos.z); + + for (int i = 0; i < level->roomsCount; i++) { + TR::Room &r = level->rooms[i]; + int mx = r.info.x + r.xSectors * 1024; + int mz = r.info.z + r.zSectors * 1024; + if (x >= r.info.x && x < mx && z >= r.info.z && z < mz && y >= r.info.yTop && y < r.info.yBottom) + return i; + } + return TR::NO_ROOM; + } + void reset(int room, const vec3 &pos, float angle, bool onwater = false) { + if (room == TR::NO_ROOM) { + stand = STAND_AIR; + room = getRoomByPos(pos); + } + + if (room == TR::NO_ROOM) + return; + + if (level->rooms[room].flags.water) + stand = STAND_UNDERWATER; + + velocity = vec3(0.0f); + getEntity().room = room; this->pos = pos; this->angle = vec3(0.0f, angle, 0.0f); diff --git a/src/level.h b/src/level.h index fb91e03..983ad93 100644 --- a/src/level.h +++ b/src/level.h @@ -138,7 +138,9 @@ struct Level : IGame { case TR::Entity::ENEMY_RAT_WATER : case TR::Entity::ENEMY_REX : case TR::Entity::ENEMY_RAPTOR : - case TR::Entity::ENEMY_MUTANT : + case TR::Entity::ENEMY_MUTANT_1 : + case TR::Entity::ENEMY_MUTANT_2 : + case TR::Entity::ENEMY_MUTANT_3 : case TR::Entity::ENEMY_CENTAUR : case TR::Entity::ENEMY_MUMMY : case TR::Entity::ENEMY_LARSON : @@ -490,7 +492,7 @@ struct Level : IGame { // if (entity.type == TR::Entity::LARA && ((Lara*)entity.controller)->state == Lara::STATE_WATER_OUT) // roomIndex = ((Lara*)entity.controller)->roomPrev; - setRoomParams(roomIndex, type, 1.0f, intensityf(lum), controller->specular, 1.0f, isModel ? mesh->models[entity.modelIndex].opaque : true); + setRoomParams(roomIndex, type, 1.0f, intensityf(lum), controller->specular, 1.0f, isModel ? !mesh->models[entity.modelIndex - 1].opaque : true); if (isModel) { // model vec3 pos = controller->getPos(); @@ -517,6 +519,12 @@ struct Level : IGame { } void update() { + #ifdef LEVEL_EDITOR + if (Input::down[ikCtrl]) { + Input::down[ikCtrl] = false; + lara->reset(TR::NO_ROOM, camera->pos, camera->angle.y, false); + } + #endif params->time += Core::deltaTime; for (int i = 0; i < level.entitiesCount; i++) { @@ -810,20 +818,23 @@ struct Level : IGame { glEnd(); Core::setDepthTest(true); - Debug::Draw::sphere(lara->mainLightPos, lara->mainLightColor.w, vec4(1, 1, 0, 1)); + // Debug::Draw::sphere(lara->mainLightPos, lara->mainLightColor.w, vec4(1, 1, 0, 1)); Box bbox = lara->getBoundingBox(); Debug::Draw::box(bbox.min, bbox.max, vec4(1, 0, 1, 1)); Core::setBlending(bmAlpha); // Debug::Level::rooms(level, lara->pos, lara->getEntity().room); - Debug::Level::lights(level, lara->getRoomIndex(), lara); + // Debug::Level::lights(level, lara->getRoomIndex(), lara); // Debug::Level::sectors(level, lara->getRoomIndex(), (int)lara->pos.y); // Core::setDepthTest(false); // Debug::Level::portals(level); // Core::setDepthTest(true); // Debug::Level::meshes(level); // Debug::Level::entities(level); + Debug::Level::zones(level, lara); + Debug::Level::blocks(level); + Core::setBlending(bmNone); /* diff --git a/src/platform/web/index.html b/src/platform/web/index.html index a477a7c..5ff642f 100644 --- a/src/platform/web/index.html +++ b/src/platform/web/index.html @@ -120,10 +120,10 @@ (.PHD, .PSX)

- OpenLara on github
+ OpenLara on github & facebook
controls:
keyboad: move - WASD / arrows, jump - Space, action - E/Ctrl, draw weapon - Q, change weapon - 1-4, walk - Shift, side steps - ZX/walk+direction, camera - MouseR)
- gamepad: PSX controls on XBox controller
+ gamepad: PSX controls for Xbox controller
Change view: V
Time Control: R - slow motion, T - fast motion
FullScreen: Alt + Enter diff --git a/src/platform/win/OpenLara.vcxproj.filters b/src/platform/win/OpenLara.vcxproj.filters new file mode 100644 index 0000000..2c1879e --- /dev/null +++ b/src/platform/win/OpenLara.vcxproj.filters @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + shaders + + + shaders + + + shaders + + + shaders + + + shaders + + + shaders + + + shaders + + + + + {3fcb6c00-268a-4570-b6ca-3bf1cf1e10d7} + + + \ No newline at end of file diff --git a/src/platform/win/OpenLara.vcxproj.user b/src/platform/win/OpenLara.vcxproj.user new file mode 100644 index 0000000..a4941d7 --- /dev/null +++ b/src/platform/win/OpenLara.vcxproj.user @@ -0,0 +1,19 @@ + + + + ../../../bin + WindowsLocalDebugger + + + ../../../bin + WindowsLocalDebugger + + + ../../../bin + WindowsLocalDebugger + + + ../../../bin + WindowsLocalDebugger + + \ No newline at end of file