1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-06 13:16:52 +02:00

#3 underwater acceleration and tilt; #4 new collision detection system for walls (slide and rotate along walls); #8 first person view mode (V key); auto-rotate camera to the back while moving

This commit is contained in:
XProger
2017-02-15 04:11:26 +03:00
parent ab89e08549
commit d9fce4457d
15 changed files with 532 additions and 202 deletions

Binary file not shown.

View File

@@ -145,7 +145,7 @@ struct Animation {
TR::AnimRange &range = level->ranges[s.rangesOffset + j]; TR::AnimRange &range = level->ranges[s.rangesOffset + j];
if (anim->frameStart + frameIndex >= range.low && anim->frameStart + frameIndex <= range.high) { if (anim->frameStart + frameIndex >= range.low && anim->frameStart + frameIndex <= range.high) {
setAnim(range.nextAnimation - model->animation, range.nextFrame); setAnim(range.nextAnimation - model->animation, range.nextFrame);
break; return true;
} }
} }
} }

View File

@@ -13,28 +13,21 @@ struct Camera : Controller {
Frustum *frustum; Frustum *frustum;
float fov, znear, zfar; float fov, znear, zfar;
vec3 target, destPos, lastDest, angleAdv; vec3 target, destPos, lastDest, advAngle;
float advTimer;
mat4 mViewInv; mat4 mViewInv;
int room; int room;
float timer; float timer;
int actTargetEntity, actCamera; int actTargetEntity, actCamera;
bool cutscene; bool cutscene;
bool firstPerson;
Basis prevBasis;
vec4 *reflectPlane; vec4 *reflectPlane;
Camera(IGame *game, Lara *owner) : Controller(game, owner ? owner->entity : 0), owner(owner), frustum(new Frustum()), timer(0.0f), actTargetEntity(-1), actCamera(-1), reflectPlane(NULL) { Camera(IGame *game, Lara *owner) : Controller(game, owner ? owner->entity : 0), owner(owner), frustum(new Frustum()), timer(0.0f), actTargetEntity(-1), actCamera(-1), reflectPlane(NULL) {
fov = 65.0f; changeView(false);
znear = 16;
zfar = 40.0f * 1024.0f;
angleAdv = vec3(0.0f);
if (owner) {
room = owner->getEntity().room;
pos = pos - owner->getDir() * 1024.0f;
target = owner->getViewPoint();
}
cutscene = owner->getEntity().type != TR::Entity::LARA && level->cameraFrames; cutscene = owner->getEntity().type != TR::Entity::LARA && level->cameraFrames;
} }
@@ -46,6 +39,30 @@ struct Camera : Controller {
return actCamera > -1 ? level->cameras[actCamera].room : room; return actCamera > -1 ? level->cameras[actCamera].room : room;
} }
virtual void checkRoom() {
TR::Level::FloorInfo info;
level->getFloorInfo(room, (int)pos.x, (int)pos.y, (int)pos.z, info);
if (info.roomNext != TR::NO_ROOM)
room = info.roomNext;
if (pos.y < info.roomCeiling) {
if (info.roomAbove != TR::NO_ROOM)
room = info.roomAbove;
else
if (info.roomCeiling != 0xffff8100)
pos.y = (float)info.roomCeiling;
}
if (pos.y > info.roomFloor) {
if (info.roomBelow != TR::NO_ROOM)
room = info.roomBelow;
else
if (info.roomFloor != 0xffff8100)
pos.y = (float)info.roomFloor;
}
}
virtual bool activate(ActionCommand *cmd) { virtual bool activate(ActionCommand *cmd) {
Controller::activate(cmd); Controller::activate(cmd);
this->timer = max(max(1.0f, this->timer), cmd->timer); this->timer = max(max(1.0f, this->timer), cmd->timer);
@@ -88,18 +105,43 @@ struct Camera : Controller {
} else } else
#endif #endif
{ {
vec3 advAngleOld = advAngle;
if (Input::down[ikMouseL]) { if (Input::down[ikMouseL]) {
vec2 delta = Input::mouse.pos - Input::mouse.start.L; vec2 delta = Input::mouse.pos - Input::mouse.start.L;
angleAdv.x -= delta.y * 0.01f; advAngle.x -= delta.y * 0.01f;
angleAdv.y += delta.x * 0.01f; advAngle.y += delta.x * 0.01f;
Input::mouse.start.L = Input::mouse.pos; Input::mouse.start.L = Input::mouse.pos;
} }
angleAdv.x -= Input::joy.R.y * 2.0f * Core::deltaTime; advAngle.x -= Input::joy.R.y * 2.0f * Core::deltaTime;
angleAdv.y += Input::joy.R.x * 2.0f * Core::deltaTime; advAngle.y += Input::joy.R.x * 2.0f * Core::deltaTime;
angle = owner->angle + angleAdv; if (advAngleOld == advAngle) {
angle.z = 0.0f; if (advTimer > 0.0f) {
advTimer -= Core::deltaTime;
if (advTimer <= 0.0f)
advTimer = 0.0f;
}
} else
advTimer = -1.0f;
if (owner->velocity != 0.0f && advTimer < 0.0f && !Input::down[ikMouseL])
advTimer = -advTimer;
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);
}
angle = owner->angle + advAngle;
angle.z = 0.0f;
if (owner->stand == Lara::STAND_ONWATER)
angle.x -= 22.0f * DEG2RAD;
if (owner->state == Lara::STATE_HANG || owner->state == Lara::STATE_HANG_LEFT || owner->state == Lara::STATE_HANG_RIGHT)
angle.x -= 60.0f * DEG2RAD;
#ifdef LEVEL_EDITOR #ifdef LEVEL_EDITOR
angle = angleAdv; angle = angleAdv;
@@ -124,7 +166,6 @@ struct Camera : Controller {
return; return;
#endif #endif
int lookAt = -1; int lookAt = -1;
if (actTargetEntity > -1) lookAt = actTargetEntity; if (actTargetEntity > -1) lookAt = actTargetEntity;
if (owner->target > -1) lookAt = owner->target; if (owner->target > -1) lookAt = owner->target;
@@ -142,6 +183,25 @@ struct Camera : Controller {
} }
} }
if (firstPerson && actCamera == -1) {
Basis head = owner->animation.getJoints(owner->getMatrix(), 14, true);
Basis eye(quat(0.0f, 0.0f, 0.0f, 1.0f), vec3(0.0f, -40.0f, 10.0f));
eye = head * eye;
mViewInv.identity();
//prevBasis = prevBasis.lerp(eye, 15.0f * Core::deltaTime);
mViewInv.setRot(eye.rot);
mViewInv.setPos(eye.pos);
mViewInv.rotateY(advAngle.y);
mViewInv.rotateX(advAngle.x + PI);
pos = mViewInv.getPos();
checkRoom();
Sound::listener.matrix = mViewInv;
return;
}
float lerpFactor = (lookAt == -1) ? 6.0f : 10.0f; float lerpFactor = (lookAt == -1) ? 6.0f : 10.0f;
vec3 dir; vec3 dir;
target = target.lerp(owner->getViewPoint(), lerpFactor * Core::deltaTime); target = target.lerp(owner->getViewPoint(), lerpFactor * Core::deltaTime);
@@ -170,34 +230,13 @@ struct Camera : Controller {
} else { } else {
vec3 eye = lastDest + dir.cross(vec3(0, 1, 0)).normal() * 2048.0f - vec3(0.0f, 512.0f, 0.0f); vec3 eye = lastDest + dir.cross(vec3(0, 1, 0)).normal() * 2048.0f - vec3(0.0f, 512.0f, 0.0f);
destPos = trace(owner->getRoomIndex(), target, eye, destRoom, true); destPos = trace(owner->getRoomIndex(), target, eye, destRoom, true);
} }
room = destRoom; room = destRoom;
} }
pos = pos.lerp(destPos, Core::deltaTime * lerpFactor); pos = pos.lerp(destPos, Core::deltaTime * lerpFactor);
if (actCamera <= -1) { if (actCamera <= -1)
TR::Level::FloorInfo info; checkRoom();
level->getFloorInfo(room, (int)pos.x, (int)pos.y, (int)pos.z, info);
if (info.roomNext != 255)
room = info.roomNext;
if (pos.y < info.roomCeiling) {
if (info.roomAbove != 255)
room = info.roomAbove;
else
if (info.roomCeiling != 0xffff8100)
pos.y = (float)info.roomCeiling;
}
if (pos.y > info.roomFloor) {
if (info.roomBelow != 255)
room = info.roomBelow;
else
if (info.roomFloor != 0xffff8100)
pos.y = (float)info.roomFloor;
}
}
} }
mViewInv = mat4(pos, target, vec3(0, -1, 0)); mViewInv = mat4(pos, target, vec3(0, -1, 0));
@@ -226,6 +265,20 @@ struct Camera : Controller {
frustum->pos = Core::viewPos; frustum->pos = Core::viewPos;
frustum->calcPlanes(Core::mViewProj); frustum->calcPlanes(Core::mViewProj);
} }
void changeView(bool firstPerson) {
this->firstPerson = firstPerson;
room = owner->getRoomIndex();
pos = owner->pos - owner->getDir() * 1024.0f;
target = owner->getViewPoint();
advAngle = vec3(0.0f);
advTimer = 0.0f;
fov = firstPerson ? 90.0f : 65.0f;
znear = firstPerson ? 8.0f : 16.0f;
zfar = 40.0f * 1024.0f;
}
}; };
#endif #endif

