mirror of
https://github.com/XProger/OpenLara.git
synced 2025-04-21 03:21:51 +02:00
#3 №14 separated lara.h & enemy.h for controllers, add basic enemy controllers, add roll animation for lara, add text in debug mode, #8 smooth following camera, side view while backflip
This commit is contained in:
parent
b80c2790d0
commit
071081b612
BIN
bin/OpenLara.exe
BIN
bin/OpenLara.exe
Binary file not shown.
73
src/camera.h
73
src/camera.h
@ -3,10 +3,10 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "controller.h"
|
||||
#include "lara.h"
|
||||
|
||||
#define MAX_CLIP_PLANES 10
|
||||
|
||||
|
||||
struct Frustum {
|
||||
|
||||
struct Poly {
|
||||
@ -47,39 +47,8 @@ struct Frustum {
|
||||
planes[i].w = -(pos.dot(planes[i].xyz));
|
||||
e1 = e2;
|
||||
}
|
||||
#ifdef _DEBUG
|
||||
dbg++;
|
||||
debugPoly = poly;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
void debug() {
|
||||
if (debugPoly.count < 3) return;
|
||||
|
||||
glUseProgram(0);
|
||||
Core::setBlending(bmAdd);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glBegin(GL_TRIANGLES);
|
||||
for (int i = 0; i < debugPoly.count; i++) {
|
||||
glVertex3fv((GLfloat*)&pos);
|
||||
glVertex3fv((GLfloat*)&debugPoly.vertices[i]);
|
||||
glVertex3fv((GLfloat*)&debugPoly.vertices[(i + 1) % debugPoly.count]);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
glColor3f(0, 1, 0);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
for (int i = 0; i <= debugPoly.count; i++) {
|
||||
glVertex3fv((GLfloat*)&debugPoly.vertices[i % debugPoly.count]);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
Core::setBlending(bmAlpha);
|
||||
}
|
||||
#endif
|
||||
|
||||
void clipPlane(const Poly &src, Poly &dst, const vec4 &plane) {
|
||||
dst.count = 0;
|
||||
if (!src.count) return;
|
||||
@ -168,7 +137,7 @@ struct Camera : Controller {
|
||||
Frustum *frustum;
|
||||
|
||||
float fov, znear, zfar;
|
||||
vec3 pos, target;
|
||||
vec3 target, destPos, lastDest;
|
||||
|
||||
int room;
|
||||
|
||||
@ -179,16 +148,17 @@ struct Camera : Controller {
|
||||
angle.y += PI;
|
||||
|
||||
room = owner->getEntity().room;
|
||||
pos = pos - getDir() * 1024.0f;
|
||||
}
|
||||
|
||||
~Camera() {
|
||||
delete frustum;
|
||||
}
|
||||
|
||||
virtual TR::Room& getRoom() {
|
||||
virtual TR::Room& getRoom() const {
|
||||
return level->rooms[room];
|
||||
}
|
||||
|
||||
|
||||
virtual void update() {
|
||||
#ifdef FREE_CAMERA
|
||||
vec3 dir = vec3(sinf(angle.y - PI) * cosf(-angle.x), -sinf(-angle.x), cosf(angle.y - PI) * cosf(-angle.x));
|
||||
@ -200,10 +170,6 @@ struct Camera : Controller {
|
||||
if (Input::down[ikA]) v = v - dir.cross(vec3(0, 1, 0));
|
||||
pos = pos + v.normal() * (Core::deltaTime * 2048.0f);
|
||||
#endif
|
||||
// deltaPos = deltaPos.lerp(targetDeltaPos, Core::deltaTime * 10.0f);
|
||||
// angle = angle.lerp(targetAngle, Core::deltaTime);
|
||||
|
||||
|
||||
if (Input::down[ikMouseL]) {
|
||||
vec2 delta = Input::mouse.pos - Input::mouse.start.L;
|
||||
angle.x += delta.y * 0.01f;
|
||||
@ -211,25 +177,31 @@ struct Camera : Controller {
|
||||
Input::mouse.start.L = Input::mouse.pos;
|
||||
}
|
||||
|
||||
// angle.x = owner->angle.x;
|
||||
angle.y = PI - owner->angle.y;
|
||||
angle.z = 0.0f;
|
||||
|
||||
angle.x = min(max(angle.x, -80 * DEG2RAD), 80 * DEG2RAD);
|
||||
|
||||
vec3 dir = vec3(sinf(PI - angle.y) * cosf(-angle.x), -sinf(-angle.x), cosf(PI - angle.y) * cosf(-angle.x));
|
||||
vec3 dir = getDir();
|
||||
|
||||
float height = owner->inWater ? 256.0f : 768.0f;
|
||||
float height = owner->stand == Controller::STAND_UNDERWATER ? 256.0f : 768.0f;
|
||||
|
||||
target = vec3(owner->pos.x, owner->pos.y - height, owner->pos.z);
|
||||
pos = target - dir * 1024.0;
|
||||
|
||||
if (owner->state != Lara::STATE_BACK_JUMP) {
|
||||
destPos = target - dir * 1024.0f;
|
||||
lastDest = destPos;
|
||||
} else
|
||||
destPos = lastDest + dir.cross(vec3(0, 1, 0)).normal() * 2048.0f - vec3(0.0f, 512.0f, 0.0f);
|
||||
|
||||
pos = pos.lerp(destPos, min(1.0f, Core::deltaTime * 2.0f));
|
||||
|
||||
FloorInfo info = getFloorInfo((int)pos.x, (int)pos.z);
|
||||
|
||||
if (info.roomNext != 255)
|
||||
room = info.roomNext;
|
||||
|
||||
if (pos.y < info.ceiling) {
|
||||
if (destPos.y < info.ceiling) {
|
||||
if (info.roomAbove != 255)
|
||||
room = info.roomAbove;
|
||||
else
|
||||
@ -255,18 +227,7 @@ struct Camera : Controller {
|
||||
Core::viewPos = Core::mViewInv.offset.xyz;
|
||||
|
||||
frustum->pos = Core::viewPos;
|
||||
frustum->calcPlanes(Core::mViewProj);
|
||||
|
||||
#ifdef _DEBUG
|
||||
vec3 offset = vec3(0.0f) - (Input::down[ikR] ? (Core::mViewInv.dir.xyz * 2048 - vec3(0, 2048, 0)) : vec3(0.0f));
|
||||
|
||||
Core::mViewInv = mat4(pos - offset, target - offset, vec3(0, -1, 0));
|
||||
Core::mView = Core::mViewInv.inverse();
|
||||
Core::mProj = mat4(fov, (float)Core::width / (float)Core::height, znear, zfar);
|
||||
|
||||
Core::mViewProj = Core::mProj * Core::mView;
|
||||
Core::viewPos = Core::mViewInv.offset.xyz;
|
||||
#endif
|
||||
frustum->calcPlanes(Core::mViewProj);
|
||||
}
|
||||
};
|
||||
|
||||
|
629
src/controller.h
629
src/controller.h
@ -12,28 +12,43 @@ struct Controller {
|
||||
TR::Level *level;
|
||||
int entity;
|
||||
|
||||
float fTime;
|
||||
int lastFrame;
|
||||
enum Stand {
|
||||
STAND_AIR, STAND_GROUND, STAND_UNDERWATER, STAND_ONWATER
|
||||
} stand;
|
||||
int state;
|
||||
int mask;
|
||||
|
||||
vec3 pos, velocity;
|
||||
vec3 angle;
|
||||
enum { LEFT = 1 << 1,
|
||||
RIGHT = 1 << 2,
|
||||
FORTH = 1 << 3,
|
||||
BACK = 1 << 4,
|
||||
JUMP = 1 << 5,
|
||||
WALK = 1 << 6,
|
||||
ACTION = 1 << 7,
|
||||
WEAPON = 1 << 8,
|
||||
DEATH = 1 << 11 };
|
||||
|
||||
int health;
|
||||
float animTime;
|
||||
int animIndex;
|
||||
int animPrevFrame;
|
||||
|
||||
float turnTime;
|
||||
vec3 pos, velocity;
|
||||
vec3 angle;
|
||||
|
||||
bool onGround;
|
||||
bool inWater;
|
||||
float angleExt;
|
||||
|
||||
Controller(TR::Level *level, int entity) : level(level), entity(entity), velocity(0.0f), fTime(0.0f), lastFrame(0), health(100), turnTime(0.0f) {
|
||||
int health;
|
||||
|
||||
float turnTime;
|
||||
|
||||
Controller(TR::Level *level, int entity) : level(level), entity(entity), velocity(0.0f), animTime(0.0f), animPrevFrame(0), health(100), turnTime(0.0f) {
|
||||
TR::Entity &e = getEntity();
|
||||
pos = vec3((float)e.x, (float)e.y, (float)e.z);
|
||||
angle = vec3(0.0f, e.rotation / 16384.0f * PI * 0.5f, 0.0f);
|
||||
onGround = inWater = false;
|
||||
pos = vec3((float)e.x, (float)e.y, (float)e.z);
|
||||
angle = vec3(0.0f, e.rotation / 16384.0f * PI * 0.5f, 0.0f);
|
||||
stand = STAND_GROUND;
|
||||
animIndex = getModel().animation;
|
||||
}
|
||||
|
||||
virtual void update() {}
|
||||
|
||||
void updateEntity() {
|
||||
TR::Entity &e = getEntity();
|
||||
e.x = int(pos.x);
|
||||
@ -42,7 +57,7 @@ struct Controller {
|
||||
e.rotation = int(angle.y / (PI * 0.5f) * 16384.0f);
|
||||
}
|
||||
|
||||
bool insideRoom(const vec3 &pos, int room) {
|
||||
bool insideRoom(const vec3 &pos, int room) const {
|
||||
TR::Room &r = level->rooms[room];
|
||||
vec3 min = vec3(r.info.x, r.info.yTop, r.info.z);
|
||||
vec3 max = min + vec3(r.xSectors * 1024, r.info.yBottom - r.info.yTop, r.zSectors * 1024);
|
||||
@ -52,11 +67,11 @@ struct Controller {
|
||||
pos.z >= min.z && pos.z <= max.z;
|
||||
}
|
||||
|
||||
TR::Entity& getEntity() {
|
||||
TR::Entity& getEntity() const {
|
||||
return level->entities[entity];
|
||||
}
|
||||
|
||||
TR::Model& getModel() {
|
||||
TR::Model& getModel() const {
|
||||
TR::Entity &entity = getEntity();
|
||||
for (int i = 0; i < level->modelsCount; i++)
|
||||
if (entity.id == level->models[i].id)
|
||||
@ -65,13 +80,13 @@ struct Controller {
|
||||
return level->models[0];
|
||||
}
|
||||
|
||||
virtual TR::Room& getRoom() {
|
||||
virtual TR::Room& getRoom() const {
|
||||
int index = getEntity().room;
|
||||
ASSERT(index >= 0 && index < level->roomsCount);
|
||||
return level->rooms[index];
|
||||
}
|
||||
|
||||
TR::Room::Sector& getSector(int x, int z, int &dx, int &dz) {
|
||||
TR::Room::Sector& getSector(int x, int z, int &dx, int &dz) const {
|
||||
TR::Room &room = getRoom();
|
||||
|
||||
int sx = x - room.info.x;
|
||||
@ -88,19 +103,26 @@ struct Controller {
|
||||
return room.sectors[sx * room.zSectors + sz];
|
||||
}
|
||||
|
||||
TR::Room::Sector& getSector(int &dx, int &dz) {
|
||||
TR::Room::Sector& getSector(int &dx, int &dz) const {
|
||||
TR::Entity &entity = getEntity();
|
||||
return getSector(entity.x, entity.z, dx, dz);
|
||||
}
|
||||
|
||||
bool changeState(int state) {
|
||||
TR::Model &model = getModel();
|
||||
TR::Animation *anim = &level->anims[model.animation];
|
||||
int setAnimation(int index, int frame = -1) {
|
||||
animIndex = index;
|
||||
TR::Animation &anim = level->anims[animIndex];
|
||||
animTime = frame == -1 ? 0.0f : ((frame - anim.frameStart) / 30.0f);
|
||||
animPrevFrame = -1;
|
||||
return state = anim.state;
|
||||
}
|
||||
|
||||
bool setState(int state) {
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
|
||||
if (state == anim->state)
|
||||
return true;
|
||||
|
||||
int fIndex = int(fTime * 30.0f);
|
||||
int fIndex = int(animTime * 30.0f);
|
||||
|
||||
bool exists = false;
|
||||
|
||||
@ -111,8 +133,7 @@ struct Controller {
|
||||
for (int j = 0; j < s.rangesCount; j++) {
|
||||
TR::AnimRange &range = level->ranges[s.rangesOffset + j];
|
||||
if (anim->frameStart + fIndex >= range.low && anim->frameStart + fIndex <= range.high) {
|
||||
model.animation = range.nextAnimation;
|
||||
fTime = (range.nextFrame - level->anims[model.animation].frameStart) / 30.0f;
|
||||
setAnimation(range.nextAnimation, range.nextFrame);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -122,7 +143,7 @@ struct Controller {
|
||||
return exists;
|
||||
}
|
||||
|
||||
int getOverlap(int fromX, int fromY, int fromZ, int toX, int toZ, int &delta) {
|
||||
int getOverlap(int fromX, int fromY, int fromZ, int toX, int toZ, int &delta) const {
|
||||
int dx, dz;
|
||||
TR::Room::Sector &s = getSector(fromX, fromZ, dx, dz);
|
||||
|
||||
@ -236,7 +257,7 @@ struct Controller {
|
||||
return info;
|
||||
}
|
||||
|
||||
void playSound(int id) {
|
||||
void playSound(int id) const {
|
||||
int16 a = level->soundsMap[id];
|
||||
TR::SoundInfo &b = level->soundsInfo[a];
|
||||
if (b.chance == 0 || (rand() & 0x7fff) <= b.chance) {
|
||||
@ -248,425 +269,15 @@ struct Controller {
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
#define FAST_TURN_TIME 1.0f
|
||||
|
||||
#define TURN_FAST PI
|
||||
#define TURN_FAST_BACK PI * 3.0f / 4.0f
|
||||
#define TURN_NORMAL PI / 2.0f
|
||||
#define TURN_SLOW PI / 3.0f
|
||||
#define TURN_TILT PI / 18.0f
|
||||
#define TURN_WATER_FAST PI * 3.0f / 4.0f
|
||||
#define TURN_WATER_SLOW PI * 2.0f / 3.0f
|
||||
#define GLIDE_SPEED 50.0f
|
||||
|
||||
struct Lara : Controller {
|
||||
|
||||
Lara(TR::Level *level, int entity) : Controller(level, entity) {
|
||||
/*
|
||||
pos = vec3(70067, -256, 29104);
|
||||
angle = vec3(0.0f, -0.68f, 0.0f);
|
||||
getEntity().room = 15;
|
||||
*/
|
||||
/*
|
||||
pos = vec3(41015, 3584, 34494);
|
||||
angle = vec3(0.0f, -PI, 0.0f);
|
||||
getEntity().room = 51;
|
||||
*/
|
||||
}
|
||||
|
||||
virtual void update() {
|
||||
TR::Model &model = getModel();
|
||||
TR::Animation *anim = &level->anims[model.animation];
|
||||
|
||||
fTime += Core::deltaTime;
|
||||
// return;
|
||||
float rot = 0.0f;
|
||||
|
||||
enum { LEFT = 1 << 1,
|
||||
RIGHT = 1 << 2,
|
||||
FORTH = 1 << 3,
|
||||
BACK = 1 << 4,
|
||||
JUMP = 1 << 5,
|
||||
WALK = 1 << 6,
|
||||
ACTION = 1 << 7,
|
||||
WEAPON = 1 << 8,
|
||||
GROUND = 1 << 9,
|
||||
WATER = 1 << 10,
|
||||
DEATH = 1 << 11 };
|
||||
|
||||
inWater = (getRoom().flags & TR::ROOM_FLAG_WATER);
|
||||
|
||||
int mask = 0;
|
||||
|
||||
if (Input::down[ikW] || Input::joy.L.y < 0) mask |= FORTH;
|
||||
if (Input::down[ikS] || Input::joy.L.y > 0) mask |= BACK;
|
||||
if (Input::down[ikA] || Input::joy.L.x < 0) mask |= LEFT;
|
||||
if (Input::down[ikD] || Input::joy.L.x > 0) mask |= RIGHT;
|
||||
if (Input::down[ikSpace] || Input::down[ikJoyX]) mask |= JUMP;
|
||||
if (Input::down[ikShift] || Input::down[ikJoyLT]) mask |= WALK;
|
||||
if (Input::down[ikE] || Input::down[ikMouseL] || Input::down[ikJoyA]) mask |= ACTION;
|
||||
if (Input::down[ikQ] || Input::down[ikMouseR] || Input::down[ikJoyY]) mask |= WEAPON;
|
||||
if (health <= 0) mask |= DEATH;
|
||||
if (onGround) mask |= GROUND;
|
||||
if (inWater) mask |= WATER;
|
||||
|
||||
int state = anim->state;
|
||||
|
||||
if (mask & DEATH)
|
||||
state = TR::STATE_DEATH;
|
||||
else if ((mask & (GROUND | WATER)) == (GROUND | WATER)) { // on water surface
|
||||
angle.x = 0.0f;
|
||||
|
||||
state = TR::STATE_SURF_TREAD;
|
||||
|
||||
} else if (mask & GROUND) {
|
||||
angle.x = 0.0f;
|
||||
|
||||
if (state == TR::STATE_COMPRESS) {
|
||||
switch (mask & (RIGHT | LEFT | FORTH | BACK)) {
|
||||
case RIGHT : state = TR::STATE_RIGHT_JUMP; break;
|
||||
case LEFT : state = TR::STATE_LEFT_JUMP; break;
|
||||
case FORTH : state = TR::STATE_FORWARD_JUMP; break;
|
||||
case BACK : state = TR::STATE_BACK_JUMP; break;
|
||||
default : state = TR::STATE_UP_JUMP; break;
|
||||
}
|
||||
} else
|
||||
if (mask & JUMP) { // jump button is pressed
|
||||
if ((mask & FORTH) && state == TR::STATE_FORWARD_JUMP)
|
||||
state = TR::STATE_RUN;
|
||||
else
|
||||
state = state == TR::STATE_RUN ? TR::STATE_FORWARD_JUMP : TR::STATE_COMPRESS;
|
||||
} else
|
||||
if (mask & WALK) { // walk button is pressed
|
||||
if (mask & FORTH)
|
||||
state = TR::STATE_WALK;
|
||||
else if (mask & BACK)
|
||||
state = TR::STATE_BACK;
|
||||
else if (mask & LEFT)
|
||||
state = TR::STATE_STEP_LEFT;
|
||||
else if (mask & RIGHT)
|
||||
state = TR::STATE_STEP_RIGHT;
|
||||
else
|
||||
state = TR::STATE_STOP;
|
||||
} else { // only dpad buttons pressed
|
||||
if (mask & FORTH)
|
||||
state = TR::STATE_RUN;
|
||||
else if (mask & BACK)
|
||||
state = TR::STATE_FAST_BACK;
|
||||
else if (mask & LEFT)
|
||||
state = turnTime < FAST_TURN_TIME ? TR::STATE_TURN_LEFT : TR::STATE_FAST_TURN;
|
||||
else if (mask & RIGHT)
|
||||
state = turnTime < FAST_TURN_TIME ? TR::STATE_TURN_RIGHT : TR::STATE_FAST_TURN;
|
||||
else
|
||||
state = TR::STATE_STOP;
|
||||
}
|
||||
|
||||
} else if (mask & WATER) { // underwater
|
||||
|
||||
if (state == TR::STATE_FORWARD_JUMP || state == TR::STATE_UP_JUMP || state == TR::STATE_BACK_JUMP || state == TR::STATE_LEFT_JUMP || state == TR::STATE_RIGHT_JUMP || state == TR::STATE_FALL || state == TR::STATE_REACH) {
|
||||
model.animation = TR::ANIM_WATER_FALL;
|
||||
fTime = 0.0f;
|
||||
state = level->anims[model.animation].state;
|
||||
} else if (state == TR::STATE_SWAN_DIVE) {
|
||||
state = TR::STATE_DIVE;
|
||||
angle.x = -PI * 0.5f;
|
||||
} else
|
||||
if (mask & JUMP)
|
||||
state = TR::STATE_SWIM;
|
||||
else
|
||||
state = (state == TR::STATE_SWIM || velocity.y > GLIDE_SPEED) ? TR::STATE_GLIDE : TR::STATE_TREAD;
|
||||
|
||||
} else { // in the air
|
||||
angle.x = 0.0f;
|
||||
|
||||
if (state == TR::STATE_FORWARD_JUMP) {
|
||||
if (mask & ACTION)
|
||||
state = TR::STATE_REACH;
|
||||
else if ((mask & (FORTH | WALK)) == (FORTH | WALK))
|
||||
state = TR::STATE_SWAN_DIVE;
|
||||
} else if (state != TR::STATE_SWAN_DIVE && state != TR::STATE_REACH && state != TR::STATE_FALL && state != TR::STATE_UP_JUMP && state != TR::STATE_BACK_JUMP && state != TR::STATE_LEFT_JUMP && state != TR::STATE_RIGHT_JUMP) {
|
||||
model.animation = TR::ANIM_FALL;
|
||||
state = level->anims[model.animation].state;
|
||||
}
|
||||
// state = TR::STATE_FALL;
|
||||
// LOG("- speed: %f\n", velocity.length());
|
||||
|
||||
}
|
||||
|
||||
// try to set new state
|
||||
if (!changeState(state)) {
|
||||
int stopState = TR::STATE_FALL;
|
||||
|
||||
if (state == TR::STATE_DIVE)
|
||||
stopState = state;
|
||||
else if ((mask & (GROUND | WATER)) == (GROUND | WATER))
|
||||
stopState = TR::STATE_SURF_TREAD;
|
||||
else if (mask & WATER)
|
||||
stopState = TR::STATE_TREAD;
|
||||
else if (mask & GROUND)
|
||||
stopState = TR::STATE_STOP;
|
||||
|
||||
changeState(stopState);
|
||||
/*
|
||||
if (state == stopState || !changeState(stopState)) {
|
||||
int stopAnim = -1;
|
||||
switch (stopState) {
|
||||
case TR::STATE_FALL : stopAnim = TR::ANIM_FALL; break;
|
||||
}
|
||||
|
||||
if (stopAnim > -1) {
|
||||
model.animation = stopAnim;
|
||||
fTime = 0.0f;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
anim = &level->anims[model.animation]; // get new animation and state (if it has been changed)
|
||||
state = anim->state;
|
||||
|
||||
int fCount = anim->frameEnd - anim->frameStart + 1;
|
||||
int fIndex = int(fTime * 30.0f);
|
||||
|
||||
#ifdef _DEBUG
|
||||
// show state transitions for current animation
|
||||
static bool lState = false;
|
||||
if (Input::down[ikEnter]) {
|
||||
if (!lState) {
|
||||
lState = true;
|
||||
static int snd_id = 0;
|
||||
playSound(snd_id);
|
||||
LOG("sound: %d\n", snd_id++);
|
||||
}
|
||||
/*
|
||||
LOG("state: %d\n", anim->state);
|
||||
for (int i = 0; i < anim->scCount; i++) {
|
||||
auto &sc = level->states[anim->scOffset + i];
|
||||
LOG("-> %d : ", (int)sc.state);
|
||||
for (int j = 0; j < sc.rangesCount; j++) {
|
||||
TR::AnimRange &range = level->ranges[sc.rangesOffset + j];
|
||||
LOG("%d ", range.nextAnimation);
|
||||
}
|
||||
LOG("\n");
|
||||
}*/
|
||||
} else
|
||||
lState = false;
|
||||
#endif
|
||||
|
||||
// calculate turn tilt
|
||||
if (state == TR::STATE_RUN && (mask & (GROUND | WATER)) == GROUND && (mask & (LEFT | RIGHT))) {
|
||||
if (mask & LEFT) angle.z -= Core::deltaTime * TURN_TILT;
|
||||
if (mask & RIGHT) angle.z += Core::deltaTime * TURN_TILT;
|
||||
angle.z = clamp(angle.z, -TURN_TILT, TURN_TILT);
|
||||
} else
|
||||
angle.z -= angle.z * min(Core::deltaTime * 8.0f, 1.0f);
|
||||
|
||||
if (state == TR::STATE_TURN_LEFT || state == TR::STATE_TURN_RIGHT || state == TR::STATE_FAST_TURN)
|
||||
turnTime += Core::deltaTime;
|
||||
else
|
||||
turnTime = 0.0f;
|
||||
|
||||
// get turning angle
|
||||
float w = 0.0f;
|
||||
|
||||
if (state == TR::STATE_SWIM || state == TR::STATE_GLIDE)
|
||||
w = TURN_WATER_FAST;
|
||||
else if (state == TR::STATE_TREAD)
|
||||
w = TURN_WATER_SLOW;
|
||||
else if (state == TR::STATE_RUN || state == TR::STATE_FAST_TURN)
|
||||
w = TURN_FAST;
|
||||
else if (state == TR::STATE_FAST_BACK)
|
||||
w = TURN_FAST_BACK;
|
||||
else if (state == TR::STATE_TURN_LEFT || state == TR::STATE_TURN_RIGHT || state == TR::STATE_WALK)
|
||||
w = TURN_NORMAL;
|
||||
else if (state == TR::STATE_FORWARD_JUMP || state == TR::STATE_BACK)
|
||||
w = TURN_SLOW;
|
||||
|
||||
if (w != 0.0f) {
|
||||
w *= Core::deltaTime;
|
||||
// yaw
|
||||
if (mask & LEFT) { angle.y -= w; velocity = velocity.rotateY(+w); }
|
||||
if (mask & RIGHT) { angle.y += w; velocity = velocity.rotateY(-w); }
|
||||
// pitch (underwater only)
|
||||
if ( ((mask & (GROUND | WATER)) == WATER) && (mask & (FORTH | BACK)) ) {
|
||||
angle.x += ((mask & FORTH) ? -w : w) * 0.5f;
|
||||
angle.x = clamp(angle.x, -PI * 0.5f, PI * 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
// get animation direction
|
||||
float d = 0.0f;
|
||||
switch (state) {
|
||||
case TR::STATE_BACK :
|
||||
case TR::STATE_BACK_JUMP :
|
||||
case TR::STATE_FAST_BACK :
|
||||
d = PI;
|
||||
break;
|
||||
case TR::STATE_STEP_LEFT :
|
||||
case TR::STATE_LEFT_JUMP :
|
||||
d = -PI * 0.5f;
|
||||
break;
|
||||
case TR::STATE_STEP_RIGHT :
|
||||
case TR::STATE_RIGHT_JUMP :
|
||||
d = PI * 0.5f;
|
||||
break;
|
||||
}
|
||||
d += angle.y;
|
||||
|
||||
bool endFrame = fIndex >= fCount;
|
||||
|
||||
// calculate moving speed
|
||||
float dt = Core::deltaTime * 30.0f;
|
||||
|
||||
if (mask & (GROUND | WATER)) {
|
||||
|
||||
if ((mask & (GROUND | WATER)) == (GROUND | WATER)) { // on water
|
||||
|
||||
} else if (mask & WATER) { // underwater
|
||||
|
||||
if (state == TR::STATE_SWIM) {
|
||||
velocity = vec3(angle.x, angle.y) * 35.0f;
|
||||
} else
|
||||
velocity = velocity - velocity * min(1.0f, Core::deltaTime * 2.0f);
|
||||
|
||||
// TODO: apply flow velocity
|
||||
} else { // on ground
|
||||
float speed = anim->speed + anim->accel * (fTime * 30.0f);
|
||||
|
||||
velocity.x = sinf(d) * speed;
|
||||
velocity.z = cosf(d) * speed;
|
||||
velocity.y += GRAVITY * dt;
|
||||
|
||||
}
|
||||
} else
|
||||
velocity.y += GRAVITY * dt;
|
||||
|
||||
|
||||
// apply animation commands
|
||||
int16 *ptr = &level->commands[anim->animCommand];
|
||||
|
||||
for (int i = 0; i < anim->acCount; i++) {
|
||||
int cmd = *ptr++;
|
||||
switch (cmd) {
|
||||
case 0x01 : { // cmd position
|
||||
int16 sx = *ptr++;
|
||||
int16 sy = *ptr++;
|
||||
int16 sz = *ptr++;
|
||||
LOG("move: %d %d %d\n", (int)sx, (int)sy, (int)sz);
|
||||
break;
|
||||
}
|
||||
case 0x02 : { // cmd jump speed
|
||||
int16 sy = *ptr++;
|
||||
int16 sz = *ptr++;
|
||||
if (endFrame) {
|
||||
LOG("jump: %d %d\n", (int)sy, (int)sz);
|
||||
velocity.x = sinf(d) * sz;
|
||||
velocity.y = sy;
|
||||
velocity.z = cosf(d) * sz;
|
||||
LOG("speed: %f\n", velocity.length());
|
||||
onGround = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x03 : // empty hands
|
||||
break;
|
||||
case 0x04 : // kill
|
||||
break;
|
||||
case 0x05 : { // play sound
|
||||
int frame = (*ptr++);
|
||||
int id = (*ptr++) & 0x3FFF;
|
||||
int idx = frame - anim->frameStart;
|
||||
|
||||
// if (fIndex != lastFrame)
|
||||
// LOG("play sound at %d current %d last %d (%d)\n", idx, fIndex, lastFrame, (int)id);
|
||||
|
||||
if (idx > lastFrame && idx <= fIndex) {
|
||||
playSound(id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x06 :
|
||||
if (fIndex != lastFrame && fIndex + anim->frameStart == ptr[0]) {
|
||||
if (ptr[1] == 0) {
|
||||
angle = angle + PI;
|
||||
}
|
||||
}
|
||||
ptr += 2;
|
||||
break;
|
||||
default :
|
||||
LOG("unknown animation command %d\n", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
// check for next animation
|
||||
if (endFrame) {
|
||||
model.animation = anim->nextAnimation;
|
||||
TR::Animation *nextAnim = &level->anims[anim->nextAnimation];
|
||||
fIndex = anim->nextFrame - nextAnim->frameStart;
|
||||
fTime = fIndex / 30.0f;
|
||||
}
|
||||
|
||||
move(velocity * dt);
|
||||
collide();
|
||||
|
||||
updateEntity();
|
||||
|
||||
lastFrame = fIndex;
|
||||
}
|
||||
|
||||
void move(const vec3 &offset) {
|
||||
vec3 p = pos;
|
||||
pos = pos + offset;
|
||||
|
||||
int delta;
|
||||
int d = getOverlap((int)p.x, (int)p.y, (int)p.z, (int)pos.x, (int)pos.z, delta);
|
||||
|
||||
int state = level->anims[getModel().animation].state;
|
||||
bool stop = false;
|
||||
|
||||
if ((d == NO_OVERLAP) ||
|
||||
((state == TR::STATE_WALK || state == TR::STATE_BACK || state == TR::STATE_STEP_LEFT || state == TR::STATE_STEP_RIGHT) && delta > 256) /*||
|
||||
(delta < -256) */) {
|
||||
|
||||
pos = p;
|
||||
|
||||
TR::Model &model = getModel();
|
||||
TR::Animation *anim = &level->anims[model.animation];
|
||||
|
||||
// smashes
|
||||
if (onGround) { // onGround
|
||||
if (d >= 256 * 4 && anim->state == TR::STATE_RUN)
|
||||
model.animation = TR::ANIM_SMASH_RUN_LEFT; // TODO: RIGHT
|
||||
else
|
||||
model.animation = TR::ANIM_STAND;
|
||||
velocity.x = velocity.z = 0.0f;
|
||||
fTime = 0;
|
||||
} else if (inWater) { // in water
|
||||
// do nothing
|
||||
//velocity.x = velocity.z = 0.0f;
|
||||
} else { // in the air
|
||||
model.animation = TR::ANIM_SMASH_JUMP;
|
||||
velocity.x = -velocity.x * 0.5f;
|
||||
velocity.z = -velocity.z * 0.5f;
|
||||
velocity.y = 0.0f;
|
||||
fTime = 0;
|
||||
}
|
||||
|
||||
} else {
|
||||
TR::Entity &entity = getEntity();
|
||||
entity.x = (int)pos.x;
|
||||
entity.y = (int)pos.y;
|
||||
entity.z = (int)pos.z;
|
||||
}
|
||||
vec3 getDir() const {
|
||||
return vec3(sinf(PI - angle.y) * cosf(-angle.x), -sinf(-angle.x), cosf(PI - angle.y) * cosf(-angle.x));
|
||||
}
|
||||
|
||||
void collide() {
|
||||
TR::Entity &entity = getEntity();
|
||||
|
||||
FloorInfo info = getFloorInfo(entity.x, entity.z);
|
||||
|
||||
|
||||
/*
|
||||
float hmin = 0.0f, hmax = -768.0f;
|
||||
if (inWater) {
|
||||
@ -713,27 +324,125 @@ struct Lara : Controller {
|
||||
entity.room = s.roomAbove;
|
||||
}
|
||||
*/
|
||||
|
||||
int state = level->anims[getModel().animation].state;
|
||||
|
||||
// TODO: use a brain!
|
||||
float extra = 0;
|
||||
if (state == TR::STATE_WALK ||
|
||||
state == TR::STATE_RUN ||
|
||||
state == TR::STATE_STOP ||
|
||||
state == TR::STATE_FAST_BACK ||
|
||||
state == TR::STATE_TURN_RIGHT ||
|
||||
state == TR::STATE_TURN_LEFT ||
|
||||
state == TR::STATE_BACK ||
|
||||
state == TR::STATE_FAST_TURN ||
|
||||
state == TR::STATE_STEP_RIGHT ||
|
||||
state == TR::STATE_STEP_LEFT ||
|
||||
state == TR::STATE_ROLL)
|
||||
extra = 256 + 128;
|
||||
|
||||
onGround = (pos.y + extra >= info.floor) && (info.roomBelow == 0xFF) && !(getRoom().flags & TR::ROOM_FLAG_WATER);
|
||||
}
|
||||
|
||||
virtual void updateVelocity() {}
|
||||
virtual void move() {}
|
||||
virtual Stand getStand() { return STAND_AIR; }
|
||||
virtual int getStateAir() { return state; }
|
||||
virtual int getStateGround() { return state; }
|
||||
virtual int getStateUnderwater() { return state; }
|
||||
virtual int getStateOnwater() { return state; }
|
||||
virtual int getStateDeath() { return state; }
|
||||
virtual int getStateDefault() { return state; }
|
||||
virtual int getInputMask() { return 0; }
|
||||
|
||||
virtual int getState(Stand stand) {
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
|
||||
int state = anim->state;
|
||||
|
||||
if (mask & DEATH)
|
||||
state = getStateDeath();
|
||||
else if (stand == STAND_GROUND)
|
||||
state = getStateGround();
|
||||
else if (stand == STAND_AIR)
|
||||
state = getStateAir();
|
||||
else if (stand == STAND_UNDERWATER)
|
||||
state = getStateUnderwater();
|
||||
else
|
||||
state = getStateOnwater();
|
||||
|
||||
// try to set new state
|
||||
if (!setState(state))
|
||||
setState(getStateDefault());
|
||||
|
||||
return level->anims[animIndex].state;
|
||||
}
|
||||
|
||||
virtual void updateBegin() {
|
||||
animTime += Core::deltaTime;
|
||||
mask = getInputMask();
|
||||
state = getState(stand = getStand());
|
||||
}
|
||||
|
||||
virtual void updateEnd() {
|
||||
int frameIndex = int(animTime * 30.0f);
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
bool endFrame = frameIndex > anim->frameEnd - anim->frameStart;
|
||||
|
||||
// apply animation commands
|
||||
int16 *ptr = &level->commands[anim->animCommand];
|
||||
|
||||
for (int i = 0; i < anim->acCount; i++) {
|
||||
int cmd = *ptr++;
|
||||
switch (cmd) {
|
||||
case 0x01 : { // cmd position
|
||||
int16 sx = *ptr++;
|
||||
int16 sy = *ptr++;
|
||||
int16 sz = *ptr++;
|
||||
LOG("move: %d %d %d\n", (int)sx, (int)sy, (int)sz);
|
||||
break;
|
||||
}
|
||||
case 0x02 : { // cmd jump speed
|
||||
int16 sy = *ptr++;
|
||||
int16 sz = *ptr++;
|
||||
if (endFrame) {
|
||||
LOG("jump: %d %d\n", (int)sy, (int)sz);
|
||||
velocity.x = sinf(angleExt) * sz;
|
||||
velocity.y = sy;
|
||||
velocity.z = cosf(angleExt) * sz;
|
||||
LOG("speed: %f\n", velocity.length());
|
||||
stand = STAND_AIR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x03 : // empty hands
|
||||
break;
|
||||
case 0x04 : // kill
|
||||
break;
|
||||
case 0x05 : { // play sound
|
||||
int frame = (*ptr++);
|
||||
int id = (*ptr++) & 0x3FFF;
|
||||
int idx = frame - anim->frameStart;
|
||||
|
||||
if (idx > animPrevFrame && idx <= frameIndex) {
|
||||
playSound(id);
|
||||
// LOG("play sound %d\n", getEntity().id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x06 : // effect
|
||||
if (frameIndex != animPrevFrame && frameIndex + anim->frameStart == ptr[0]) {
|
||||
if (ptr[1] == 0) // rolling
|
||||
angle.y = angle.y + PI;
|
||||
}
|
||||
ptr += 2;
|
||||
break;
|
||||
default :
|
||||
LOG("unknown animation command %d\n", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
if (endFrame) // if animation is end - switch to next
|
||||
setAnimation(anim->nextAnimation, anim->nextFrame);
|
||||
else
|
||||
animPrevFrame = frameIndex;
|
||||
|
||||
updateVelocity();
|
||||
move();
|
||||
collide();
|
||||
|
||||
updateEntity();
|
||||
}
|
||||
|
||||
virtual void updateState() {}
|
||||
|
||||
virtual void update() {
|
||||
updateBegin();
|
||||
updateState();
|
||||
updateEnd();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
76
src/debug.h
76
src/debug.h
@ -4,8 +4,28 @@
|
||||
#include "core.h"
|
||||
#include "format.h"
|
||||
|
||||
extern HDC hDC;
|
||||
|
||||
namespace Debug {
|
||||
|
||||
static GLuint font;
|
||||
|
||||
void init() {
|
||||
font = glGenLists(256);
|
||||
HDC hdc = hDC;
|
||||
HFONT hfont = CreateFontA(-MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0,
|
||||
0, 0, FW_BOLD, 0, 0, 0,
|
||||
ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
|
||||
ANTIALIASED_QUALITY, DEFAULT_PITCH, "Courier New");
|
||||
SelectObject(hdc, hfont);
|
||||
wglUseFontBitmaps(hdc, 0, 256, font);
|
||||
DeleteObject(hfont);
|
||||
}
|
||||
|
||||
void free() {
|
||||
glDeleteLists(font, 256);
|
||||
}
|
||||
|
||||
void begin() {
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadMatrixf((GLfloat*)&Core::mProj);
|
||||
@ -111,6 +131,41 @@ namespace Debug {
|
||||
glVertex3fv((GLfloat*)&p);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
void text(const vec2 &pos, const vec4 &color, const char *str) {
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(0, Core::width, Core::height, 0, 0, 1);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glColor4fv((GLfloat*)&color);
|
||||
glRasterPos2f(pos.x, pos.y);
|
||||
glListBase(font);
|
||||
glCallLists(strlen(str), GL_UNSIGNED_BYTE, str);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
void text(const vec3 &pos, const vec4 &color, const char *str) {
|
||||
vec4 p = Core::mViewProj * vec4(pos, 1);
|
||||
if (p.w > 0) {
|
||||
p.xyz = p.xyz * (1.0f / p.w);
|
||||
p.y = -p.y;
|
||||
p.xyz = (p.xyz * 0.5f + vec3(0.5f)) * vec3(Core::width, Core::height, 1.0f);
|
||||
text(vec2(p.x, p.y), color, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Level {
|
||||
@ -373,12 +428,14 @@ namespace Debug {
|
||||
sm->getBox(true, m.rotation, min, max);
|
||||
Debug::Draw::box(offset + min - vec3(10.0f), offset + max + vec3(10.0f), vec4(1, 0, 0, 0.50));
|
||||
}
|
||||
|
||||
/*
|
||||
TR::Mesh *mesh = (TR::Mesh*)&level.meshData[level.meshOffsets[sm->mesh] / 2];
|
||||
|
||||
ASSERT(mesh->radius == 0 || mesh->radius == 0x10000);
|
||||
|
||||
Debug::Draw::sphere(offset + (min + max) * 0.5f, 128, mesh->radius == 0 ? vec4(0, 0, 1, 1) : vec4(0, 1, 0, 1));
|
||||
{ //if (mesh->collider.info || mesh->collider.flags) {
|
||||
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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
// dynamic objects
|
||||
@ -435,7 +492,14 @@ namespace Debug {
|
||||
|
||||
int offset = level.meshOffsets[m.mStart + k];
|
||||
TR::Mesh *mesh = (TR::Mesh*)&level.meshData[offset / 2];
|
||||
Debug::Draw::sphere(matrix * joint * mesh->center, mesh->radius & 0x3FF, mesh->radius > 0x3FF ? vec4(1, 0, 0, 0.5f) : vec4(0, 1, 1, 0.5f));
|
||||
Debug::Draw::sphere(matrix * joint * mesh->center, mesh->collider.radius, mesh->collider.info ? vec4(1, 0, 0, 0.5f) : vec4(0, 1, 1, 0.5f));
|
||||
/*
|
||||
{ //if (e.id != 0) {
|
||||
char buf[255];
|
||||
sprintf(buf, "(%d) radius %d info %d flags %d", e.id, (int)mesh->collider.radius, (int)mesh->collider.info, (int)mesh->collider.flags);
|
||||
Debug::Draw::text(matrix * joint * mesh->center, vec4(0.5, 1, 0.5, 1), buf);
|
||||
}
|
||||
*/
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
84
src/enemy.h
Normal file
84
src/enemy.h
Normal file
@ -0,0 +1,84 @@
|
||||
#ifndef H_ENEMY
|
||||
#define H_ENEMY
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
struct Enemy : Controller {
|
||||
Enemy(TR::Level *level, int entity) : Controller(level, entity) {}
|
||||
|
||||
virtual Stand getStand() {
|
||||
return STAND_GROUND;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Wolf : Enemy {
|
||||
|
||||
enum {
|
||||
STATE_STOP = 1,
|
||||
STATE_WALK = 2,
|
||||
STATE_RUN = 3,
|
||||
STATE_STALKING = 5,
|
||||
STATE_JUMP = 6,
|
||||
STATE_HOWL = 7,
|
||||
STATE_SLEEP = 8,
|
||||
STATE_GROWL = 9,
|
||||
STATE_10 = 10, // WTF?
|
||||
STATE_ATTACK = 12,
|
||||
};
|
||||
|
||||
Wolf(TR::Level *level, int entity) : Enemy(level, entity) {}
|
||||
|
||||
virtual int getStateGround() {
|
||||
// STATE_SLEEP -> STATE_STOP
|
||||
// STATE_STOP -> STATE_WALK, STATE_HOWL, STATE_SLEEP, STATE_GROWL
|
||||
// STATE_WALK -> NULL
|
||||
// STATE_RUN -> STaTE_JUMP, STATe_GROWL, STATE_10
|
||||
// STATE_STALKING -> STATE_RUN, STATE_GROWL, STATE_BITING
|
||||
// STATE_JUMP -> STATE_RUN
|
||||
// STATE_GROWL -> STATE_STOP, STATE_RUN, STATE_STALKING, STATE_HOWL, STATE_ATTACK
|
||||
// STATE_BITING -> NULL
|
||||
|
||||
// if (state == STATE_SLEEP) return STATE_STOP;
|
||||
// if (state == STATE_STOP) return STATE_GROWL;
|
||||
// if (state == STATE_GROWL) return STATE_ATTACK;
|
||||
// if (state == STATE_RUN) return STATE_10;
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Bear : Enemy {
|
||||
|
||||
enum {
|
||||
STATE_STOP = 1,
|
||||
};
|
||||
|
||||
Bear(TR::Level *level, int entity) : Enemy(level, entity) {}
|
||||
|
||||
virtual int getStateGround() {
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Bat : Enemy {
|
||||
|
||||
enum {
|
||||
STATE_AWAKE = 1,
|
||||
STATE_FLY = 2,
|
||||
};
|
||||
|
||||
Bat(TR::Level *level, int entity) : Enemy(level, entity) {}
|
||||
|
||||
virtual Stand getStand() {
|
||||
return STAND_AIR;
|
||||
}
|
||||
|
||||
virtual int getStateAir() {
|
||||
animTime = 0.0f;
|
||||
return STATE_AWAKE;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
106
src/format.h
106
src/format.h
@ -47,9 +47,9 @@ namespace TR {
|
||||
#define ENTITY_ENEMY_RAPTOR 19
|
||||
#define ENTITY_ENEMY_MUTANT 20
|
||||
|
||||
#define ENTITY_ENEMY_CENTAUR 23
|
||||
#define ENTITY_ENEMY_MUMMY 24
|
||||
#define ENTITY_ENEMY_LARSON 27
|
||||
#define ENTITY_ENEMY_CENTAUR 23
|
||||
#define ENTITY_ENEMY_MUMMY 24
|
||||
#define ENTITY_ENEMY_LARSON 27
|
||||
|
||||
#define ENTITY_CRYSTAL 83
|
||||
|
||||
@ -71,76 +71,6 @@ namespace TR {
|
||||
#define ENTITY_AMMO_SHOTGUN 89
|
||||
#define ENTITY_AMMO_MAGNUM 90
|
||||
|
||||
// http://www.tombraiderforums.com/showthread.php?t=148859&highlight=Explanation+left
|
||||
enum LaraAnim : int32 {
|
||||
ANIM_STAND = 11,
|
||||
ANIM_FALL = 34,
|
||||
ANIM_SMASH_JUMP = 32,
|
||||
ANIM_SMASH_RUN_LEFT = 53,
|
||||
ANIM_SMASH_RUN_RIGHT = 54,
|
||||
ANIM_WATER_FALL = 112,
|
||||
};
|
||||
|
||||
// http://www.tombraiderforums.com/showthread.php?t=211681
|
||||
enum LaraState : int32 {
|
||||
STATE_WALK,
|
||||
STATE_RUN,
|
||||
STATE_STOP,
|
||||
STATE_FORWARD_JUMP,
|
||||
STATE_4,
|
||||
STATE_FAST_BACK,
|
||||
STATE_TURN_RIGHT,
|
||||
STATE_TURN_LEFT,
|
||||
STATE_DEATH,
|
||||
STATE_FALL,
|
||||
STATE_HANG,
|
||||
STATE_REACH,
|
||||
STATE_SPLAT,
|
||||
STATE_TREAD,
|
||||
STATE_FAST_TURN_14,
|
||||
STATE_COMPRESS,
|
||||
STATE_BACK,
|
||||
STATE_SWIM,
|
||||
STATE_GLIDE,
|
||||
STATE_NULL_19,
|
||||
STATE_FAST_TURN,
|
||||
STATE_STEP_RIGHT,
|
||||
STATE_STEP_LEFT,
|
||||
STATE_ROLL,
|
||||
STATE_SLIDE,
|
||||
STATE_BACK_JUMP,
|
||||
STATE_RIGHT_JUMP,
|
||||
STATE_LEFT_JUMP,
|
||||
STATE_UP_JUMP,
|
||||
STATE_FALL_BACK,
|
||||
STATE_HANG_LEFT,
|
||||
STATE_HANG_RIGHT,
|
||||
STATE_SLIDE_BACK,
|
||||
STATE_SURF_TREAD,
|
||||
STATE_SURF_SWIM,
|
||||
STATE_DIVE,
|
||||
STATE_PUSH_BLOCK,
|
||||
STATE_PULL_BLOCK,
|
||||
STATE_PUSH_PULL_READY,
|
||||
STATE_PICK_UP,
|
||||
STATE_SWITCH_ON,
|
||||
STATE_SWITCH_OFF,
|
||||
STATE_USE_KEY,
|
||||
STATE_USE_PUZZLE,
|
||||
STATE_UNDERWATER_DEATH,
|
||||
STATE_ROLL_45,
|
||||
STATE_SPECIAL,
|
||||
STATE_SURF_BACK,
|
||||
STATE_SURF_LEFT,
|
||||
STATE_SURF_RIGHT,
|
||||
STATE_NULL_50,
|
||||
STATE_NULL_51,
|
||||
STATE_SWAN_DIVE,
|
||||
STATE_FAST_DIVE,
|
||||
STATE_HANDSTAND,
|
||||
STATE_WATER_OUT,
|
||||
STATE_MAX };
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct fixed {
|
||||
@ -268,14 +198,19 @@ namespace TR {
|
||||
uint16 boxIndex:15, end:1;
|
||||
};
|
||||
|
||||
struct Collider {
|
||||
uint16 radius:10, info:6;
|
||||
uint16 flags:16;
|
||||
};
|
||||
|
||||
struct Mesh {
|
||||
Vertex center;
|
||||
int32 radius;
|
||||
Vertex center;
|
||||
Collider collider;
|
||||
|
||||
int16 vCount;
|
||||
Vertex *vertices; // List of vertices (relative coordinates)
|
||||
|
||||
int16 nCount;
|
||||
uint16 vCount;
|
||||
Vertex *vertices; // List of vertices (relative coordinates)
|
||||
|
||||
int16 nCount;
|
||||
union {
|
||||
Vertex *normals;
|
||||
int16 *lights; // if nCount < 0 -> (abs(nCount))
|
||||
@ -303,7 +238,9 @@ namespace TR {
|
||||
uint16 flags; // 0x0100 indicates "initially invisible", 0x3e00 is Activation Mask
|
||||
// 0x3e00 indicates "open" or "activated"; these can be XORed with
|
||||
// related FloorData::FDlist fields (e.g. for switches)
|
||||
uint16 align; // ! not exists in file !
|
||||
// not exists in file
|
||||
uint16 align;
|
||||
void *controller; // Controller implementation or NULL
|
||||
};
|
||||
|
||||
struct Animation {
|
||||
@ -474,7 +411,7 @@ namespace TR {
|
||||
int16 floor; // Height value in global units
|
||||
uint16 overlap; // Index into Overlaps[].
|
||||
|
||||
bool contains(int x, int z) {
|
||||
bool contains(uint32 x, uint32 z) {
|
||||
return x >= minX && x <= maxX && z >= minZ && z <= maxZ;
|
||||
}
|
||||
};
|
||||
@ -673,8 +610,11 @@ namespace TR {
|
||||
stream.read(animTexturesData, stream.read(animTexturesDataSize));
|
||||
// entities (enemies, items, lara etc.)
|
||||
entities = new Entity[stream.read(entitiesCount)];
|
||||
for (int i = 0; i < entitiesCount; i++)
|
||||
stream.raw(&entities[i], sizeof(entities[i]) - sizeof(entities[i].align));
|
||||
for (int i = 0; i < entitiesCount; i++) {
|
||||
stream.raw(&entities[i], sizeof(entities[i]) - sizeof(entities[i].align) - sizeof(entities[i].controller));
|
||||
entities[i].align = 0;
|
||||
entities[i].controller = NULL;
|
||||
}
|
||||
// palette
|
||||
stream.seek(32 * 256); // skip lightmap palette
|
||||
|
||||
|
401
src/lara.h
Normal file
401
src/lara.h
Normal file
@ -0,0 +1,401 @@
|
||||
#ifndef H_LARA
|
||||
#define H_LARA
|
||||
|
||||
#include "controller.h"
|
||||
|
||||
#define FAST_TURN_TIME 1.0f
|
||||
|
||||
#define TURN_FAST PI
|
||||
#define TURN_FAST_BACK PI * 3.0f / 4.0f
|
||||
#define TURN_NORMAL PI / 2.0f
|
||||
#define TURN_SLOW PI / 3.0f
|
||||
#define TURN_TILT PI / 18.0f
|
||||
#define TURN_WATER_FAST PI * 3.0f / 4.0f
|
||||
#define TURN_WATER_SLOW PI * 2.0f / 3.0f
|
||||
#define GLIDE_SPEED 50.0f
|
||||
|
||||
struct Lara : Controller {
|
||||
|
||||
// http://www.tombraiderforums.com/showthread.php?t=148859&highlight=Explanation+left
|
||||
enum LaraAnim : int32 {
|
||||
ANIM_STAND = 11,
|
||||
ANIM_FALL = 34,
|
||||
ANIM_SMASH_JUMP = 32,
|
||||
ANIM_SMASH_RUN_LEFT = 53,
|
||||
ANIM_SMASH_RUN_RIGHT = 54,
|
||||
ANIM_WATER_FALL = 112,
|
||||
ANIM_STAND_ROLL = 146,
|
||||
};
|
||||
|
||||
// http://www.tombraiderforums.com/showthread.php?t=211681
|
||||
enum LaraState : int32 {
|
||||
STATE_WALK,
|
||||
STATE_RUN,
|
||||
STATE_STOP,
|
||||
STATE_FORWARD_JUMP,
|
||||
STATE_4,
|
||||
STATE_FAST_BACK,
|
||||
STATE_TURN_RIGHT,
|
||||
STATE_TURN_LEFT,
|
||||
STATE_DEATH,
|
||||
STATE_FALL,
|
||||
STATE_HANG,
|
||||
STATE_REACH,
|
||||
STATE_SPLAT,
|
||||
STATE_TREAD,
|
||||
STATE_FAST_TURN_14,
|
||||
STATE_COMPRESS,
|
||||
STATE_BACK,
|
||||
STATE_SWIM,
|
||||
STATE_GLIDE,
|
||||
STATE_NULL_19,
|
||||
STATE_FAST_TURN,
|
||||
STATE_STEP_RIGHT,
|
||||
STATE_STEP_LEFT,
|
||||
STATE_ROLL,
|
||||
STATE_SLIDE,
|
||||
STATE_BACK_JUMP,
|
||||
STATE_RIGHT_JUMP,
|
||||
STATE_LEFT_JUMP,
|
||||
STATE_UP_JUMP,
|
||||
STATE_FALL_BACK,
|
||||
STATE_HANG_LEFT,
|
||||
STATE_HANG_RIGHT,
|
||||
STATE_SLIDE_BACK,
|
||||
STATE_SURF_TREAD,
|
||||
STATE_SURF_SWIM,
|
||||
STATE_DIVE,
|
||||
STATE_PUSH_BLOCK,
|
||||
STATE_PULL_BLOCK,
|
||||
STATE_PUSH_PULL_READY,
|
||||
STATE_PICK_UP,
|
||||
STATE_SWITCH_ON,
|
||||
STATE_SWITCH_OFF,
|
||||
STATE_USE_KEY,
|
||||
STATE_USE_PUZZLE,
|
||||
STATE_UNDERWATER_DEATH,
|
||||
STATE_ROLL_45,
|
||||
STATE_SPECIAL,
|
||||
STATE_SURF_BACK,
|
||||
STATE_SURF_LEFT,
|
||||
STATE_SURF_RIGHT,
|
||||
STATE_NULL_50,
|
||||
STATE_NULL_51,
|
||||
STATE_SWAN_DIVE,
|
||||
STATE_FAST_DIVE,
|
||||
STATE_HANDSTAND,
|
||||
STATE_WATER_OUT,
|
||||
STATE_MAX };
|
||||
|
||||
Lara(TR::Level *level, int entity) : Controller(level, entity) {
|
||||
/*
|
||||
// level 2 (pool)
|
||||
pos = vec3(70067, -256, 29104);
|
||||
angle = vec3(0.0f, -0.68f, 0.0f);
|
||||
getEntity().room = 15;
|
||||
*/
|
||||
/*
|
||||
// level 2 (wolf)
|
||||
pos = vec3(75671, -1024, 22862);
|
||||
angle = vec3(0.0f, -PI * 0.25f, 0.0f);
|
||||
getEntity().room = 13;
|
||||
*/
|
||||
/*
|
||||
// level 3a
|
||||
pos = vec3(41015, 3584, 34494);
|
||||
angle = vec3(0.0f, -PI, 0.0f);
|
||||
getEntity().room = 51;
|
||||
*/
|
||||
/*
|
||||
// level 1
|
||||
pos = vec3(20215, 6656, 52942);
|
||||
angle = vec3(0.0f, PI, 0.0f);
|
||||
getEntity().room = 14;
|
||||
*/
|
||||
}
|
||||
|
||||
bool isMovingState(int state) {
|
||||
return state == STATE_RUN || state == STATE_WALK || state == STATE_STEP_LEFT || state == STATE_STEP_RIGHT;
|
||||
}
|
||||
|
||||
virtual Stand getStand() {
|
||||
if (getRoom().flags & TR::ROOM_FLAG_WATER)
|
||||
return STAND_UNDERWATER; // TODO: ONWATER
|
||||
|
||||
int extra = isMovingState(state) ? 256 : 0;
|
||||
|
||||
TR::Entity &e = getEntity();
|
||||
FloorInfo info = getFloorInfo(e.x, e.z);
|
||||
if (info.roomBelow == 0xFF && e.y + extra >= info.floor)
|
||||
return STAND_GROUND;
|
||||
|
||||
return STAND_AIR;
|
||||
}
|
||||
|
||||
virtual int getStateAir() {
|
||||
angle.x = 0.0f;
|
||||
|
||||
if (state == STATE_FORWARD_JUMP) {
|
||||
if (mask & ACTION) return STATE_REACH;
|
||||
if ((mask & (FORTH | WALK)) == (FORTH | WALK)) return STATE_SWAN_DIVE;
|
||||
} else
|
||||
if (state != STATE_SWAN_DIVE && state != STATE_REACH && state != STATE_FALL && state != STATE_UP_JUMP && state != STATE_BACK_JUMP && state != STATE_LEFT_JUMP && state != STATE_RIGHT_JUMP && state != STATE_ROLL)
|
||||
return setAnimation(ANIM_FALL);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
virtual int getStateGround() {
|
||||
angle.x = 0.0f;
|
||||
|
||||
if ( (mask & (FORTH | BACK)) == (FORTH | BACK) && (state == STATE_STOP || state == STATE_RUN) )
|
||||
return setAnimation(ANIM_STAND_ROLL);
|
||||
|
||||
// ready to jump
|
||||
if (state == STATE_COMPRESS) {
|
||||
switch (mask & (RIGHT | LEFT | FORTH | BACK)) {
|
||||
case RIGHT : return STATE_RIGHT_JUMP;
|
||||
case LEFT : return STATE_LEFT_JUMP;
|
||||
case FORTH : return STATE_FORWARD_JUMP;
|
||||
case BACK : return STATE_BACK_JUMP;
|
||||
default : return STATE_UP_JUMP;
|
||||
}
|
||||
}
|
||||
|
||||
// jump button is pressed
|
||||
if (mask & JUMP) {
|
||||
if ((mask & FORTH) && state == STATE_FORWARD_JUMP)
|
||||
return STATE_RUN;
|
||||
return state == STATE_RUN ? STATE_FORWARD_JUMP : STATE_COMPRESS;
|
||||
}
|
||||
|
||||
// walk button is pressed
|
||||
if (mask & WALK) {
|
||||
if (mask & FORTH) return STATE_WALK;
|
||||
if (mask & BACK) return STATE_BACK;
|
||||
if (mask & LEFT) return STATE_STEP_LEFT;
|
||||
if (mask & RIGHT) return STATE_STEP_RIGHT;
|
||||
return STATE_STOP;
|
||||
}
|
||||
|
||||
// only dpad buttons pressed
|
||||
if (mask & FORTH) return STATE_RUN;
|
||||
if (mask & BACK) return STATE_FAST_BACK;
|
||||
if (mask & LEFT) return turnTime < FAST_TURN_TIME ? STATE_TURN_LEFT : STATE_FAST_TURN;
|
||||
if (mask & RIGHT) return turnTime < FAST_TURN_TIME ? STATE_TURN_RIGHT : STATE_FAST_TURN;
|
||||
return STATE_STOP;
|
||||
}
|
||||
|
||||
virtual int getStateUnderwater() {
|
||||
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)
|
||||
return setAnimation(ANIM_WATER_FALL);
|
||||
|
||||
if (state == STATE_SWAN_DIVE) {
|
||||
angle.x = -PI * 0.5f;
|
||||
return STATE_DIVE;
|
||||
}
|
||||
|
||||
if (mask & JUMP) return STATE_SWIM;
|
||||
return (state == STATE_SWIM || velocity.y > GLIDE_SPEED) ? STATE_GLIDE : STATE_TREAD;
|
||||
}
|
||||
|
||||
virtual int getStateOnwater() {
|
||||
angle.x = 0.0f;
|
||||
return STATE_SURF_TREAD;
|
||||
}
|
||||
|
||||
virtual int getStateDeath() {
|
||||
return STATE_DEATH;
|
||||
}
|
||||
|
||||
virtual int getStateDefault() {
|
||||
if (state == STATE_DIVE) return state;
|
||||
if (stand == STAND_ONWATER) return STATE_SURF_TREAD;
|
||||
if (stand == STAND_UNDERWATER) return STATE_TREAD;
|
||||
if (stand == STAND_GROUND) return STATE_STOP;
|
||||
return STATE_FALL;
|
||||
}
|
||||
|
||||
virtual int getInputMask() {
|
||||
mask = 0;
|
||||
if (Input::down[ikW] || Input::joy.L.y < 0) mask |= FORTH;
|
||||
if (Input::down[ikS] || Input::joy.L.y > 0) mask |= BACK;
|
||||
if (Input::down[ikA] || Input::joy.L.x < 0) mask |= LEFT;
|
||||
if (Input::down[ikD] || Input::joy.L.x > 0) mask |= RIGHT;
|
||||
if (Input::down[ikSpace] || Input::down[ikJoyX]) mask |= JUMP;
|
||||
if (Input::down[ikShift] || Input::down[ikJoyLT]) mask |= WALK;
|
||||
if (Input::down[ikE] || Input::down[ikMouseL] || Input::down[ikJoyA]) mask |= ACTION;
|
||||
if (Input::down[ikQ] || Input::down[ikMouseR] || Input::down[ikJoyY]) mask |= WEAPON;
|
||||
if (health <= 0) mask = DEATH;
|
||||
return mask;
|
||||
}
|
||||
|
||||
virtual void updateState() {
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
|
||||
int fCount = anim->frameEnd - anim->frameStart;
|
||||
int fIndex = int(animTime * 30.0f);
|
||||
|
||||
float rot = 0.0f;
|
||||
|
||||
#ifdef _DEBUG
|
||||
// show state transitions for current animation
|
||||
static bool lState = false;
|
||||
if (Input::down[ikEnter]) {
|
||||
if (!lState) {
|
||||
lState = true;
|
||||
static int snd_id = 0;
|
||||
playSound(snd_id);
|
||||
LOG("sound: %d\n", snd_id++);
|
||||
}
|
||||
/*
|
||||
LOG("state: %d\n", anim->state);
|
||||
for (int i = 0; i < anim->scCount; i++) {
|
||||
auto &sc = level->states[anim->scOffset + i];
|
||||
LOG("-> %d : ", (int)sc.state);
|
||||
for (int j = 0; j < sc.rangesCount; j++) {
|
||||
AnimRange &range = level->ranges[sc.rangesOffset + j];
|
||||
LOG("%d ", range.nextAnimation);
|
||||
}
|
||||
LOG("\n");
|
||||
}*/
|
||||
} else
|
||||
lState = false;
|
||||
#endif
|
||||
|
||||
// calculate turn tilt
|
||||
if (state == STATE_RUN && (mask & (LEFT | RIGHT))) {
|
||||
if (mask & LEFT) angle.z -= Core::deltaTime * TURN_TILT;
|
||||
if (mask & RIGHT) angle.z += Core::deltaTime * TURN_TILT;
|
||||
angle.z = clamp(angle.z, -TURN_TILT, TURN_TILT);
|
||||
} else
|
||||
angle.z -= angle.z * min(Core::deltaTime * 8.0f, 1.0f);
|
||||
|
||||
if (state == STATE_TURN_LEFT || state == STATE_TURN_RIGHT || state == STATE_FAST_TURN)
|
||||
turnTime += Core::deltaTime;
|
||||
else
|
||||
turnTime = 0.0f;
|
||||
|
||||
// get turning angle
|
||||
float w = 0.0f;
|
||||
|
||||
if (state == STATE_SWIM || state == STATE_GLIDE)
|
||||
w = TURN_WATER_FAST;
|
||||
else if (state == STATE_TREAD)
|
||||
w = TURN_WATER_SLOW;
|
||||
else if (state == STATE_RUN || state == STATE_FAST_TURN)
|
||||
w = TURN_FAST;
|
||||
else if (state == STATE_FAST_BACK)
|
||||
w = TURN_FAST_BACK;
|
||||
else if (state == STATE_TURN_LEFT || state == STATE_TURN_RIGHT || state == STATE_WALK)
|
||||
w = TURN_NORMAL;
|
||||
else if (state == STATE_FORWARD_JUMP || state == STATE_BACK)
|
||||
w = TURN_SLOW;
|
||||
|
||||
if (w != 0.0f) {
|
||||
w *= Core::deltaTime;
|
||||
// yaw
|
||||
if (mask & LEFT) { angle.y -= w; velocity = velocity.rotateY(+w); }
|
||||
if (mask & RIGHT) { angle.y += w; velocity = velocity.rotateY(-w); }
|
||||
// pitch (underwater only)
|
||||
if (stand == STAND_UNDERWATER && (mask & (FORTH | BACK)) ) {
|
||||
angle.x += ((mask & FORTH) ? -w : w) * 0.5f;
|
||||
angle.x = clamp(angle.x, -PI * 0.5f, PI * 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
// get animation direction
|
||||
angleExt = angle.y;
|
||||
switch (state) {
|
||||
case STATE_BACK :
|
||||
case STATE_BACK_JUMP :
|
||||
case STATE_FAST_BACK :
|
||||
angleExt += PI;
|
||||
break;
|
||||
case STATE_STEP_LEFT :
|
||||
case STATE_LEFT_JUMP :
|
||||
angleExt -= PI * 0.5f;
|
||||
break;
|
||||
case STATE_STEP_RIGHT :
|
||||
case STATE_RIGHT_JUMP :
|
||||
angleExt += PI * 0.5f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void updateVelocity() {
|
||||
// calculate moving speed
|
||||
float dt = Core::deltaTime * 30.0f;
|
||||
|
||||
if (stand == STAND_AIR) {
|
||||
velocity.y += GRAVITY * dt;
|
||||
} else if (stand == STAND_ONWATER) {
|
||||
|
||||
} else if (stand == STAND_UNDERWATER) {
|
||||
|
||||
if (state == STATE_SWIM)
|
||||
velocity = vec3(angle.x, angle.y) * 35.0f;
|
||||
else
|
||||
velocity = velocity - velocity * min(1.0f, Core::deltaTime * 2.0f);
|
||||
|
||||
// TODO: apply flow velocity
|
||||
} else if (stand == STAND_GROUND) {
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
|
||||
float speed = anim->speed + anim->accel * (animTime * 30.0f);
|
||||
|
||||
velocity.x = sinf(angleExt) * speed;
|
||||
velocity.z = cosf(angleExt) * speed;
|
||||
velocity.y += GRAVITY * dt;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void move() {
|
||||
vec3 offset = velocity * Core::deltaTime * 30.0f;
|
||||
|
||||
vec3 p = pos;
|
||||
pos = pos + offset;
|
||||
|
||||
int delta;
|
||||
int d = getOverlap((int)p.x, (int)p.y, (int)p.z, (int)pos.x, (int)pos.z, delta);
|
||||
|
||||
int state = level->anims[animIndex].state;
|
||||
bool stop = false;
|
||||
|
||||
if ((d == NO_OVERLAP) ||
|
||||
((state == STATE_WALK || state == STATE_BACK || state == STATE_STEP_LEFT || state == STATE_STEP_RIGHT) && delta > 256) /*||
|
||||
(delta < -256) */) {
|
||||
|
||||
pos = p;
|
||||
|
||||
TR::Animation *anim = &level->anims[animIndex];
|
||||
|
||||
// smashes
|
||||
if (stand == STAND_GROUND) { // onGround
|
||||
if (d >= 256 * 4 && anim->state == STATE_RUN)
|
||||
setAnimation(ANIM_SMASH_RUN_LEFT); // TODO: RIGHT
|
||||
else
|
||||
setAnimation(ANIM_STAND);
|
||||
|
||||
velocity.x = velocity.z = 0.0f;
|
||||
} else if (stand == STAND_UNDERWATER) { // in water
|
||||
// do nothing
|
||||
//velocity.x = velocity.z = 0.0f;
|
||||
} else { // in the air
|
||||
setAnimation(ANIM_SMASH_JUMP);
|
||||
|
||||
velocity.x = -velocity.x * 0.5f;
|
||||
velocity.z = -velocity.z * 0.5f;
|
||||
velocity.y = 0.0f;
|
||||
}
|
||||
|
||||
} else {
|
||||
TR::Entity &entity = getEntity();
|
||||
entity.x = (int)pos.x;
|
||||
entity.y = (int)pos.y;
|
||||
entity.z = (int)pos.z;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
105
src/level.h
105
src/level.h
@ -4,10 +4,10 @@
|
||||
#include "core.h"
|
||||
#include "utils.h"
|
||||
#include "format.h"
|
||||
#include "controller.h"
|
||||
#include "lara.h"
|
||||
#include "enemy.h"
|
||||
#include "camera.h"
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include "debug.h"
|
||||
#endif
|
||||
@ -30,31 +30,68 @@ struct Level {
|
||||
float time;
|
||||
|
||||
Level(Stream &stream) : level{stream}, time(0.0f) {
|
||||
#ifdef _DEBUG
|
||||
Debug::init();
|
||||
#endif
|
||||
mesh = new MeshBuilder(level);
|
||||
|
||||
initAtlas();
|
||||
initShaders();
|
||||
initOverrides();
|
||||
|
||||
int entity = 0;
|
||||
for (int i = 0; i < level.entitiesCount; i++)
|
||||
if (level.entities[i].id == ENTITY_LARA) {
|
||||
entity = i;
|
||||
break;
|
||||
for (int i = 0; i < level.entitiesCount; i++) {
|
||||
TR::Entity &entity = level.entities[i];
|
||||
switch (entity.id) {
|
||||
case ENTITY_LARA :
|
||||
entity.controller = (lara = new Lara(&level, i));
|
||||
break;
|
||||
case ENTITY_ENEMY_WOLF :
|
||||
entity.controller = new Wolf(&level, i);
|
||||
break;
|
||||
case ENTITY_ENEMY_BEAR :
|
||||
entity.controller = new Bear(&level, i);
|
||||
break;
|
||||
case ENTITY_ENEMY_BAT :
|
||||
entity.controller = new Bat(&level, i);
|
||||
break;
|
||||
case ENTITY_ENEMY_TWIN :
|
||||
case ENTITY_ENEMY_CROCODILE_LAND :
|
||||
case ENTITY_ENEMY_CROCODILE_WATER :
|
||||
case ENTITY_ENEMY_LION_MALE :
|
||||
case ENTITY_ENEMY_LION_FEMALE :
|
||||
case ENTITY_ENEMY_PUMA :
|
||||
case ENTITY_ENEMY_GORILLA :
|
||||
case ENTITY_ENEMY_RAT_LAND :
|
||||
case ENTITY_ENEMY_RAT_WATER :
|
||||
case ENTITY_ENEMY_REX :
|
||||
case ENTITY_ENEMY_RAPTOR :
|
||||
case ENTITY_ENEMY_MUTANT :
|
||||
case ENTITY_ENEMY_CENTAUR :
|
||||
case ENTITY_ENEMY_MUMMY :
|
||||
case ENTITY_ENEMY_LARSON :
|
||||
entity.controller = new Enemy(&level, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lara = new Lara(&level, entity);
|
||||
camera = new Camera(&level, lara);
|
||||
ASSERT(lara != NULL);
|
||||
camera = new Camera(&level, lara);
|
||||
}
|
||||
|
||||
~Level() {
|
||||
#ifdef _DEBUG
|
||||
Debug::free();
|
||||
#endif
|
||||
for (int i = 0; i < level.entitiesCount; i++)
|
||||
delete (Controller*)level.entities[i].controller;
|
||||
|
||||
for (int i = 0; i < shMAX; i++)
|
||||
delete shaders[i];
|
||||
|
||||
delete atlas;
|
||||
delete mesh;
|
||||
|
||||
delete camera;
|
||||
delete lara;
|
||||
delete camera;
|
||||
}
|
||||
|
||||
void initAtlas() {
|
||||
@ -236,11 +273,6 @@ struct Level {
|
||||
renderRoom(p.roomIndex, roomIndex);
|
||||
}
|
||||
camera->frustum = camFrustum; // pop camera frustum
|
||||
|
||||
#ifdef _DEBUG
|
||||
glColor3f(0, 0.05, 0);
|
||||
camera->frustum->debug();
|
||||
#endif
|
||||
}
|
||||
|
||||
MeshBuilder::MeshInfo* getMeshInfoByOffset(uint32 meshOffset) {
|
||||
@ -250,16 +282,15 @@ struct Level {
|
||||
for (int i = 0; i < mesh->mCount; i++)
|
||||
if (mesh->meshInfo[i].offset == level.meshOffsets[meshOffset])
|
||||
return &mesh->meshInfo[i];
|
||||
|
||||
ASSERT(false);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void renderMesh(uint32 meshOffset) {
|
||||
MeshBuilder::MeshInfo *m = getMeshInfoByOffset(meshOffset);
|
||||
ASSERT(m != NULL);
|
||||
if (!m) return;
|
||||
if (!m) return; // invisible mesh (level.meshOffsets[meshOffset] == 0) camera target entity etc.
|
||||
|
||||
if ((m->radius & 0xFFFF) == 0 || camera->frustum->isVisible(Core::mModel * m->center, (m->radius & 0x3FF)) * 2) {
|
||||
if (!m->collider.radius || camera->frustum->isVisible(Core::mModel * m->center, m->collider.radius * 2)) {
|
||||
Core::active.shader->setParam(uModel, Core::mModel);
|
||||
mesh->renderMesh(m);
|
||||
}
|
||||
@ -295,14 +326,21 @@ struct Level {
|
||||
return ma.getRot().slerp(mb.getRot(), t).normal();
|
||||
}
|
||||
|
||||
void renderModel(const TR::Model &model, vec3 angle) {
|
||||
TR::Animation *anim = &level.anims[model.animation];
|
||||
void renderModel(const TR::Model &model, const TR::Entity &entity) {
|
||||
TR::Animation *anim;
|
||||
float fTime;
|
||||
vec3 angle;
|
||||
|
||||
float fTime = time;
|
||||
Controller *controller = (Controller*)entity.controller;
|
||||
|
||||
if (model.id == ENTITY_LARA) {
|
||||
fTime = lara->fTime;
|
||||
angle = lara->angle;
|
||||
if (controller) {
|
||||
angle = controller->angle;
|
||||
fTime = controller->animTime;
|
||||
anim = &level.anims[controller->animIndex];
|
||||
} else {
|
||||
anim = &level.anims[model.animation];
|
||||
angle = vec3(0.0f, entity.rotation / 16384.0f * PI * 0.5f, 0.0f);
|
||||
fTime = time;
|
||||
}
|
||||
|
||||
if (angle.y != 0.0f) Core::mModel.rotateY(angle.y);
|
||||
@ -405,9 +443,6 @@ struct Level {
|
||||
}
|
||||
|
||||
void renderEntity(const TR::Entity &entity) {
|
||||
// if (!(entity.flags & ENTITY_FLAG_VISIBLE))
|
||||
// return;
|
||||
|
||||
TR::Room &room = level.rooms[entity.room];
|
||||
if (!(room.flags & TR::ROOM_FLAG_VISIBLE)) // check for room visibility
|
||||
return;
|
||||
@ -431,7 +466,7 @@ struct Level {
|
||||
for (int i = 0; i < level.modelsCount; i++)
|
||||
if (entity.id == level.models[i].id) {
|
||||
isModel = true;
|
||||
renderModel(level.models[i], vec3(0, entity.rotation / 16384.0f * PI * 0.5f, 0));
|
||||
renderModel(level.models[i], entity);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -455,7 +490,13 @@ struct Level {
|
||||
|
||||
void update() {
|
||||
time += Core::deltaTime;
|
||||
lara->update();
|
||||
|
||||
for (int i = 0; i < level.entitiesCount; i++) {
|
||||
Controller *controller = (Controller*)level.entities[i].controller;
|
||||
if (controller)
|
||||
controller->update();
|
||||
}
|
||||
|
||||
camera->update();
|
||||
}
|
||||
|
||||
@ -511,7 +552,7 @@ struct Level {
|
||||
// Debug::Level::rooms(level, lara->pos, lara->getEntity().room);
|
||||
// Debug::Level::lights(level);
|
||||
// Debug::Level::portals(level);
|
||||
Debug::Level::meshes(level);
|
||||
// Debug::Level::meshes(level);
|
||||
Debug::end();
|
||||
#endif
|
||||
}
|
||||
|
40
src/mesh.h
40
src/mesh.h
@ -67,9 +67,9 @@ struct MeshBuilder {
|
||||
|
||||
// objects meshes
|
||||
struct MeshInfo : MeshRange {
|
||||
int offset;
|
||||
TR::Vertex center;
|
||||
int32 radius;
|
||||
int offset;
|
||||
TR::Vertex center;
|
||||
TR::Collider collider;
|
||||
} *meshInfo;
|
||||
int mCount;
|
||||
|
||||
@ -131,7 +131,7 @@ struct MeshBuilder {
|
||||
|
||||
mCount = 0;
|
||||
TR::Mesh *ptr = (TR::Mesh*)level.meshData;
|
||||
while ( ((int)ptr - (int)level.meshData) < level.meshDataSize * 2 ) {
|
||||
while ( ((intptr_t)ptr - (intptr_t)level.meshData) < level.meshDataSize * 2 ) {
|
||||
mCount++;
|
||||
|
||||
OFFSET(ptr->vCount * sizeof(TR::Vertex));
|
||||
@ -155,7 +155,7 @@ struct MeshBuilder {
|
||||
iCount += ptr->ctCount * 3;
|
||||
vCount += ptr->ctCount * 3;
|
||||
OFFSET(ptr->ctCount * sizeof(TR::Triangle) + sizeof(TR::Mesh));
|
||||
ptr = (TR::Mesh*)(((int)ptr + 3) & -4);
|
||||
ptr = (TR::Mesh*)(((intptr_t)ptr + 3) & -4);
|
||||
}
|
||||
meshInfo = new MeshInfo[mCount];
|
||||
|
||||
@ -229,15 +229,14 @@ struct MeshBuilder {
|
||||
}
|
||||
|
||||
// build objects geometry
|
||||
mCount = 0;
|
||||
ptr = (TR::Mesh*)level.meshData;
|
||||
while ( ((int)ptr - (int)level.meshData) < level.meshDataSize * sizeof(uint16) ) {
|
||||
MeshInfo &info = meshInfo[mCount++];
|
||||
info.offset = (int)ptr - (int)level.meshData;
|
||||
info.vStart = vCount;
|
||||
info.iStart = iCount;
|
||||
info.center = ptr->center;
|
||||
info.radius = ptr->radius;
|
||||
for (int i = 0; i < mCount; i++) {
|
||||
MeshInfo &info = meshInfo[i];
|
||||
info.offset = (intptr_t)ptr - (intptr_t)level.meshData;
|
||||
info.vStart = vCount;
|
||||
info.iStart = iCount;
|
||||
info.center = ptr->center;
|
||||
info.collider = ptr->collider;
|
||||
|
||||
TR::Vertex *mVertices = (TR::Vertex*)&ptr->vertices;
|
||||
|
||||
@ -326,7 +325,7 @@ struct MeshBuilder {
|
||||
} else {
|
||||
uint8 a = 255 - (lights[f.vertices[k]] >> 5);
|
||||
vertices[vCount].normal = { 0, 0, 0, 1 };
|
||||
vertices[vCount].color = { a, a, a, 255 }; // TODO: apply color
|
||||
vertices[vCount].color = { a, a, a, 255 };
|
||||
}
|
||||
vCount++;
|
||||
}
|
||||
@ -352,16 +351,15 @@ struct MeshBuilder {
|
||||
} else {
|
||||
uint8 a = 255 - (lights[f.vertices[k]] >> 5);
|
||||
vertices[vCount].normal = { 0, 0, 0, 1 };
|
||||
vertices[vCount].color = { a, a, a, 255 }; // TODO: apply color
|
||||
vertices[vCount].color = { a, a, a, 255 };
|
||||
}
|
||||
vCount++;
|
||||
}
|
||||
}
|
||||
OFFSET(ptr->ctCount * sizeof(TR::Triangle) + sizeof(TR::Mesh));
|
||||
|
||||
ptr = (TR::Mesh*)(((int)ptr + 3) & -4);
|
||||
|
||||
info.iCount = iCount - info.iStart;
|
||||
|
||||
OFFSET(ptr->ctCount * sizeof(TR::Triangle) + sizeof(TR::Mesh));
|
||||
ptr = (TR::Mesh*)(((intptr_t)ptr + 3) & -4);
|
||||
}
|
||||
|
||||
// build sprite sequences
|
||||
@ -388,8 +386,8 @@ struct MeshBuilder {
|
||||
int tile = tex.tile.index;
|
||||
int tx = (tile % 4) * 256;
|
||||
int ty = (tile / 4) * 256;
|
||||
return vec2( ((tx + tex.vertices[0].Xpixel) << 5) + 16,
|
||||
((ty + tex.vertices[0].Ypixel) << 5) + 16 );
|
||||
return vec2( (float)(((tx + tex.vertices[0].Xpixel) << 5) + 16),
|
||||
(float)(((ty + tex.vertices[0].Ypixel) << 5) + 16) );
|
||||
}
|
||||
|
||||
void initAnimTextures(TR::Level &level) {
|
||||
|
@ -100,8 +100,10 @@
|
||||
<ClInclude Include="..\controller.h" />
|
||||
<ClInclude Include="..\core.h" />
|
||||
<ClInclude Include="..\debug.h" />
|
||||
<ClInclude Include="..\enemy.h" />
|
||||
<ClInclude Include="..\game.h" />
|
||||
<ClInclude Include="..\input.h" />
|
||||
<ClInclude Include="..\lara.h" />
|
||||
<ClInclude Include="..\level.h" />
|
||||
<ClInclude Include="..\mesh.h" />
|
||||
<ClInclude Include="..\shader.h" />
|
||||
|
@ -174,6 +174,8 @@ void freeGL(HGLRC hRC) {
|
||||
wglDeleteContext(hRC);
|
||||
}
|
||||
|
||||
HDC hDC;
|
||||
|
||||
int main() {
|
||||
#ifdef _DEBUG
|
||||
_CrtMemState _ms;
|
||||
@ -188,7 +190,7 @@ int main() {
|
||||
|
||||
joyInit();
|
||||
|
||||
HDC hDC = GetDC(hWnd);
|
||||
hDC = GetDC(hWnd);
|
||||
HGLRC hRC = initGL(hDC);
|
||||
Game::init();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user