View File

@@ -29,12 +29,24 @@ struct Character : Controller {
vec3 velocity; vec3 velocity;
float angleExt; float angleExt;
float speed;
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) { 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) {
animation.initOverrides(); animation.initOverrides();
rotHead = rotChest = quat(0, 0, 0, 1); rotHead = rotChest = quat(0, 0, 0, 1);
} }
void rotateY(float delta) {
angle.y += delta;
velocity = velocity.rotateY(-delta);
}
void rotateX(float delta) {
angle.x = clamp(angle.x + delta, -PI * 0.49f, PI * 0.49f);
}
virtual void hit(int damage, Controller *enemy = NULL) { virtual void hit(int damage, Controller *enemy = NULL) {
health -= damage; health -= damage;
}; };
@@ -44,13 +56,13 @@ struct Character : Controller {
TR::Entity &e = getEntity(); TR::Entity &e = getEntity();
level->getFloorInfo(e.room, e.x, e.y, e.z, info); level->getFloorInfo(e.room, e.x, e.y, e.z, info);
if (info.roomNext != 0xFF) if (info.roomNext != TR::NO_ROOM)
e.room = info.roomNext; e.room = info.roomNext;
if (info.roomBelow != 0xFF && e.y > info.roomFloor) if (info.roomBelow != TR::NO_ROOM && e.y > info.roomFloor)
e.room = info.roomBelow; e.room = info.roomBelow;
if (info.roomAbove != 0xFF && e.y <= info.roomCeiling) { if (info.roomAbove != TR::NO_ROOM && e.y <= info.roomCeiling) {
if (stand == STAND_UNDERWATER && !level->rooms[info.roomAbove].flags.water) { if (stand == STAND_UNDERWATER && !level->rooms[info.roomAbove].flags.water) {
stand = STAND_ONWATER; stand = STAND_ONWATER;
velocity.y = 0; velocity.y = 0;
@@ -106,18 +118,13 @@ struct Character : Controller {
virtual void updateTilt(bool active, float tiltSpeed, float tiltMax) { virtual void updateTilt(bool active, float tiltSpeed, float tiltMax) {
// calculate turning tilt // calculate turning tilt
if (active && (input & (LEFT | RIGHT)) && (tilt == 0.0f || (tilt < 0.0f && (input & LEFT)) || (tilt > 0.0f && (input & RIGHT)))) { if (active && (input & (LEFT | RIGHT)) && (tilt == 0.0f || (tilt < 0.0f && (input & LEFT)) || (tilt > 0.0f && (input & RIGHT)))) {
if (input & LEFT) tilt -= tiltSpeed * Core::deltaTime; if (input & LEFT) tilt -= tiltSpeed;
if (input & RIGHT) tilt += tiltSpeed * Core::deltaTime; if (input & RIGHT) tilt += tiltSpeed;
} else tilt = clamp(tilt, -tiltMax, +tiltMax);
if (fabsf(tilt) > 0.01f) { } else {
if (tilt > 0.0f) if (tilt > 0.0f) tilt = max(0.0f, tilt - tiltSpeed);
tilt -= min(tilt, tiltSpeed * 4.0f * Core::deltaTime); if (tilt < 0.0f) tilt = min(0.0f, tilt + tiltSpeed);
else }
tilt -= max(tilt, -tiltSpeed * 4.0f * Core::deltaTime);
} else
tilt = 0.0f;
tilt = clamp(tilt, -tiltMax, tiltMax);
angle.z = tilt; angle.z = tilt;
} }

104
src/collision.h Normal file
View File

@@ -0,0 +1,104 @@
#ifndef H_COLLISION
#define H_COLLISION
#include "core.h"
#include "utils.h"
#include "format.h"
struct Collision {
enum Side { NONE, LEFT, RIGHT, FRONT, TOP, BOTTOM } side;
struct Info {
int room, roomAbove, roomBelow, floor, ceiling;
} info[4];
Collision() : side(NONE) {}
Collision(TR::Level *level, int room, vec3 &pos, const vec3 &offset, const vec3 &velocity, float radius, float angle, int minHeight, int maxHeight, int maxAscent, int maxDescent) {
if (velocity.x > 0.0f || velocity.z > 0.0f)
angle = normalizeAngle(PI2 + vec2(velocity.z, velocity.x).angle());
pos += velocity;
int q = angleQuadrant(angle);
const vec2 v[] = {
{ -radius, radius },
{ radius, radius },
{ radius, -radius },
{ -radius, -radius },
};
const vec2 &l = v[q], &r = v[(q + 1) % 4];
vec2 f = (q %= 2) ? vec2(l.x, radius * cosf(angle)) : vec2(radius * sinf(angle), l.y),
p(pos.x, pos.z),
d(0.0F);
vec3 hpos = pos + offset;
int height = maxHeight - minHeight;
if (checkHeight(level, room, hpos, vec2(0.0f), height, 0xFFFFFF, 0xFFFFFF, side = NONE)) {
pos -= velocity;
side = FRONT;
return;
}
if (info[NONE].ceiling > hpos.y - maxHeight) {
pos.y = info[NONE].ceiling + maxHeight - offset.y;
side = TOP;
}
if (info[NONE].floor < hpos.y + minHeight) {
pos.y = info[NONE].floor - minHeight - offset.y;
side = BOTTOM;
}
if (checkHeight(level, room, hpos, f, height, maxAscent, maxDescent, FRONT)) {
d = vec2(-velocity.x, -velocity.z);
q ^= 1;
d[q] = getOffset(p[q] + f[q], p[q]);
} else if (checkHeight(level, room, hpos, l, height, maxAscent, maxDescent, LEFT)) {
d[q] = getOffset(p[q] + l[q], p[q] + f[q]);
} else if (checkHeight(level, room, hpos, r, height, maxAscent, maxDescent, RIGHT)) {
d[q] = getOffset(p[q] + r[q], p[q] + f[q]);
} else
return;
pos += vec3(d.x, 0.0f, d.y);
}
inline bool checkHeight(TR::Level *level, int room, const vec3 &pos, const vec2 &offset, int height, int maxAscent, int maxDescent, Side side) {
TR::Level::FloorInfo info;
int py = int(pos.y);
level->getFloorInfo(room, int(pos.x + offset.x), py, int(pos.z + offset.y), info);
Info &inf = this->info[side];
inf.room = info.roomNext != TR::NO_ROOM ? info.roomNext : room;
inf.roomAbove = info.roomAbove;
inf.roomBelow = info.roomBelow;
inf.floor = info.floor;
inf.ceiling = info.ceiling;
if ((info.ceiling == info.floor) || (info.floor - info.ceiling < height) || (py - info.floor > maxAscent) || (info.floor - py > maxDescent) || (info.ceiling > py)) {
this->side = side;
return true;
}
return false;
}
inline float getOffset(float from, float to) {
int a = int(from) / 1024;
int b = int(to) / 1024;
from -= float(a * 1024.0f);
if (b == a)
return 0.0f;
else if (b > a)
return -from + 1025.0f;
return -from - 1.0f;
}
};
#endif

View File

@@ -5,6 +5,7 @@
#include "frustum.h" #include "frustum.h"
#include "mesh.h" #include "mesh.h"
#include "animation.h" #include "animation.h"
#include "collision.h"
#define GRAVITY (6.0f * 30.0f) #define GRAVITY (6.0f * 30.0f)
#define NO_OVERLAP 0x7FFFFFFF #define NO_OVERLAP 0x7FFFFFFF
@@ -119,13 +120,14 @@ struct Controller {
return false; return false;
} }
void updateEntity() { void updateEntity() {
TR::Entity &e = getEntity(); TR::Entity &e = getEntity();
e.x = int(pos.x); e.x = int(pos.x);
e.y = int(pos.y); e.y = int(pos.y);
e.z = int(pos.z); e.z = int(pos.z);
while (angle.y < 0.0f) angle.y += 2 * PI; angle.y = normalizeAngle(angle.y);
while (angle.y > 2 * PI) angle.y -= 2 * PI;
e.rotation = angle.y; e.rotation = angle.y;
} }
@@ -220,26 +222,18 @@ struct Controller {
} }
void alignToWall(float offset = 0.0f) { void alignToWall(float offset = 0.0f) {
float fx = pos.x / 1024.0f; int q = angleQuadrant(angle.y);
float fz = pos.z / 1024.0f; int x = int(pos.x) & ~1023;
fx -= (int)fx; int z = int(pos.z) & ~1023;
fz -= (int)fz;
int k; switch (q) {
if (fx > 1.0f - fz) case 0 : pos.z = z + 1024 + offset; break;
k = fx < fz ? 0 : 1; case 1 : pos.x = x + 1024 + offset; break;
else case 2 : pos.z = z - offset; break;
k = fx < fz ? 3 : 2; case 3 : pos.x = x - offset; break;
angle.y = k * PI * 0.5f; // clamp angle to n*PI/2
if (offset != 0.0f) {
vec3 dir = getDir() * (512.0f - offset);
if (k % 2)
pos.x = int(pos.x / 1024.0f) * 1024.0f + 512.0f + dir.x;
else
pos.z = int(pos.z / 1024.0f) * 1024.0f + 512.0f + dir.z;
} }
angle.y = q * (PI * 0.5f);
updateEntity(); updateEntity();
} }
@@ -264,7 +258,7 @@ struct Controller {
if (lr != room || lx != sx || lz != sz) { if (lr != room || lx != sx || lz != sz) {
level->getFloorInfo(room, sx, py, sz, info); level->getFloorInfo(room, sx, py, sz, info);
if (info.roomNext != 0xFF) { if (info.roomNext != TR::NO_ROOM) {
room = info.roomNext; room = info.roomNext;
level->getFloorInfo(room, sx, py, sz, info); level->getFloorInfo(room, sx, py, sz, info);
} }
@@ -274,9 +268,9 @@ struct Controller {
} }
if (isCamera) { if (isCamera) {
if (py > info.floor && info.roomBelow != 0xFF) if (py > info.floor && info.roomBelow != TR::NO_ROOM)
room = info.roomBelow; room = info.roomBelow;
else if (py < info.ceiling && info.roomAbove != 0xFF) else if (py < info.ceiling && info.roomAbove != TR::NO_ROOM)
room = info.roomAbove; room = info.roomAbove;
else if (py > info.floor || py < info.ceiling) { else if (py > info.floor || py < info.ceiling) {
int minX = px / 1024 * 1024; int minX = px / 1024 * 1024;
@@ -289,14 +283,14 @@ struct Controller {
} }
} else { } else {
if (py > info.floor) { if (py > info.floor) {
if (info.roomBelow != 0xFF) if (info.roomBelow != TR::NO_ROOM)
room = info.roomBelow; room = info.roomBelow;
else else
break; break;
} }
if (py < info.ceiling) { if (py < info.ceiling) {
if (info.roomAbove != 0xFF) if (info.roomAbove != TR::NO_ROOM)
room = info.roomAbove; room = info.roomAbove;
else else
break; break;

View File

@@ -179,7 +179,7 @@ struct Wolf : Enemy {
return (state == STATE_STOP || state == STATE_SLEEP) ? STATE_SLEEP : STATE_STOP; return (state == STATE_STOP || state == STATE_SLEEP) ? STATE_SLEEP : STATE_STOP;
switch (state) { switch (state) {
case STATE_SLEEP : return target > -1 ? STATE_STOP : state; case STATE_SLEEP : return (target > -1 && level->entities[target].room == getRoomIndex()) ? STATE_STOP : state;
case STATE_STOP : return target > -1 ? STATE_HOWL : STATE_SLEEP; case STATE_STOP : return target > -1 ? STATE_HOWL : STATE_SLEEP;
case STATE_HOWL : return state; case STATE_HOWL : return state;
case STATE_GROWL : return target > -1 ? (randf() > 0.5f ? STATE_STALKING : STATE_RUN) : STATE_STOP; case STATE_GROWL : return target > -1 ? (randf() > 0.5f ? STATE_STALKING : STATE_RUN) : STATE_STOP;

View File

@@ -1551,8 +1551,8 @@ namespace TR {
int sx = x - room.info.x; int sx = x - room.info.x;
int sz = z - room.info.z; int sz = z - room.info.z;
sx = clamp(sx, 0, (room.xSectors - 1) * 1024); sx = clamp(sx, 0, room.xSectors * 1024 - 1);
sz = clamp(sz, 0, (room.zSectors - 1) * 1024); sz = clamp(sz, 0, room.zSectors * 1024 - 1);
dx = sx % 1024; dx = sx % 1024;
dz = sz % 1024; dz = sz % 1024;
@@ -1584,13 +1584,13 @@ namespace TR {
return; return;
Room::Sector *sBelow = &s; Room::Sector *sBelow = &s;
while (sBelow->roomBelow != 0xFF) sBelow = &getSector(sBelow->roomBelow, x, z, dx, dz); while (sBelow->roomBelow != TR::NO_ROOM) sBelow = &getSector(sBelow->roomBelow, x, z, dx, dz);
info.floor = 256 * sBelow->floor; info.floor = 256 * sBelow->floor;
parseFloorData(info, sBelow->floorIndex, dx, dz); parseFloorData(info, sBelow->floorIndex, dx, dz);
if (info.roomNext == 0xFF) { if (info.roomNext == TR::NO_ROOM) {
Room::Sector *sAbove = &s; Room::Sector *sAbove = &s;
while (sAbove->roomAbove != 0xFF) sAbove = &getSector(sAbove->roomAbove, x, z, dx, dz); while (sAbove->roomAbove != TR::NO_ROOM) sAbove = &getSector(sAbove->roomAbove, x, z, dx, dz);
if (sAbove != sBelow) { if (sAbove != sBelow) {
info.ceiling = 256 * sAbove->ceiling; info.ceiling = 256 * sAbove->ceiling;
parseFloorData(info, sAbove->floorIndex, dx, dz); parseFloorData(info, sAbove->floorIndex, dx, dz);

View File

@@ -39,13 +39,21 @@ namespace Game {
void update() { void update() {
float dt = Core::deltaTime; float dt = Core::deltaTime;
if (Input::down[ikV]) { // third <-> first person view
level->camera->changeView(!level->camera->firstPerson);
Input::down[ikV] = false;
}
if (Input::down[ikR]) // slow motion (for animation debugging) if (Input::down[ikR]) // slow motion (for animation debugging)
Core::deltaTime /= 10.0f; Core::deltaTime /= 10.0f;
if (Input::down[ikT])
Core::deltaTime *= 10.0f; if (Input::down[ikT]) // fast motion
for (int i = 0; i < 9; i++)
level->update();
level->update(); level->update();
Core::deltaTime = dt; Core::deltaTime = dt;
} }

View File

@@ -12,16 +12,25 @@
#define TURN_FAST_BACK PI * 3.0f / 4.0f #define TURN_FAST_BACK PI * 3.0f / 4.0f
#define TURN_NORMAL PI / 2.0f #define TURN_NORMAL PI / 2.0f
#define TURN_SLOW PI / 3.0f #define TURN_SLOW PI / 3.0f
#define TURN_WATER_FAST PI * 3.0f / 4.0f #define TURN_WATER_FAST (DEG2RAD * 150.0f)
#define TURN_WATER_SLOW PI * 2.0f / 3.0f #define TURN_WATER_SLOW (DEG2RAD * 60.0f)
#define TURN_WALL_Y (DEG2RAD * 150.0f)
#define TURN_WALL_X (DEG2RAD * 60.0f)
#define TURN_WALL_X_CLAMP (DEG2RAD * 35.0f)
#define TILT_MAX (PI / 18.0f) #define LARA_TILT_SPEED (DEG2RAD * 37.5f)
#define TILT_SPEED TILT_MAX #define LARA_TILT_MAX (DEG2RAD * 10.0f)
#define GLIDE_SPEED 35.0f #define LARA_HANG_OFFSET 724
#define SWIM_SPEED 45.0f #define LARA_HEIGHT 762
#define LARA_HEIGHT_WATER 400
#define LARA_RADIUS 100.0f
#define LARA_RADIUS_WATER 300.0f
#define LARA_HANG_OFFSET 724.0f #define LARA_WATER_ACCEL 2.0f
#define LARA_SURF_SPEED 15.0f
#define LARA_SWIM_SPEED 50.0f
#define LARA_SWIM_FRICTION 1.0f
#define LARA_WET_SPECULAR 0.5f #define LARA_WET_SPECULAR 0.5f
#define LARA_WET_TIMER (LARA_WET_SPECULAR / 16.0f) // 4 sec #define LARA_WET_TIMER (LARA_WET_SPECULAR / 16.0f) // 4 sec
@@ -232,8 +241,8 @@ struct Lara : Character {
for (int i = 0; i < jointsCount - 1; i++) { for (int i = 0; i < jointsCount - 1; i++) {
TR::Node &t = node[min(i, model->mCount - 2)]; TR::Node &t = node[min(i, model->mCount - 2)];
joints[i].posPrev = joints[i].pos = basis.pos; joints[i].posPrev = joints[i].pos = basis.pos;
joints[i].length = t.z; joints[i].length = float(t.z);
basis.translate(vec3(0.0f, 0.0f, -t.z)); basis.translate(vec3(0.0f, 0.0f, -joints[i].length));
} }
joints[jointsCount - 1].posPrev = joints[jointsCount - 1].pos = basis.pos; joints[jointsCount - 1].posPrev = joints[jointsCount - 1].pos = basis.pos;
joints[jointsCount - 1].length = 1.0f; joints[jointsCount - 1].length = 1.0f;
@@ -397,7 +406,7 @@ struct Lara : Character {
if (level->extra.braid > -1) if (level->extra.braid > -1)
braid = new Braid(this, vec3(-4.0f, 24.0f, -48.0f)); braid = new Braid(this, vec3(-4.0f, 24.0f, -48.0f));
/* /*
pos = vec3(40448, 3584, 60928); pos = vec3(40448, 3584, 60928);
angle = vec3(0.0f, PI * 0.5f, 0.0f); angle = vec3(0.0f, PI * 0.5f, 0.0f);
getEntity().room = 14; getEntity().room = 14;
@@ -434,12 +443,12 @@ struct Lara : Character {
angle = vec3(0.0f, -PI * 0.25f, 0.0f); angle = vec3(0.0f, -PI * 0.25f, 0.0f);
getEntity().room = 13; getEntity().room = 13;
// level 2 (room 43) // level 2 (reach)
pos = vec3(31400, -2560, 25200); pos = vec3(31400, -2560, 25200);
angle = vec3(0.0f, PI, 0.0f); angle = vec3(0.0f, PI, 0.0f);
getEntity().room = 43; getEntity().room = 43;
// level 2 (room 16) // level 2 (hang & climb)
pos = vec3(60907, 0, 39642); pos = vec3(60907, 0, 39642);
angle = vec3(0.0f, PI * 3 / 2, 0.0f); angle = vec3(0.0f, PI * 3 / 2, 0.0f);
getEntity().room = 16; getEntity().room = 16;
@@ -1074,6 +1083,13 @@ struct Lara : Character {
wpnHide(); wpnHide();
} }
virtual void cmdJump(const vec3 &vel) {
vec3 v = vel;
if (state == STATE_HANG_UP)
v.y = (3.0f - sqrtf(-2.0f * GRAVITY / 30.0f * (collision.info[Collision::FRONT].floor - pos.y + 800.0f)));
Character::cmdJump(v);
}
void drawGun(int right) { 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) if (layers[1].mask & mask)
@@ -1100,22 +1116,21 @@ struct Lara : Character {
bool waterOut() { bool waterOut() {
// TODO: playSound 36 // TODO: playSound 36
vec3 dst = pos + getDir() * 32.0f; if (collision.side != Collision::FRONT || pos.y - collision.info[Collision::FRONT].floor > 256 + 128)
return false;
TR::Level::FloorInfo infoCur, infoDst; vec3 dst = pos + getDir() * (LARA_RADIUS + 32.0f);
level->getFloorInfo(getEntity().room, (int)pos.x, (int)pos.y, (int)pos.z, infoCur),
level->getFloorInfo(infoCur.roomAbove, (int)dst.x, (int)dst.y, (int)dst.z, infoDst);
int h = int(pos.y - infoDst.floor); TR::Level::FloorInfo info;
level->getFloorInfo(collision.info[Collision::NONE].roomAbove, int(dst.x), int(dst.y), int(dst.z), info);
if (h >= 0 && h <= 356 && (state == STATE_SURF_TREAD || animation.setState(STATE_SURF_TREAD)) && animation.setState(STATE_STOP)) { // possibility check int h = int(pos.y - info.floor);
alignToWall(-96.0f);
pos.y = float(infoDst.floor); if (h >= 0 && h <= (256 + 128) && (state == STATE_SURF_TREAD || animation.setState(STATE_SURF_TREAD)) && animation.setState(STATE_STOP)) {
//pos = dst; // set new position alignToWall(LARA_RADIUS);
getEntity().room = collision.info[Collision::NONE].roomAbove;
pos.y = float(info.floor);
specular = LARA_WET_SPECULAR; specular = LARA_WET_SPECULAR;
getEntity().room = infoCur.roomAbove;
updateEntity(); updateEntity();
return true; return true;
} }
@@ -1353,12 +1368,35 @@ struct Lara : Character {
velocity.x = velocity.z = 0.0f; velocity.x = velocity.z = 0.0f;
if ((state == STATE_REACH || state == STATE_UP_JUMP) && (input & ACTION) && emptyHands()) { if ((state == STATE_REACH || state == STATE_UP_JUMP) && (input & ACTION) && emptyHands()) {
if (state == STATE_REACH && velocity.y < 0.0f) if (state == STATE_REACH && velocity.y < 0.0f)
return state; return state;
vec3 p = pos;
Collision c = Collision(level, getRoomIndex(), p, getDir() * 32.0f, vec3(0.0f), LARA_RADIUS, angleExt, 0, 0, 0, 0);
if (c.side != Collision::FRONT)
return state;
Box bounds = animation.getBoundingBox(pos, 0); Box bounds = animation.getBoundingBox(pos, 0);
int floor = c.info[Collision::FRONT].floor;
int hands = int(bounds.min.y);
if (abs(floor - hands) < 32) {
alignToWall(-LARA_RADIUS);
pos.y = float(floor + LARA_HANG_OFFSET);
stand = STAND_HANG;
updateEntity();
if (state == STATE_REACH) {
return STATE_HANG; // TODO: ANIM_HANG_WALL / ANIM_HANG_NOWALL
} else
return animation.setAnim(ANIM_HANG, -15);
}
/*
vec3 p = pos + getDir() * 128.0f; vec3 p = pos + getDir() * 128.0f;
TR::Level::FloorInfo info; TR::Level::FloorInfo info;
// TODO: use brain // TODO: use brain
@@ -1372,7 +1410,7 @@ struct Lara : Character {
} while (info.ceiling > bounds.min.y && info.roomAbove != 0xFF); } while (info.ceiling > bounds.min.y && info.roomAbove != 0xFF);
if (abs(info.floor - int(bounds.min.y)) < 16) { // reach fall if (abs(info.floor - int(bounds.min.y)) < 16) { // reach fall
alignToWall(96.0f); alignToWall(LARA_RADIUS);
pos.y = info.floor + 724.0f; pos.y = info.floor + 724.0f;
updateEntity(); updateEntity();
@@ -1382,6 +1420,7 @@ struct Lara : Character {
} else } else
return animation.setAnim(ANIM_HANG, -15); return animation.setAnim(ANIM_HANG, -15);
} }
*/
} }
if (state == STATE_FORWARD_JUMP) { if (state == STATE_FORWARD_JUMP) {
@@ -1409,7 +1448,7 @@ struct Lara : Character {
if ((dx <= (512 + 128) && dz <= (512 - 128)) || if ((dx <= (512 + 128) && dz <= (512 - 128)) ||
(dx <= (512 - 128) && dz <= (512 + 128))) { (dx <= (512 - 128) && dz <= (512 + 128))) {
alignToWall(); alignToWall(-LARA_RADIUS);
Block *block = (Block*)e.controller; Block *block = (Block*)e.controller;
block->angle.y = angle.y; block->angle.y = angle.y;
@@ -1427,26 +1466,22 @@ struct Lara : Character {
if ((input & ACTION) && emptyHands() && doPickUp()) if ((input & ACTION) && emptyHands() && doPickUp())
return STATE_PICK_UP; return STATE_PICK_UP;
if ((input & (FORTH | ACTION)) == (FORTH | ACTION) && (animation.index == ANIM_STAND || animation.index == ANIM_STAND_NORMAL) && emptyHands()) { // TODO: get rid of animation.index if ((input & (FORTH | ACTION)) == (FORTH | ACTION) && (animation.index == ANIM_STAND || animation.index == ANIM_STAND_NORMAL) && emptyHands() && collision.side == Collision::FRONT) { // TODO: get rid of animation.index
vec3 p = pos + getDir() * 64.0f; int floor = collision.info[Collision::FRONT].floor;
TR::Level::FloorInfo info; int h = (int)pos.y - floor;
level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.y, (int)p.z, info);
int h = (int)pos.y - info.floor;
int aIndex = animation.index; int aIndex = animation.index;
if (info.floor == info.ceiling || h < 256 + 128) { if (h <= 2 * 256 + 128) {
; // do nothing
} else if (h <= 2 * 256 + 128) {
aIndex = ANIM_CLIMB_2; aIndex = ANIM_CLIMB_2;
pos.y = info.floor + 512.0f; pos.y = floor + 512.0f;
} else if (h <= 3 * 256 + 128) { } else if (h <= 3 * 256 + 128) {
aIndex = ANIM_CLIMB_3; aIndex = ANIM_CLIMB_3;
pos.y = info.floor + 768.0f; pos.y = floor + 768.0f;
} else if (h <= 7 * 256 + 128) } else if (h <= 7 * 256 + 128)
aIndex = ANIM_CLIMB_JUMP; aIndex = ANIM_CLIMB_JUMP;
if (aIndex != animation.index) { if (aIndex != animation.index) {
alignToWall(96.0f); alignToWall(-LARA_RADIUS);
return animation.setAnim(aIndex); return animation.setAnim(aIndex);
} }
} }
@@ -1490,7 +1525,7 @@ struct Lara : Character {
int pushState = (input & FORTH) ? STATE_PUSH_BLOCK : STATE_PULL_BLOCK; int pushState = (input & FORTH) ? STATE_PUSH_BLOCK : STATE_PULL_BLOCK;
Block *block = getBlock(); Block *block = getBlock();
if (animation.canSetState(pushState) && block->doMove((input & FORTH) != 0)) { if (animation.canSetState(pushState) && block->doMove((input & FORTH) != 0)) {
alignToWall(128.0f); alignToWall(-LARA_RADIUS);
return pushState; return pushState;
} }
} }
@@ -1553,9 +1588,9 @@ struct Lara : Character {
if (input & FORTH) { if (input & FORTH) {
// possibility check // possibility check
TR::Level::FloorInfo info; TR::Level::FloorInfo info;
vec3 p = pos + getDir() * 128.0f; vec3 p = pos + getDir() * (LARA_RADIUS + 2.0f);
level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.y, (int)p.z, info); level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.y, (int)p.z, info);
if (info.floor - info.ceiling >= 768) if (info.floor - info.ceiling >= LARA_HEIGHT)
return (input & WALK) ? STATE_HANDSTAND : STATE_HANG_UP; return (input & WALK) ? STATE_HANDSTAND : STATE_HANG_UP;
} }
return STATE_HANG; return STATE_HANG;
@@ -1568,6 +1603,8 @@ struct Lara : Character {
if (state == STATE_FORWARD_JUMP || state == STATE_UP_JUMP || state == STATE_BACK_JUMP || state == STATE_LEFT_JUMP || state == STATE_RIGHT_JUMP || state == STATE_FALL || state == STATE_REACH) { if (state == STATE_FORWARD_JUMP || state == STATE_UP_JUMP || state == STATE_BACK_JUMP || state == STATE_LEFT_JUMP || state == STATE_RIGHT_JUMP || state == STATE_FALL || state == STATE_REACH) {
game->waterDrop(pos, 256.0f, 0.2f); game->waterDrop(pos, 256.0f, 0.2f);
Sprite::add(game, TR::Entity::WATER_SPLASH, getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z); Sprite::add(game, TR::Entity::WATER_SPLASH, getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z);
pos.y += 100.0f;
angle.x = -45.0f * DEG2RAD;
return animation.setAnim(ANIM_WATER_FALL); // TODO: wronng animation return animation.setAnim(ANIM_WATER_FALL); // TODO: wronng animation
} }
@@ -1577,10 +1614,13 @@ struct Lara : Character {
Sprite::add(game, TR::Entity::WATER_SPLASH, getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z); Sprite::add(game, TR::Entity::WATER_SPLASH, getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z);
return STATE_DIVE; return STATE_DIVE;
} }
if (input & JUMP) return STATE_SWIM; if (input & JUMP) return STATE_SWIM;
if (state == STATE_GLIDE && speed < LARA_SWIM_SPEED * 2.0f / 3.0f)
return STATE_TREAD;
return (state == STATE_SWIM || velocity.y > GLIDE_SPEED) ? STATE_GLIDE : STATE_TREAD; return STATE_GLIDE;
} }
virtual int getStateOnwater() { virtual int getStateOnwater() {
@@ -1709,9 +1749,9 @@ struct Lara : Character {
if (state == STATE_SWIM || state == STATE_GLIDE) if (state == STATE_SWIM || state == STATE_GLIDE)
w *= TURN_WATER_FAST; w *= TURN_WATER_FAST;
else if (state == STATE_TREAD || state == STATE_SURF_TREAD || state == STATE_SURF_SWIM || state == STATE_SURF_BACK) else if (state == STATE_TREAD || state == STATE_SURF_TREAD || state == STATE_SURF_SWIM || state == STATE_SURF_BACK)
w *= TURN_WATER_SLOW; w *= TURN_WATER_FAST;
else if (state == STATE_RUN) else if (state == STATE_RUN)
w *= sign(w) != sign(tilt) ? 0.0f : w * TURN_FAST * tilt / TILT_MAX; w *= sign(w) != sign(tilt) ? 0.0f : w * TURN_FAST * tilt / LARA_TILT_MAX;
else if (state == STATE_FAST_TURN) else if (state == STATE_FAST_TURN)
w *= TURN_FAST; w *= TURN_FAST;
else if (state == STATE_FAST_BACK) else if (state == STATE_FAST_BACK)
@@ -1723,17 +1763,12 @@ struct Lara : Character {
else else
w = 0.0f; w = 0.0f;
if (w != 0.0f) { if (w != 0.0f)
w *= Core::deltaTime; rotateY(w * Core::deltaTime);
angle.y += w;
velocity = velocity.rotateY(-w); // in-air velocity rotation control
}
// pitch (underwater only) // pitch (underwater only)
if (stand == STAND_UNDERWATER && (input & (FORTH | BACK)) ) { if (stand == STAND_UNDERWATER && (input & (FORTH | BACK)))
angle.x += ((input & FORTH) ? -TURN_WATER_SLOW : TURN_WATER_SLOW) * 0.5f * Core::deltaTime; rotateX(((input & FORTH) ? -TURN_WATER_SLOW : TURN_WATER_SLOW) * Core::deltaTime);
angle.x = clamp(angle.x, -PI * 0.5f, PI * 0.5f);
}
// get animation direction // get animation direction
angleExt = angle.y; angleExt = angle.y;
@@ -1743,6 +1778,7 @@ struct Lara : Character {
case STATE_BACK_JUMP : case STATE_BACK_JUMP :
case STATE_FAST_BACK : case STATE_FAST_BACK :
case STATE_SLIDE_BACK : case STATE_SLIDE_BACK :
case STATE_ROLL_1 :
angleExt += PI; angleExt += PI;
break; break;
case STATE_LEFT_JUMP : case STATE_LEFT_JUMP :
@@ -1768,18 +1804,15 @@ struct Lara : Character {
case STAND_HANG : case STAND_HANG :
case STAND_ONWATER : { case STAND_ONWATER : {
float speed = 0.0f;
switch (state) { switch (state) {
case STATE_SURF_SWIM : case STATE_SURF_SWIM :
case STATE_SURF_BACK : case STATE_SURF_BACK :
case STATE_SURF_LEFT : case STATE_SURF_LEFT :
case STATE_SURF_RIGHT : case STATE_SURF_RIGHT :
speed = 15.0f; speed = min(speed + 30.0f * LARA_WATER_ACCEL * Core::deltaTime, LARA_SURF_SPEED);
break; break;
default : default :
speed = animation.getSpeed(); speed = animation.getSpeed();
if (animation.index == ANIM_STAND_ROLL_END)
speed = -speed;
} }
if (stand == STAND_ONWATER) { if (stand == STAND_ONWATER) {
@@ -1790,9 +1823,9 @@ struct Lara : Character {
TR::Entity &e = getEntity(); TR::Entity &e = getEntity();
TR::Level::FloorInfo info; TR::Level::FloorInfo info;
if (stand == STAND_HANG) { if (stand == STAND_HANG) {
vec3 p = pos + getDir() * 128.0f; vec3 p = pos + getDir() * (LARA_RADIUS + 2.0f);
level->getFloorInfo(e.room, (int)p.x, (int)p.y, (int)p.z, info); level->getFloorInfo(e.room, (int)p.x, (int)p.y, (int)p.z, info);
if (info.roomAbove != 0xFF && info.floor >= e.y - LARA_HANG_OFFSET) if (info.roomAbove != TR::NO_ROOM && info.floor >= e.y - LARA_HANG_OFFSET)
level->getFloorInfo(info.roomAbove, (int)p.x, (int)p.y, (int)p.z, info); level->getFloorInfo(info.roomAbove, (int)p.x, (int)p.y, (int)p.z, info);
} else } else
level->getFloorInfo(e.room, e.x, e.y, e.z, info); level->getFloorInfo(e.room, e.x, e.y, e.z, info);
@@ -1803,16 +1836,13 @@ struct Lara : Character {
break; break;
} }
case STAND_UNDERWATER : { case STAND_UNDERWATER : {
float speed = 0.0f;
if (animation.index == ANIM_TO_UNDERWATER) if (animation.index == ANIM_TO_UNDERWATER)
speed = 15.0f; speed = 15.0f;
if (state == STATE_SWIM) if (state == STATE_SWIM)
speed = SWIM_SPEED; speed = min(speed + 30.0f * LARA_WATER_ACCEL * Core::deltaTime, LARA_SWIM_SPEED);
if (state == STATE_TREAD || state == STATE_GLIDE)
if (speed != 0.0f) speed = max(speed - 30.0f * LARA_SWIM_FRICTION * Core::deltaTime, 0.0f);
velocity = vec3(angle.x, angle.y) * speed; velocity = vec3(angle.x, angle.y) * speed;
else
velocity = velocity - velocity * min(1.0f, Core::deltaTime * 2.0f);
// TODO: apply flow velocity // TODO: apply flow velocity
break; break;
} }
@@ -1820,9 +1850,13 @@ struct Lara : Character {
} }
virtual void updatePosition() { // TODO: sphere / bbox collision virtual void updatePosition() { // TODO: sphere / bbox collision
updateTilt(state == STATE_RUN, TILT_SPEED, TILT_MAX); // tilt control
vec2 vTilt(LARA_TILT_SPEED * Core::deltaTime, LARA_TILT_MAX);
if (stand == STAND_UNDERWATER)
vTilt *= 2.0f;
updateTilt(state == STATE_RUN || stand == STAND_UNDERWATER, vTilt.x, vTilt.y);
if (velocity.length() >= 0.001f) if (velocity.length() >= 1.0f)
move(); move();
if (getEntity().type != TR::Entity::LARA) { if (getEntity().type != TR::Entity::LARA) {
@@ -1844,11 +1878,11 @@ struct Lara : Character {
} }
void move() { void move() {
TR::Entity &e = getEntity(); //TR::Entity &e = getEntity();
TR::Level::FloorInfo info; //TR::Level::FloorInfo info;
float f, c; //float f, c;
bool canPassGap = true; //bool canPassGap = true;
/* /*
if (velocity != 0.0f) { if (velocity != 0.0f) {
vec3 dir = velocity.normal() * 128.0f; vec3 dir = velocity.normal() * 128.0f;
@@ -1868,11 +1902,53 @@ struct Lara : Character {
} }
} }
*/ */
vec3 offset = velocity * Core::deltaTime * 30.0f; vec3 vel = velocity * Core::deltaTime * 30.0f;
vec3 opos(pos), offset(0.0f);
vec3 p = pos; float radius = stand == STAND_UNDERWATER ? LARA_RADIUS_WATER : LARA_RADIUS;
pos = pos + offset; int maxHeight = stand == STAND_UNDERWATER ? LARA_HEIGHT_WATER : LARA_HEIGHT;
int minHeight = 0;
int maxAscent = 256 + 128;
int maxDescent = 0xFFFFFF;
if (state == STATE_WALK || state == STATE_BACK)
maxDescent = maxAscent;
if (state == STATE_STEP_LEFT || state == STATE_STEP_RIGHT)
maxAscent = maxDescent = 64;
if (stand == STAND_ONWATER)
maxAscent = -1;
if (stand == STAND_HANG) {
maxHeight = 0;
maxAscent = maxDescent = 64;
offset = getDir() * (LARA_RADIUS + 32.0f);
offset.y -= LARA_HANG_OFFSET;
}
if (stand == STAND_UNDERWATER) {
offset.y += LARA_HEIGHT_WATER * 0.5f;
}
int room = getRoomIndex();
collision = Collision(level, room, pos, offset, vel, radius, angleExt, minHeight, maxHeight, maxAscent, maxDescent);
if (stand != STAND_HANG && (collision.side == Collision::LEFT || collision.side == Collision::RIGHT)) {
float rot = TURN_WALL_Y * Core::deltaTime;
rotateY((collision.side == Collision::LEFT) ? rot : -rot);
}
if (stand == STAND_HANG && collision.side != Collision::FRONT) {
offset.x = offset.z = 0.0f;
minHeight = LARA_HANG_OFFSET;
maxDescent = 0xFFFFFF;
maxAscent = -LARA_HANG_OFFSET;
vec3 p = pos;
collision = Collision(level, room, p, offset, vec3(0.0f), radius, angleExt, minHeight, maxHeight, maxAscent, maxDescent);
if (collision.side == Collision::FRONT)
pos = opos;
}
/*
if (canPassGap) { if (canPassGap) {
level->getFloorInfo(e.room, (int)pos.x, (int)pos.y, (int)pos.z, info); level->getFloorInfo(e.room, (int)pos.x, (int)pos.y, (int)pos.z, info);
canPassGap = (info.floor - info.ceiling) >= (stand == STAND_GROUND ? 768 : 512); canPassGap = (info.floor - info.ceiling) >= (stand == STAND_GROUND ? 768 : 512);
@@ -1880,6 +1956,8 @@ struct Lara : Character {
f = info.floor - pos.y; f = info.floor - pos.y;
c = pos.y - info.ceiling; c = pos.y - info.ceiling;
*/
/* /*
TR::Animation *anim = animation; TR::Animation *anim = animation;
Box eBox = Box(pos - vec3(128.0f, 0.0f, 128.0f), pos + vec3(128.0, getHeight(), 128.0f)); // getBoundingBox(); Box eBox = Box(pos - vec3(128.0f, 0.0f, 128.0f), pos + vec3(128.0, getHeight(), 128.0f)); // getBoundingBox();
@@ -1915,6 +1993,7 @@ struct Lara : Character {
} }
} }
*/ */
/*
if (canPassGap) if (canPassGap)
switch (stand) { switch (stand) {
case STAND_AIR : { case STAND_AIR : {
@@ -1959,38 +2038,46 @@ struct Lara : Character {
break; break;
default : ; default : ;
} }
*/
// get current leading foot in animation
int rightStart = 0; int rightStart = 0;
if (state == STATE_RUN) rightStart = 6; if (state == STATE_RUN) rightStart = 6;
if (state == STATE_WALK) rightStart = 13; if (state == STATE_WALK) rightStart = 13;
if (state == STATE_BACK) rightStart = 28; if (state == STATE_BACK) rightStart = 28;
bool isLeftFoot = animation.frameIndex < rightStart || animation.frameIndex > (rightStart + animation.framesCount / 2); bool isLeftFoot = animation.frameIndex < rightStart || animation.frameIndex > (rightStart + animation.framesCount / 2);
if (!canPassGap) { if (stand == STAND_UNDERWATER) {
pos = p; // TODO: use smart ejection if (collision.side == Collision::TOP)
rotateX(-TURN_WALL_X * Core::deltaTime);
if (collision.side == Collision::BOTTOM)
rotateX( TURN_WALL_X * Core::deltaTime);
}
if (stand == STAND_AIR && collision.side == Collision::TOP && velocity.y < 0.0f)
velocity.y = 30.0f;
if (collision.side == Collision::FRONT) {
int floor = collision.info[Collision::FRONT].floor;
// hit the wall // hit the wall
switch (stand) { switch (stand) {
case STAND_AIR : case STAND_AIR :
if (state == STATE_UP_JUMP || state == STATE_REACH) { if (state == STATE_UP_JUMP || state == STATE_REACH)
velocity.x = velocity.z = 0.0f; velocity.x = velocity.z = 0.0f;
if (c <= 0 && offset.y < 0.0f) offset.y = velocity.y = 0.0f;
}
if (velocity.x != 0.0f || velocity.z != 0.0f) { if (velocity.x != 0.0f || velocity.z != 0.0f) {
animation.setAnim(ANIM_SMASH_JUMP); animation.setAnim(ANIM_SMASH_JUMP);
velocity.x = -velocity.x * 0.5f; velocity.x = -velocity.x * 0.25f;
velocity.z = -velocity.z * 0.5f; velocity.z = -velocity.z * 0.25f;
if (offset.y < 0.0f) if (velocity.y < 0.0f)
offset.y = velocity.y = 0.0f; velocity.y = 30.0f;
} }
pos.y = p.y + offset.y;
break; break;
case STAND_GROUND : case STAND_GROUND :
case STAND_HANG : case STAND_HANG :
if (f <= -(256 * 3 - 128) && state == STATE_RUN) if (opos.y - floor > (256 * 3 - 128) && state == STATE_RUN)
animation.setAnim(isLeftFoot ? ANIM_SMASH_RUN_LEFT : ANIM_SMASH_RUN_RIGHT); animation.setAnim(isLeftFoot ? ANIM_SMASH_RUN_LEFT : ANIM_SMASH_RUN_RIGHT);
else if (stand == STAND_HANG) else if (stand == STAND_HANG)
animation.setAnim(ANIM_HANG, -21); animation.setAnim(ANIM_HANG, -21);
@@ -1998,11 +2085,18 @@ struct Lara : Character {
animation.setAnim((state == STATE_RUN || state == STATE_WALK) ? (isLeftFoot ? ANIM_STAND_LEFT : ANIM_STAND_RIGHT) : ANIM_STAND); animation.setAnim((state == STATE_RUN || state == STATE_WALK) ? (isLeftFoot ? ANIM_STAND_LEFT : ANIM_STAND_RIGHT) : ANIM_STAND);
velocity.x = velocity.z = 0.0f; velocity.x = velocity.z = 0.0f;
break; break;
case STAND_UNDERWATER :
if (fabsf(angle.x) > TURN_WALL_X_CLAMP)
rotateX(TURN_WALL_X * Core::deltaTime * sign(angle.x));
else
pos.y = opos.y;
break;
default : ;// no smash animation default : ;// no smash animation
} }
} else } else {
if (stand == STAND_GROUND) { if (stand == STAND_GROUND) {
int h = int(info.floor - pos.y); int floor = collision.info[Collision::NONE].floor;
int h = int(floor - opos.y);
if (h >= 256 && state == STATE_FAST_BACK) { if (h >= 256 && state == STATE_FAST_BACK) {
stand = STAND_AIR; stand = STAND_AIR;
@@ -2010,18 +2104,20 @@ struct Lara : Character {
} else if (h >= 128 && (state == STATE_WALK || state == STATE_BACK)) { // descend } else if (h >= 128 && (state == STATE_WALK || state == STATE_BACK)) { // descend
if (state == STATE_WALK) animation.setAnim(isLeftFoot ? ANIM_WALK_DESCEND_LEFT : ANIM_WALK_DESCEND_RIGHT); if (state == STATE_WALK) animation.setAnim(isLeftFoot ? ANIM_WALK_DESCEND_LEFT : ANIM_WALK_DESCEND_RIGHT);
if (state == STATE_BACK) animation.setAnim(isLeftFoot ? ANIM_BACK_DESCEND_LEFT : ANIM_BACK_DESCEND_RIGHT); if (state == STATE_BACK) animation.setAnim(isLeftFoot ? ANIM_BACK_DESCEND_LEFT : ANIM_BACK_DESCEND_RIGHT);
pos.y = float(info.floor); pos.y = float(floor);
} else if (h > -1.0f) { } else if (h > -1.0f) {
pos.y = min((float)info.floor, pos.y += DESCENT_SPEED * Core::deltaTime); pos.y = min(float(floor), pos.y += DESCENT_SPEED * Core::deltaTime);
} else if (h > -128) { } else if (h > -128) {
pos.y = float(info.floor); pos.y = float(floor);
} else if (h >= -(256 + 128) && (state == STATE_RUN || state == STATE_WALK)) { // ascend } else if (h >= -(256 + 128) && (state == STATE_RUN || state == STATE_WALK)) { // ascend
if (state == STATE_RUN) animation.setAnim(isLeftFoot ? ANIM_RUN_ASCEND_LEFT : ANIM_RUN_ASCEND_RIGHT); if (state == STATE_RUN) animation.setAnim(isLeftFoot ? ANIM_RUN_ASCEND_LEFT : ANIM_RUN_ASCEND_RIGHT);
if (state == STATE_WALK) animation.setAnim(isLeftFoot ? ANIM_WALK_ASCEND_LEFT : ANIM_WALK_ASCEND_RIGHT); if (state == STATE_WALK) animation.setAnim(isLeftFoot ? ANIM_WALK_ASCEND_LEFT : ANIM_WALK_ASCEND_RIGHT);
pos.y = float(info.floor); pos.y = float(floor);
} else } else
pos.y = float(info.floor); pos.y = float(floor);
} }
collision.side = Collision::NONE;
}
updateEntity(); updateEntity();
checkRoom(); checkRoom();

View File

@@ -767,7 +767,8 @@ struct Level : IGame {
delete cube; delete cube;
delete mesh; delete mesh;
delete camera; delete camera;
Sound::stopAll();
} }
void initTextures() { void initTextures() {
@@ -1148,7 +1149,7 @@ struct Level : IGame {
} }
} }
} }
} }
camera->update(); camera->update();
waterCache->update(); waterCache->update();
@@ -1375,6 +1376,40 @@ struct Level : IGame {
// renderModel(level.models[modelIndex], level.entities[4]); // renderModel(level.models[modelIndex], level.entities[4]);
*/ */
Debug::begin(); Debug::begin();
lara->updateEntity(); // TODO clip angle while rotating
int q = int(normalizeAngle(lara->angleExt + PI * 0.25f) / (PI * 0.5f));
float radius = 256.0f;
const vec2 v[] = {
{ -radius, radius },
{ radius, radius },
{ radius, -radius },
{ -radius, -radius },
};
const vec2 &l = v[q],
&r = v[(q + 1) % 4],
&f = (q %= 2) ? vec2(l.x, radius * cosf(lara->angleExt)) : vec2(radius * sinf(lara->angleExt), l.y);
vec3 F = vec3(f.x, 0.0f, f.y);
vec3 L = vec3(l.x, 0.0f, l.y);
vec3 R = vec3(r.x, 0.0f, r.y);
vec3 p, n = lara->pos + vec3(0.0f, -512.0f, 0.0f);
Core::setDepthTest(false);
glBegin(GL_LINES);
glColor3f(0, 0, 1); p = n; glVertex3fv((GLfloat*)&p); p += F; glVertex3fv((GLfloat*)&p);
glColor3f(1, 0, 0); p = n; glVertex3fv((GLfloat*)&p); p += L; glVertex3fv((GLfloat*)&p);
glColor3f(0, 1, 0); p = n; glVertex3fv((GLfloat*)&p); p += R; glVertex3fv((GLfloat*)&p);
glColor3f(1, 1, 0); p = lara->pos; glVertex3fv((GLfloat*)&p); p -= vec3(0.0f, LARA_HANG_OFFSET, 0.0f); glVertex3fv((GLfloat*)&p);
glEnd();
Core::setDepthTest(true);
/* /*
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glPushMatrix(); glPushMatrix();
@@ -1415,15 +1450,17 @@ struct Level : IGame {
glPopMatrix(); glPopMatrix();
*/ */
Core::setBlending(bmAlpha);
// Debug::Level::rooms(level, lara->pos, lara->getEntity().room); // Debug::Level::rooms(level, lara->pos, lara->getEntity().room);
// Debug::Level::lights(level, lara->getRoomIndex()); // Debug::Level::lights(level, lara->getRoomIndex());
// Debug::Level::sectors(level, lara->getRoomIndex(), (int)lara->pos.y); Debug::Level::sectors(level, lara->getRoomIndex(), (int)lara->pos.y);
// Core::setDepthTest(false); // Core::setDepthTest(false);
// Debug::Level::portals(level); // Debug::Level::portals(level);
// Core::setDepthTest(true); // Core::setDepthTest(true);
// Debug::Level::meshes(level); // Debug::Level::meshes(level);
// Debug::Level::entities(level); // Debug::Level::entities(level);
Core::setBlending(bmNone);
/* /*
static int dbg_ambient = 0; static int dbg_ambient = 0;
dbg_ambient = int(params.time * 2) % 4; dbg_ambient = int(params.time * 2) % 4;

View File

@@ -124,6 +124,8 @@
controls:<br> controls:<br>
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)<br> 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)<br>
gamepad: PSX controls on XBox controller<br> gamepad: PSX controls on XBox controller<br>
Change view: V<br>
Time Control: R - slow motion, T - fast motion<br>
FullScreen: Alt + Enter FullScreen: Alt + Enter
</span> </span>

View File

@@ -189,6 +189,7 @@
<ClInclude Include="..\..\animation.h" /> <ClInclude Include="..\..\animation.h" />
<ClInclude Include="..\..\camera.h" /> <ClInclude Include="..\..\camera.h" />
<ClInclude Include="..\..\character.h" /> <ClInclude Include="..\..\character.h" />
<ClInclude Include="..\..\collision.h" />
<ClInclude Include="..\..\controller.h" /> <ClInclude Include="..\..\controller.h" />
<ClInclude Include="..\..\core.h" /> <ClInclude Include="..\..\core.h" />
<ClInclude Include="..\..\debug.h" /> <ClInclude Include="..\..\debug.h" />

View File

@@ -530,6 +530,12 @@ namespace Sound {
delete stream; delete stream;
return NULL; return NULL;
} }
void stopAll() {
for (int i = 0; i < channelsCount; i++)
delete channels[i];
channelsCount = 0;
}
} }
#endif #endif

View File

@@ -85,6 +85,16 @@ float shortAngle(float a, float b) {
return clampAngle(n - int(n / PI2) * PI2); return clampAngle(n - int(n / PI2) * PI2);
} }
float normalizeAngle(float angle) {
while (angle < 0.0f) angle += PI2;
while (angle > PI2) angle -= PI2;
return angle;
}
int angleQuadrant(float angle) {
return int(normalizeAngle(angle + PI * 0.25f) / (PI * 0.5f));
}
float decrease(float delta, float &value, float &speed) { float decrease(float delta, float &value, float &speed) {
if (speed > 0.0f && fabsf(delta) > 0.01f) { if (speed > 0.0f && fabsf(delta) > 0.01f) {
if (delta > 0) speed = min(delta, speed); if (delta > 0) speed = min(delta, speed);
@@ -112,11 +122,13 @@ struct vec2 {
vec2(float s) : x(s), y(s) {} vec2(float s) : x(s), y(s) {}
vec2(float x, float y) : x(x), y(y) {} vec2(float x, float y) : x(x), y(y) {}
float& operator [] (int index) const { ASSERT(index >= 0 && index <= 1); return ((float*)this)[index]; } inline float& operator [] (int index) const { ASSERT(index >= 0 && index <= 1); return ((float*)this)[index]; }
bool operator == (float s) const { return x == s && y == s; } inline bool operator == (const vec2 &v) const { return x == v.x && y == v.y; }
bool operator != (float s) const { return !(*this == s); } inline bool operator != (const vec2 &v) const { return !(*this == v); }
vec2 operator - () const { return vec2(-x, -y); } inline bool operator == (float s) const { return x == s && y == s; }
inline bool operator != (float s) const { return !(*this == s); }
inline vec2 operator - () const { return vec2(-x, -y); }
vec2& operator += (const vec2 &v) { x += v.x; y += v.y; return *this; } vec2& operator += (const vec2 &v) { x += v.x; y += v.y; return *this; }
vec2& operator -= (const vec2 &v) { x -= v.x; y -= v.y; return *this; } vec2& operator -= (const vec2 &v) { x -= v.x; y -= v.y; return *this; }
@@ -138,6 +150,7 @@ struct vec2 {
float length2() const { return dot(*this); } float length2() const { return dot(*this); }
float length() const { return sqrtf(length2()); } float length() const { return sqrtf(length2()); }
vec2 normal() const { float s = length(); return s == 0.0 ? (*this) : (*this)*(1.0f/s); } vec2 normal() const { float s = length(); return s == 0.0 ? (*this) : (*this)*(1.0f/s); }
float angle() const { return atan2(y, x); }
}; };
struct vec3 { struct vec3 {
@@ -148,11 +161,13 @@ struct vec3 {
vec3(const vec2 &xy, float z = 0.0f) : x(xy.x), y(xy.y), z(z) {} vec3(const vec2 &xy, float z = 0.0f) : x(xy.x), y(xy.y), z(z) {}
vec3(float lng, float lat) : x(sinf(lat) * cosf(lng)), y(-sinf(lng)), z(cosf(lat) * cosf(lng)) {} vec3(float lng, float lat) : x(sinf(lat) * cosf(lng)), y(-sinf(lng)), z(cosf(lat) * cosf(lng)) {}
float& operator [] (int index) const { ASSERT(index >= 0 && index <= 2); return ((float*)this)[index]; } inline float& operator [] (int index) const { ASSERT(index >= 0 && index <= 2); return ((float*)this)[index]; }
bool operator == (float s) const { return x == s && y == s && z == s; } inline bool operator == (const vec3 &v) const { return x == v.x && y == v.y && z == v.z; }
bool operator != (float s) const { return !(*this == s); } inline bool operator != (const vec3 &v) const { return !(*this == v); }
vec3 operator - () const { return vec3(-x, -y, -z); } inline bool operator == (float s) const { return x == s && y == s && z == s; }
inline bool operator != (float s) const { return !(*this == s); }
inline vec3 operator - () const { return vec3(-x, -y, -z); }
vec3& operator += (const vec3 &v) { x += v.x; y += v.y; z += v.z; return *this; } vec3& operator += (const vec3 &v) { x += v.x; y += v.y; z += v.z; return *this; }
vec3& operator -= (const vec3 &v) { x -= v.x; y -= v.y; z -= v.z; return *this; } vec3& operator -= (const vec3 &v) { x -= v.x; y -= v.y; z -= v.z; return *this; }
@@ -598,6 +613,13 @@ struct Basis {
void rotate(const quat &q) { void rotate(const quat &q) {
rot = rot * q; rot = rot * q;
} }
Basis lerp(const Basis &basis, float t) {
Basis b;
b.rot = rot.lerp(basis.rot, t);
b.pos = pos.lerp(basis.pos, t);
return b;
}
}; };
struct ubyte2 { struct ubyte2 {