1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-03-13 23:59:41 +01:00

#3 climb & hang states fixes, water splash, item pickup, slow motion mode for debug (R key)

This commit is contained in:
XProger 2016-10-16 20:32:31 +03:00
parent c22ffa886f
commit dddcbd13c5
9 changed files with 531 additions and 330 deletions

Binary file not shown.

View File

@ -7,7 +7,7 @@
#define MAX_CLIP_PLANES 10
#define CAMERA_OFFSET (1024.0f + 512.0f)
#define CAMERA_OFFSET (1024.0f + 256.0f)
struct Frustum {
@ -198,7 +198,7 @@ struct Camera : Controller {
activateNext();
return true;
}
virtual void update() {
if (timer > 0.0f) {
timer -= Core::deltaTime;
@ -210,13 +210,13 @@ struct Camera : Controller {
}
}
#ifdef FREE_CAMERA
vec3 dir = vec3(sinf(angle.y - PI) * cosf(-angle.x), -sinf(-angle.x), cosf(angle.y - PI) * cosf(-angle.x));
vec3 d = vec3(sinf(angle.y - PI) * cosf(-angle.x), -sinf(-angle.x), cosf(angle.y - PI) * cosf(-angle.x));
vec3 v = vec3(0);
if (Input::down[ikW]) v = v + dir;
if (Input::down[ikS]) v = v - dir;
if (Input::down[ikD]) v = v + dir.cross(vec3(0, 1, 0));
if (Input::down[ikA]) v = v - dir.cross(vec3(0, 1, 0));
if (Input::down[ikUp]) v = v + d;
if (Input::down[ikDown]) v = v - d;
if (Input::down[ikRight]) v = v + d.cross(vec3(0, 1, 0));
if (Input::down[ikLeft]) v = v - d.cross(vec3(0, 1, 0));
pos = pos + v.normal() * (Core::deltaTime * 2048.0f);
#endif
if (Input::down[ikMouseR]) {
@ -274,7 +274,7 @@ struct Camera : Controller {
if (info.roomNext != 255)
room = info.roomNext;
if (destPos.y < info.ceiling) {
if (pos.y < info.ceiling) {
if (info.roomAbove != 255)
room = info.roomAbove;
else
@ -300,17 +300,21 @@ struct Camera : Controller {
float dist = dir.length();
dir = dir * (1.0f / dist);
int lx = -1, lz = -1;
int lr = -1, lx = -1, lz = -1;
TR::Level::FloorInfo info;
while (dist > 1.0f) {
int sx = px / 1024 * 1024 + 512,
sz = pz / 1024 * 1024 + 512;
if (lx != sx || lz != sz) {
if (lr != room || lx != sx || lz != sz) {
level->getFloorInfo(room, sx, sz, info);
if (info.roomNext != 0xFF) {
room = info.roomNext;
level->getFloorInfo(room, sx, sz, info);
}
lr = room;
lx = sx;
lz = sz;
if (info.roomNext != 0xFF) room = info.roomNext;
}
if (py > info.floor && info.roomBelow != 0xFF)

View File

@ -98,10 +98,10 @@ struct Controller {
return getEntity().room;
}
int setAnimation(int index, int frame = -1) {
int setAnimation(int index, int frame = 0) {
animIndex = index;
TR::Animation &anim = level->anims[animIndex];
animTime = frame == -1 ? 0.0f : ((frame - anim.frameStart) / 30.0f);
animTime = (frame <= 0 ? -frame : (frame - anim.frameStart)) / 30.0f;
ASSERT(anim.frameStart <= anim.frameEnd);
animPrevFrame = int(animTime * 30.0f) - 1;
return state = anim.state;
@ -134,26 +134,25 @@ struct Controller {
return exists;
}
int getOverlap(int fromX, int fromY, int fromZ, int toX, int toZ, int &delta) const {
int getOverlap(int fromX, int fromY, int fromZ, int toX, int toZ) const {
int dx, dz;
TR::Room::Sector &s = level->getSector(getEntity().room, fromX, fromZ, dx, dz);
if (s.boxIndex == 0xFFFF) return NO_OVERLAP;
if (s.boxIndex == 0xFFFF)
return NO_OVERLAP;
TR::Box &b = level->boxes[s.boxIndex];
if (b.contains(toX, toZ)) {
delta = 0;
return b.floor;
}
if (b.contains(toX, toZ))
return 0;
int floor = NO_OVERLAP;
delta = floor;
int delta = NO_OVERLAP;
TR::Overlap *o = &level->overlaps[b.overlap & 0x7FFF];
do {
TR::Box &ob = level->boxes[o->boxIndex];
if (ob.contains(toX, toZ)) { // get min delta
int d = abs(ob.floor - fromY);
int d = abs(b.floor - ob.floor);
if (d < delta) {
floor = ob.floor;
delta = d;
@ -161,8 +160,10 @@ struct Controller {
}
} while (!(o++)->end);
delta = floor - b.floor;
return floor;
if (floor == NO_OVERLAP)
return NO_OVERLAP;
return b.floor - floor;
}
void playSound(int id) const {
@ -208,20 +209,23 @@ struct Controller {
if (entity.y > info.floor) {
if (info.roomBelow == 0xFF) {
entity.y = info.floor;
pos.y = entity.y;
velocity.y = 0.0f;
if (entity.y > info.floor) {
entity.y = info.floor;
pos.y = entity.y;
velocity.y = 0.0f;
}
} else
entity.room = info.roomBelow;
}
int height = getHeight();
if (entity.y - getHeight() < info.ceiling) {
if (entity.y - height < info.ceiling) {
if (info.roomAbove == 0xFF) {
pos.y = entity.y = info.ceiling + height;
velocity.y = fabsf(velocity.y);
if (velocity.y < 0.0f)
velocity.y = GRAVITY;
} else {
if (stand == STAND_UNDERWATER && !(level->rooms[info.roomAbove].flags & TR::ROOM_FLAG_WATER)) {
if (stand == STAND_UNDERWATER && !level->rooms[info.roomAbove].flags.water) {
stand = STAND_ONWATER;
velocity.y = 0;
pos.y = info.ceiling;
@ -277,19 +281,21 @@ struct Controller {
}
virtual bool activate(ActionCommand *cmd) { actionCommand = cmd; return true; }
virtual void doCustomCommand (int curFrame, int prevFrame) {}
virtual void updateVelocity() {}
virtual void move() {}
virtual Stand getStand() { return STAND_AIR; }
virtual int getHeight() { return 0; }
virtual int getStateAir() { return state; }
virtual int getStateGround() { return state; }
virtual int getStateSlide() { return state; }
virtual int getStateHang() { 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 void checkRoom() {}
virtual void move() {}
virtual Stand getStand() { return STAND_AIR; }
virtual int getHeight() { return 0; }
virtual int getStateAir() { return state; }
virtual int getStateGround() { return state; }
virtual int getStateSlide() { return state; }
virtual int getStateHang() { 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];
@ -324,8 +330,8 @@ struct Controller {
}
virtual void updateEnd() {
TR::Entity &e = getEntity();
move();
collide();
updateEntity();
}
@ -374,6 +380,7 @@ struct Controller {
if (endFrame) {
pos = pos + vec3(sx, sy, sz).rotateY(-angle.y);
updateEntity();
checkRoom();
LOG("move: %d %d %d\n", (int)sx, (int)sy, (int)sz);
}
break;
@ -403,8 +410,9 @@ struct Controller {
if (idx > animPrevFrame && idx <= frameIndex) {
if (cmd == TR::ANIM_CMD_EFFECT) {
switch (id) {
case TR::EFFECT_ROTATE_180 : angle.y = angle.y + PI; break;
case TR::EFFECT_LARA_BUBBLES : playSound(TR::SND_BUBBLE); break;
case TR::EFFECT_ROTATE_180 : angle.y = angle.y + PI; break;
case TR::EFFECT_LARA_BUBBLES : if (rand() % 10 > 6) playSound(TR::SND_BUBBLE); break;
case TR::EFFECT_LARA_HANDSFREE : break;
default : LOG("unknown special cmd %d (anim %d)\n", id, animIndex);
}
} else
@ -416,6 +424,8 @@ struct Controller {
}
}
doCustomCommand(frameIndex, animPrevFrame);
if (endFrame) { // if animation is end - switch to next
setAnimation(anim->nextAnimation, anim->nextFrame);
activateNext();
@ -479,4 +489,12 @@ struct SpriteController : Controller {
}
};
void addSprite(TR::Level *level, TR::Entity::Type type, int room, int x, int y, int z, int frame = -1) {
int index = level->entityAdd(type, room, x, y, z, 0, -1);
if (index > -1) {
level->entities[index].intensity = 0x1FFF - level->rooms[room].ambient;
level->entities[index].controller = new SpriteController(level, index, true, frame);
}
}
#endif

View File

@ -184,58 +184,35 @@ namespace Debug {
namespace Level {
void debugFloor(const TR::Level &level, const vec3 &f, const vec3 &c, int floorIndex, int boxIndex, bool current) {
if (floorIndex == 0) return;
void debugFloor(const TR::Level &level, int roomIndex, int x, int z) {
TR::Level::FloorInfo info;
level.getFloorInfo(roomIndex, x, z, info);
vec3 f = vec3(x, info.floor, z);
vec3 c = vec3(x, info.ceiling, z);
vec3 vf[4] = { f, f + vec3(1024, 0, 0), f + vec3(1024, 0, 1024), f + vec3(0, 0, 1024) };
vec3 vc[4] = { c, c + vec3(1024, 0, 0), c + vec3(1024, 0, 1024), c + vec3(0, 0, 1024) };
if (current)
glColor3f(1, 1, 1);
else
glColor3f(0, 1, 0);
bool isPortal = false;
float fx = 0.0f, fz = 0.0f;
TR::FloorData *fd = &level.floors[floorIndex];
TR::FloorData::Command cmd;
do {
cmd = (*fd++).cmd;
switch (cmd.func) {
case TR::FloorData::PORTAL :
isPortal = true;
fd++;
break; // portal
case TR::FloorData::FLOOR : // floor & ceiling
case TR::FloorData::CEILING : {
TR::FloorData::Slant slant = (*fd++).slant;
int sx = 256 * (int)slant.x;
int sz = 256 * (int)slant.z;
auto &p = cmd.func == 0x02 ? vf : vc;
if (cmd.func == TR::FloorData::FLOOR) { // floor
fx = (float)slant.x;
fz = (float)slant.z;
int sx = 256 * info.slantX;
int sz = 256 * info.slantZ;
if (sx > 0) {
p[0].y += sx;
p[3].y += sx;
} else {
p[1].y -= sx;
p[2].y -= sx;
}
if (sx > 0) {
vf[0].y += sx;
vf[3].y += sx;
} else {
vf[1].y -= sx;
vf[2].y -= sx;
}
if (sz > 0) {
p[0].y += sz;
p[1].y += sz;
} else {
p[3].y -= sz;
p[2].y -= sz;
}
if (sz > 0) {
vf[0].y += sz;
vf[1].y += sz;
} else {
vf[3].y -= sz;
vf[2].y -= sz;
}
/*
} else { // ceiling
if (sx < 0) {
p[0].y += sx;
@ -253,22 +230,7 @@ namespace Debug {
p[2].y += sz;
}
}
break;
}
case TR::FloorData::TRIGGER : {
TR::FloorData::TriggerInfo info = (*fd++).triggerInfo;
TR::FloorData::TriggerCommand trigCmd;
glColor3f(1, 0, 1);
do {
trigCmd = (*fd++).triggerCmd; // trigger action
} while (!trigCmd.end);
break;
}
default :
if (!cmd.end)
LOG("unknown func %d : %d\n", cmd.func, cmd.sub);
}
} while (!cmd.end);
*/
glBegin(GL_LINE_STRIP);
for (int i = 0; i < 5; i++)
@ -281,7 +243,7 @@ namespace Debug {
glVertex3fv((GLfloat*)&vc[i % 4]);
glEnd();
if (isPortal) {
if (info.roomNext != 0xFF) {
glColor4f(0.0f, 0.0f, 1.0f, 0.5f);
glBegin(GL_QUADS);
for (int i = 3; i >= 0; i--)
@ -289,31 +251,26 @@ namespace Debug {
glEnd();
}
if (info.roomAbove != 0xFF) {
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
glBegin(GL_QUADS);
for (int i = 3; i >= 0; i--) {
vec3 v = vf[i];
v.y -= 32.0f;
glVertex3fv((GLfloat*)&v);
}
glEnd();
}
/*
if (boxIndex == 0xFFFF) {
glBegin(GL_LINES);
float x = f.x + 512.0f, z = f.z + 512.0f;
glVertex3f(x, f.y, z);
glVertex3f(x, c.y, z);
glEnd();
}
vec3 a = (vf[0] + vf[1] + vf[2] + vf[3]) * 0.25f;
vec3 n = vec3(-fx, -4.0f, -fz).normal();
vec3 b = a + n * 1.0f;
vec3 v = ((Controller*)level.entities[0].controller)->getDir();
vec3 p = v - n * n.dot(v);
vec3 d = b + p.normal() * 256.0f;
glBegin(GL_LINES);
glColor3f(1, 1, 1);
glVertex3fv((GLfloat*)&a);
glVertex3fv((GLfloat*)&b);
glColor3f(1, 1, 0);
glVertex3fv((GLfloat*)&b);
glVertex3fv((GLfloat*)&d);
glEnd();
}*/
}
void debugBox(const TR::Box &b) {
@ -340,6 +297,7 @@ namespace Debug {
vec3 p = (pos - vec3(room.info.x, 0, room.info.z)) / vec3(1024, 1, 1024);
glDisable(GL_DEPTH_TEST);
for (int z = 0; z < room.zSectors; z++)
for (int x = 0; x < room.xSectors; x++) {
TR::Room::Sector &s = room.sectors[x * room.zSectors + z];
@ -354,18 +312,17 @@ namespace Debug {
vec3 c(x * 1024 + room.info.x, s.ceiling * 256 + 1, z * 1024 + room.info.z);
bool current = (int)p.x == x && (int)p.z == z;
debugFloor(level, f, c, s.floorIndex, s.boxIndex, current);
/*
debugFloor(level, roomIndex, room.info.x + x * 1024, room.info.z + z * 1024);
if (current && s.boxIndex != 0xFFFF && level.boxes[s.boxIndex].overlap != 0xFFFF) {
glDisable(GL_DEPTH_TEST);
glColor4f(0.0f, 1.0f, 0.0f, 0.25f);
debugBox(level.boxes[s.boxIndex]);
glColor4f(1.0f, 1.0f, 0.0f, 0.25f);
debugOverlaps(level, s.boxIndex);
glEnable(GL_DEPTH_TEST);
}
*/
}
glEnable(GL_DEPTH_TEST);
}
void rooms(const TR::Level &level, const vec3 &pos, int roomIndex) {
@ -395,6 +352,7 @@ namespace Debug {
glBegin(GL_QUADS);
for (int i = 0; i < level.roomsCount; i++) {
// if (level.entities[91].room != i) continue;
TR::Room &r = level.rooms[i];
for (int j = 0; j < r.portalsCount; j++) {
TR::Room::Portal &p = r.portals[j];
@ -415,7 +373,7 @@ namespace Debug {
TR::Entity &e = level.entities[i];
char buf[255];
sprintf(buf, "%d", (int)e.id);
sprintf(buf, "%d", (int)e.type);
Debug::Draw::text(vec3(e.x, e.y, e.z), vec4(0.8, 0.0, 0.0, 1), buf);
}
}
@ -491,7 +449,7 @@ namespace Debug {
if (!node) continue; // ???
if (e.id == m.id) {
if (e.type == m.type) {
ASSERT(m.animation < 0xFFFF);
int fSize = sizeof(TR::AnimFrame) + m.mCount * sizeof(uint16) * 2;
@ -554,11 +512,11 @@ namespace Debug {
}
}
void info(const TR::Level &level, const TR::Entity &entity) {
void info(const TR::Level &level, const TR::Entity &entity, int state, int anim, int frame) {
char buf[255];
sprintf(buf, "DIP = %d, TRI = %d, SND = %d", Core::stats.dips, Core::stats.tris, Sound::channelsCount);
Debug::Draw::text(vec2(16, 16), vec4(1.0f), buf);
sprintf(buf, "pos = (%d, %d, %d), room = %d", entity.x, entity.y, entity.z, entity.room);
sprintf(buf, "pos = (%d, %d, %d), room = %d, state = %d, anim = %d, frame = %d", entity.x, entity.y, entity.z, entity.room, state, anim, frame);
Debug::Draw::text(vec2(16, 32), vec4(1.0f), buf);
TR::Level::FloorInfo info;

View File

@ -10,11 +10,6 @@
namespace TR {
enum : int32 {
ROOM_FLAG_WATER = 0x0001,
ROOM_FLAG_VISIBLE = 0x8000
};
enum {
ANIM_CMD_NONE ,
ANIM_CMD_MOVE ,
@ -100,10 +95,6 @@ namespace TR {
CUTSCENE , // play cutscene
};
#define ENTITY_FLAG_CLEAR 0x0080
#define ENTITY_FLAG_VISIBLE 0x0100
#define ENTITY_FLAG_ACTIVE 0x3E00
#pragma pack(push, 1)
struct fixed {
@ -186,7 +177,9 @@ namespace TR {
uint16 lightsCount;
uint16 meshesCount;
int16 alternateRoom;
int16 flags;
struct {
uint16 water:1, unused:14, rendered:1;
} flags;
struct Portal {
uint16 roomIndex;
@ -215,7 +208,9 @@ namespace TR {
angle rotation;
uint16 intensity;
uint16 meshID;
uint16 flags; // ! not exists in file !
struct { // ! not exists in file !
uint16 unused:15, rendered:1;
} flags;
} *meshes;
};
@ -287,20 +282,8 @@ namespace TR {
};
struct Entity {
int16 id; // Object Identifier (matched in Models[], or SpriteSequences[], as appropriate)
int16 room; // which room contains this item
int32 x, y, z; // world coords
angle rotation; // ((0xc000 >> 14) * 90) degrees
int16 intensity; // (constant lighting; -1 means use mesh lighting)
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)
// not exists in file
uint16 align;
int16 modelIndex; // index of representation in models (index + 1) or spriteSequences (-(index + 1)) arrays
void *controller; // Controller implementation or NULL
enum {
enum Type : int16 {
NONE = -1,
LARA = 0,
ENEMY_TWIN = 6,
@ -353,11 +336,24 @@ namespace TR {
AMMO_SHOTGUN = 89, // sprite
AMMO_MAGNUMS = 90, // sprite
AMMO_UZIS = 91, // sprite
MEDIKIT_SMALL = 93, // sprite
MEDIKIT_BIG = 94, // sprite
PUZZLE_1 = 110, // sprite
PUZZLE_2 = 111, // sprite
PUZZLE_3 = 112, // sprite
PUZZLE_4 = 113, // sprite
HOLE_PUZZLE = 118,
PICKUP = 126, // sprite
KEY_1 = 129, // sprite
KEY_2 = 130, // sprite
KEY_3 = 131, // sprite
KEY_4 = 132, // sprite
HOLE_KEY = 137,
ARTIFACT = 143, // sprite
@ -375,7 +371,19 @@ namespace TR {
VIEW_TARGET = 169,
GLYPH = 190, // sprite
};
} type;
int16 room;
int32 x, y, z;
angle rotation;
int16 intensity;
union {
struct { uint16 unused:7, clear:1, invisible:1, active:5, unused2:1, rendered:1; };
uint16 value;
} flags;
// not exists in file
uint16 align;
int16 modelIndex; // index of representation in models (index + 1) or spriteSequences (-(index + 1)) arrays
void *controller; // Controller implementation or NULL
};
struct Animation {
@ -447,25 +455,27 @@ namespace TR {
};
struct Model {
uint32 id; // Item Identifier (matched in Entities[])
uint16 mCount; // number of meshes in this object
uint16 mStart; // stating mesh (offset into MeshPointers[])
uint32 node; // offset into MeshTree[]
uint32 frame; // byte offset into Frames[] (divide by 2 for Frames[i])
uint16 animation; // offset into Animations[]
uint16 align; // ! not exists in file !
Entity::Type type;
uint16 unused;
uint16 mCount;
uint16 mStart;
uint32 node;
uint32 frame;
uint16 animation;
uint16 align;
};
struct StaticMesh {
uint32 id; // Static Mesh Identifier
uint16 mesh; // Mesh (offset into MeshPointers[])
MinMax box[2]; // visible (minX, maxX, minY, maxY, minZ, maxZ) & collision
uint32 id;
uint16 mesh;
MinMax vbox;
MinMax cbox;
uint16 flags;
void getBox(bool collision, angle rotation, vec3 &min, vec3 &max) {
int k = rotation.value / 0x4000;
MinMax &m = box[collision];
MinMax &m = collision ? cbox : vbox;
ASSERT(m.minX <= m.maxX && m.minY <= m.maxY && m.minZ <= m.maxZ);
@ -516,9 +526,10 @@ namespace TR {
};
struct SpriteSequence {
int32 id; // Sprite identifier
int16 sCount; // Negative of ``how many sprites are in this sequence''
int16 sStart; // Where (in sprite texture list) this sequence starts
Entity::Type type;
uint16 unused;
int16 sCount;
int16 sStart;
};
struct Camera {
@ -800,10 +811,10 @@ namespace TR {
stream.raw(&e, sizeof(e) - sizeof(e.align) - sizeof(e.controller) - sizeof(e.modelIndex));
e.align = 0;
e.controller = NULL;
e.modelIndex = getModelIndex(e.id);
e.modelIndex = getModelIndex(e.type);
}
for (int i = entitiesBaseCount; i < entitiesCount; i++) {
entities[i].id = -1;
entities[i].type = Entity::NONE;
entities[i].controller = NULL;
}
// palette
@ -889,33 +900,33 @@ namespace TR {
return NULL;
}
int16 getModelIndex(int16 id) const {
int16 getModelIndex(Entity::Type type) const {
for (int i = 0; i < modelsCount; i++)
if (id == models[i].id)
if (type == models[i].type)
return i + 1;
for (int i = 0; i < spriteSequencesCount; i++)
if (id == spriteSequences[i].id)
if (type == spriteSequences[i].type)
return -(i + 1);
ASSERT(false);
return 0;
}
int entityAdd(int16 id, int16 room, int32 x, int32 y, int32 z, angle rotation, int16 intensity) {
int entityAdd(TR::Entity::Type type, int16 room, int32 x, int32 y, int32 z, angle rotation, int16 intensity) {
int entityIndex = -1;
for (int i = entitiesBaseCount; i < entitiesCount; i++)
if (entities[i].id == -1) {
if (entities[i].type == Entity::NONE) {
Entity &e = entities[i];
e.id = id;
e.type = type;
e.room = room;
e.x = x;
e.y = y;
e.z = z;
e.rotation = rotation;
e.intensity = intensity;
e.flags = 0;
e.modelIndex = getModelIndex(e.id);
e.flags.value = 0;
e.modelIndex = getModelIndex(e.type);
e.controller = NULL;
return i;
}
@ -923,7 +934,7 @@ namespace TR {
}
void entityRemove(int entityIndex) {
entities[entityIndex].id = -1;
entities[entityIndex].type = Entity::NONE;
entities[entityIndex].controller = NULL;
}
@ -945,7 +956,7 @@ namespace TR {
return room.sectors[sx * room.zSectors + sz];
}
void getFloorInfo(int roomIndex, int x, int z, FloorInfo &info) const {
void getFloorInfo(int roomIndex, int x, int z, FloorInfo &info, bool actual = false, int prevRoom = 0xFF) const {
int dx, dz;
Room::Sector &s = getSector(roomIndex, x, z, dx, dz);
@ -961,6 +972,20 @@ namespace TR {
info.trigger = Trigger::ACTIVATE;
info.trigCmdCount = 0;
if (actual) {
if (info.roomBelow != 0xFF && info.roomBelow != prevRoom) {
FloorInfo tmp;
getFloorInfo(info.roomBelow, x, z, tmp, true, roomIndex);
info.floor = tmp.floor;
}
if (info.roomAbove != 0xFF && info.roomAbove != prevRoom) {
FloorInfo tmp;
getFloorInfo(info.roomAbove, x, z, tmp, true, roomIndex);
info.ceiling = tmp.ceiling;
}
}
if (!s.floorIndex) return;
FloorData *fd = &floors[s.floorIndex];
@ -1012,6 +1037,9 @@ namespace TR {
}
} while (!cmd.end);
if (actual && info.roomNext != 0xFF)
getFloorInfo(info.roomNext, x, z, info, actual, prevRoom);
}
}; // struct Level

View File

@ -14,6 +14,10 @@
#define TURN_WATER_SLOW PI * 2.0f / 3.0f
#define GLIDE_SPEED 50.0f
#define LARA_HANG_OFFSET 735
#define PICKUP_FRAME_GROUND 40
#define PICKUP_FRAME_UNDERWATER 16
#define MAX_TRIGGER_ACTIONS 64
@ -22,6 +26,7 @@
struct Lara : Controller {
ActionCommand actionList[MAX_TRIGGER_ACTIONS];
int lastPickUp;
// http://www.tombraiderforums.com/showthread.php?t=148859
enum {
@ -29,7 +34,8 @@ struct Lara : Controller {
ANIM_CLIMB_JUMP = 26,
ANIM_HANG_UP = 29,
ANIM_HANG_FALL = 28,
ANIM_HANG_WALL = 29,
ANIM_FALL = 34,
ANIM_SMASH_JUMP = 32,
@ -51,7 +57,7 @@ struct Lara : Controller {
ANIM_SLIDE_FORTH = 70,
ANIM_HANG_FORTH = 96,
ANIM_HANG = 96,
ANIM_STAND_NORMAL = 103,
@ -66,6 +72,8 @@ struct Lara : Controller {
ANIM_HIT_RIGHT = 128,
ANIM_STAND_ROLL_BEGIN = 146,
ANIM_STAND_ROLL_END = 147,
ANIM_HANG_NOWALL = 150,
};
// http://www.tombraiderforums.com/showthread.php?t=211681
@ -93,7 +101,7 @@ struct Lara : Controller {
STATE_FAST_TURN,
STATE_STEP_RIGHT,
STATE_STEP_LEFT,
STATE_ROLL,
STATE_ROLL_1,
STATE_SLIDE,
STATE_BACK_JUMP,
STATE_RIGHT_JUMP,
@ -115,7 +123,7 @@ struct Lara : Controller {
STATE_USE_KEY,
STATE_USE_PUZZLE,
STATE_UNDERWATER_DEATH,
STATE_ROLL_45,
STATE_ROLL_2,
STATE_SPECIAL,
STATE_SURF_BACK,
STATE_SURF_LEFT,
@ -129,18 +137,38 @@ struct Lara : Controller {
STATE_MAX };
Lara(TR::Level *level, int entity) : Controller(level, entity) {
#ifdef _DEBUG
/*
// gym
pos = vec3(43182, 2473, 51556);
angle = vec3(0.0f, PI * 0.5f, 0.0f);
getEntity().room = 12;
*/
// 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 2 (room 1)
pos = vec3(31400, -2560, 25200);
angle = vec3(0.0f, PI, 0.0f);
getEntity().room = 43;
// level 2 (medikit)
pos = vec3(30800, -7936, 22131);
angle = vec3(0.0f, 0.0f, 0.0f);
getEntity().room = 58;
*/
/*
// level 3a
pos = vec3(41015, 3584, 34494);
@ -153,7 +181,8 @@ struct Lara : Controller {
angle = vec3(0.0f, PI, 0.0f);
getEntity().room = 14;
*/
// updateEntity();
updateEntity();
#endif
}
bool waterOut(int &outState) {
@ -164,7 +193,9 @@ struct Lara : Controller {
level->getFloorInfo(getEntity().room, (int)pos.x, (int)pos.z, infoCur),
level->getFloorInfo(infoCur.roomAbove, (int)dst.x, (int)dst.z, infoDst);
if (infoDst.roomBelow == 0xFF && pos.y - infoDst.floor <= 256) { // possibility check
int h = int(pos.y - infoDst.floor);
if (h > 0 && h <= 256) { // possibility check
if (!setState(STATE_STOP)) { // can't set water out state
outState = STATE_STOP;
return true;
@ -183,7 +214,55 @@ struct Lara : Controller {
return false;
}
void performTrigger() {
bool doPickUp() {
int room = getRoomIndex();
TR::Entity &e = getEntity();
for (int i = 0; i < level->entitiesCount; i++) {
TR::Entity &item = level->entities[i];
if (item.room == room && !item.flags.invisible) {
if (abs(item.x - e.x) > 256 || abs(item.z - e.z) > 256)
continue;
switch (item.type) {
case TR::Entity::WEAPON_PISTOLS :
case TR::Entity::WEAPON_SHOTGUN :
case TR::Entity::WEAPON_MAGNUMS :
case TR::Entity::WEAPON_UZIS :
case TR::Entity::AMMO_SHOTGUN :
case TR::Entity::AMMO_MAGNUMS :
case TR::Entity::AMMO_UZIS :
case TR::Entity::MEDIKIT_SMALL :
case TR::Entity::MEDIKIT_BIG :
case TR::Entity::PUZZLE_1 :
case TR::Entity::PUZZLE_2 :
case TR::Entity::PUZZLE_3 :
case TR::Entity::PUZZLE_4 :
case TR::Entity::PICKUP :
case TR::Entity::KEY_1 :
case TR::Entity::KEY_2 :
case TR::Entity::KEY_3 :
case TR::Entity::KEY_4 :
case TR::Entity::ARTIFACT :
lastPickUp = i;
angle.x = 0.0f;
pos.x = item.x;
pos.y = item.y;
pos.z = item.z;
if (stand == STAND_UNDERWATER) { // TODO: lerp to pos/angle
pos = pos - getDir() * 256.0f;
pos.y -= 256;
}
updateEntity();
return true;
default : ;
}
}
}
return false;
}
void doTrigger() {
if (actionCommand) return;
TR::Entity &e = getEntity();
@ -191,8 +270,8 @@ struct Lara : Controller {
level->getFloorInfo(e.room, e.x, e.z, info);
if (!info.trigCmdCount) return; // has no trigger
bool isActive = (level->entities[info.trigCmd[0].args].flags & ENTITY_FLAG_ACTIVE);
if (info.trigInfo.once == 1 && (level->entities[info.trigCmd[0].args].flags & ENTITY_FLAG_ACTIVE)) return; // once trigger is already activated
bool isActive = (level->entities[info.trigCmd[0].args].flags.active);
if (info.trigInfo.once == 1 && isActive) return; // once trigger is already activated
int actionState = state;
switch (info.trigger) {
@ -243,7 +322,7 @@ struct Lara : Controller {
Controller *controller = this;
for (int i = 0; i < info.trigCmdCount; i++) {
if (!controller) {
LOG("! next activation entity %d has no controller\n", level->entities[info.trigCmd[i].args].id);
LOG("! next activation entity %d has no controller\n", level->entities[info.trigCmd[i].args].type);
playSound(TR::SND_NO);
return;
}
@ -263,7 +342,6 @@ struct Lara : Controller {
actionItem++;
}
LOG("perform\n");
actionList[0].next = &actionList[1];
actionCommand = &actionList[0];
@ -316,31 +394,52 @@ struct Lara : Controller {
}
virtual Stand getStand() {
if (stand == STAND_HANG && (mask & ACTION))
return stand;
if (state == STATE_HANG || state == STATE_HANG_LEFT || state == STATE_HANG_RIGHT) {
if (mask & ACTION)
return STAND_HANG;
setAnimation(ANIM_HANG_FALL);
velocity = vec3(0.0f);
pos.y += 128.0f;
updateEntity();
return STAND_AIR;
}
if (state == STATE_HANDSTAND || state == STATE_HANG_UP)
return STAND_HANG;
if (stand == STAND_ONWATER && state != STATE_DIVE && state != STATE_STOP)
return stand;
if (getRoom().flags & TR::ROOM_FLAG_WATER)
if (getRoom().flags.water)
return STAND_UNDERWATER; // TODO: ONWATER
TR::Entity &e = getEntity();
TR::Level::FloorInfo info;
level->getFloorInfo(e.room, e.x, e.z, info);
level->getFloorInfo(e.room, e.x, e.z, info, true);
if (stand == STAND_SLIDE || (stand == STAND_AIR && velocity.y > 0) || stand == STAND_GROUND) {
if (e.y + 8 >= info.floor && (abs(info.slantX) > 2 || abs(info.slantZ) > 2)) {
if (stand == STAND_AIR)
playSound(TR::SND_LANDING);
pos.y = info.floor;
updateEntity();
if (stand == STAND_GROUND || stand == STAND_AIR)
slideStart();
return STAND_SLIDE;
}
}
int extra = stand != STAND_AIR ? 256 : 0;
if (stand != STAND_HANG && info.roomBelow == 0xFF && e.y + extra >= info.floor)
if (e.y + extra >= info.floor) {
if (stand != STAND_GROUND) {
pos.y = info.floor;
updateEntity();
}
return STAND_GROUND;
}
return STAND_AIR;
}
@ -354,18 +453,37 @@ struct Lara : Controller {
virtual int getStateAir() {
angle.x = 0.0f;
if (state == STATE_REACH && getDir().dot(vec3(velocity.x, 0.0f, velocity.z)) < 0)
velocity.x = velocity.z = 0.0f;
if ((state == STATE_REACH || state == STATE_UP_JUMP) && (mask & ACTION)) {
if (state == STATE_REACH && velocity.y < 0.0f)
return state;
vec3 p = pos + getDir() * 128.0f;
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.z, info);
info.roomAbove = getRoomIndex();
level->getFloorInfo(info.roomAbove, (int)pos.x, (int)pos.z, info);
if (info.roomAbove == 0xFF)
info.roomAbove = getRoomIndex();
if (abs(int(info.floor - (p.y - 768.0f + 64.0f))) < 32) {
do {
level->getFloorInfo(info.roomAbove, (int)p.x, (int)p.z, info, true);
} while (info.ceiling > p.y - LARA_HANG_OFFSET && info.roomAbove != 0xFF);
if (abs(int(info.floor - (p.y - LARA_HANG_OFFSET))) < 16) {
turnToWall();
pos = pos - getDir() * 128.0f; // TODO: collision wall offset
pos.y = info.floor + 768.0f - 64.0f;
stand = STAND_HANG;
pos = pos - getDir() * 96.0f; // TODO: collision wall offset
pos.y = info.floor + LARA_HANG_OFFSET;
updateEntity();
return setAnimation(state == STATE_HANG ? ANIM_HANG_UP : ANIM_HANG_FORTH);
stand = STAND_HANG;
if (state == STATE_REACH) {
return STATE_HANG; // TODO: ANIM_HANG_WALL / ANIM_HANG_NOWALL
} else
return setAnimation(ANIM_HANG, -15);
}
}
@ -373,14 +491,17 @@ struct Lara : Controller {
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)
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_1 && state != STATE_ROLL_2)
return setAnimation(ANIM_FALL);
return state;
}
virtual int getStateGround() {
angle.x = 0.0f;
angle.x = 0.0f;
if (mask == ACTION && doPickUp())
return STATE_PICK_UP;
/*
// hit test
@ -431,18 +552,20 @@ struct Lara : Controller {
if ( (mask & (FORTH | ACTION)) == (FORTH | ACTION) && (animIndex == ANIM_STAND || animIndex == ANIM_STAND_NORMAL) ) {
vec3 p = pos + getDir() * 64.0f;
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.z, info);
level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.z, info, true);
int h = (int)pos.y - info.floor;
int aIndex = animIndex;
if (h < 2 * 256 - 16)
if (h < 256 + 128) {
; // do nothing
else if (h <= 2 * 256 + 16)
} else if (h <= 2 * 256 + 128) {
aIndex = ANIM_CLIMB_2;
else if (h <= 3 * 256 + 16)
pos.y = info.floor + 512;
} else if (h <= 3 * 256 + 128) {
aIndex = ANIM_CLIMB_3;
else if (h <= 7 * 256 + 16)
aIndex = ANIM_CLIMB_JUMP;
pos.y = info.floor + 768;
} else if (h <= 7 * 256 + 128)
aIndex = ANIM_CLIMB_JUMP;
if (aIndex != animIndex) {
turnToWall();
@ -459,8 +582,7 @@ struct Lara : Controller {
return STATE_STOP;
}
virtual int getStateSlide() {
void slideStart() {
TR::Entity &e = getEntity();
if (state != STATE_SLIDE && state != STATE_SLIDE_BACK) {
@ -484,29 +606,40 @@ struct Lara : Controller {
angle.y = dir;
updateEntity();
return setAnimation(aIndex);
setAnimation(aIndex);
}
}
if (mask & JUMP) {
stand = STAND_GROUND;
pos.y -= 16;
updateEntity();
virtual int getStateSlide() {
if (mask & JUMP)
return state == STATE_SLIDE ? STATE_FORWARD_JUMP : STATE_BACK_JUMP;
}
return state;
}
virtual int getStateHang() {
if (mask & LEFT) return STATE_HANG_LEFT;
if (mask & RIGHT) return STATE_HANG_RIGHT;
if (mask & FORTH) return (mask & WALK) ? STATE_HANDSTAND : STATE_HANG_UP;
if (mask & FORTH) {
// possibility check
TR::Level::FloorInfo info;
vec3 p = pos + getDir() * 128.0f;
level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.z, info, true);
if (info.floor - info.ceiling >= 768) {
LOG("%d %d %d\n", info.floor, info.ceiling, info.floor - info.ceiling);
return (mask & WALK) ? STATE_HANDSTAND : STATE_HANG_UP;
}
}
return STATE_HANG;
}
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)
if (mask == ACTION && doPickUp())
return STATE_PICK_UP;
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) {
addSprite(level, TR::Entity::Type::WATER_SPLASH, getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z);
return setAnimation(ANIM_WATER_FALL);
}
if (state == STATE_SWAN_DIVE) {
angle.x = -PI * 0.5f;
@ -550,7 +683,7 @@ struct Lara : Controller {
}
virtual int getStateDefault() {
if (state == STATE_DIVE) return state;
if (state == STATE_DIVE || (state == STATE_RUN && (mask & JUMP)) ) return state;
switch (stand) {
case STAND_GROUND : return STATE_STOP;
case STAND_HANG : return STATE_HANG;
@ -580,7 +713,7 @@ struct Lara : Controller {
}
virtual void updateState() {
performTrigger();
doTrigger();
TR::Animation *anim = &level->anims[animIndex];
@ -595,11 +728,11 @@ struct Lara : Controller {
if (Input::down[ikEnter]) {
if (!lState) {
lState = true;
/*
static int snd_id = 0;//160;
playSound(snd_id);
/*playSound(snd_id);
*/
// setAnimation(snd_id);
//setAnimation(snd_id);
//LOG("sound: %d\n", snd_id++);
LOG("state: %d\n", anim->state);
@ -684,6 +817,17 @@ struct Lara : Controller {
}
}
virtual void doCustomCommand(int curFrame, int prevFrame) {
if (state == STATE_PICK_UP) {
if (!level->entities[lastPickUp].flags.invisible) {
int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER;
if (curFrame >= pickupFrame)
level->entities[lastPickUp].flags.invisible = true; // TODO: add to inventory
}
}
}
virtual void updateVelocity() {
// calculate moving speed
float dt = Core::deltaTime * 30.0f;
@ -723,7 +867,13 @@ struct Lara : Controller {
} else {
TR::Entity &e = getEntity();
TR::Level::FloorInfo info;
level->getFloorInfo(e.room, e.x, e.z, info);
if (stand == STAND_HANG) {
vec3 p = pos + getDir() * 128.0f;
level->getFloorInfo(e.room, (int)p.x, (int)p.z, info);
if (info.roomAbove != 0xFF && info.floor >= e.y - LARA_HANG_OFFSET)
level->getFloorInfo(info.roomAbove, (int)p.x, (int)p.z, info);
} else
level->getFloorInfo(e.room, e.x, e.z, info);
vec3 v(sinf(angleExt), 0.0f, cosf(angleExt));
velocity = info.getSlant(v) * speed;
@ -747,8 +897,31 @@ struct Lara : Controller {
}
}
virtual void move() { // TORO: as part of Controller
if (velocity.length() < EPS) return;
virtual void checkRoom() {
TR::Level::FloorInfo info;
TR::Entity &e = getEntity();
level->getFloorInfo(e.room, e.x, e.z, info);
if (info.roomNext != 0xFF)
e.room = info.roomNext;
if (info.roomBelow != 0xFF && e.y > info.floor)
e.room = info.roomBelow;
if (info.roomAbove != 0xFF && e.y <= info.ceiling) {
if (stand == STAND_UNDERWATER && !level->rooms[info.roomAbove].flags.water) {
stand = STAND_ONWATER;
velocity.y = 0;
pos.y = info.ceiling;
updateEntity();
} else
if (stand != STAND_ONWATER)
e.room = info.roomAbove;
}
}
virtual void move() { // TODO: sphere / bbox collision
if (velocity.length() < 0.001f) return;
vec3 offset = velocity * Core::deltaTime * 30.0f;
@ -756,96 +929,107 @@ struct Lara : Controller {
pos = pos + offset;
TR::Level::FloorInfo info;
level->getFloorInfo(getEntity().room, (int)pos.x, (int)pos.z, info);
level->getFloorInfo(getEntity().room, (int)pos.x, (int)pos.z, info, true);
int delta;
int d = getOverlap((int)p.x, (int)p.y, (int)p.z, (int)pos.x, (int)pos.z, delta);
// get frame to get height
TR::Animation *anim = &level->anims[animIndex];
int height = getHeight();
bool canPassGap;
bool canPassGap = (info.floor - info.ceiling) >= (stand == STAND_GROUND ? 768 : 512);
float f = info.floor - pos.y;
float c = pos.y - info.ceiling;
float h = info.floor - pos.y;
switch (stand) {
case STAND_AIR :
canPassGap = ((int)p.y - d) <= 512 && (info.roomAbove != 0xFF || (pos.y - height - info.ceiling > -256));
break;
case STAND_UNDERWATER :
canPassGap = ((int)p.y - d) < 128;
break;
case STAND_ONWATER : {
canPassGap = info.roomAbove != 0xFF;
break;
}
default : // TODO: height
if (state == STATE_WALK || state == STATE_BACK)
canPassGap = h >= -256 && h <= 256;
else
if (state == STATE_STEP_LEFT || state == STATE_STEP_RIGHT)
canPassGap = h >= -128 && h <= 128;
if (canPassGap)
switch (stand) {
case STAND_GROUND : {
if (state == STATE_WALK || state == STATE_BACK)
canPassGap = fabsf(f) <= (256.0f + 128.0f);
else
canPassGap = h >= -256 - 16;
}
if (state == STATE_STEP_LEFT || state == STATE_STEP_RIGHT)
canPassGap = fabsf(f) <= (128.0f + 64.0f);
else
canPassGap = f >= -(256 + 128);
break;
}
case STAND_UNDERWATER :
canPassGap = f > 0.0f && c > 0.0f;
break;
case STAND_AIR : {
int fSize = sizeof(TR::AnimFrame) + getModel().mCount * sizeof(uint16) * 2;
TR::AnimFrame *frame = (TR::AnimFrame*)&level->frameData[(anim->frameOffset + (int(animTime * 30.0f / anim->frameRate) * fSize) / 2)];
TR::Animation *anim = &level->anims[animIndex];
int frame = int(animTime * 30.0f);
bool left = (anim->frameEnd - anim->frameStart) / 2 > frame;
f = info.floor - (p.y + frame->box.maxY);
c = (p.y + frame->box.minY) - info.ceiling;
canPassGap = f >= -256 && c >= (state == STATE_UP_JUMP ? 0.0f : -256);
break;
}
case STAND_ONWATER : {
canPassGap = (info.floor - p.y) >= 1.0f && c >= 1.0f;
break;
}
default : ;
}
if (d == NO_OVERLAP || !canPassGap) {
bool isLeftFoot = (anim->frameEnd - anim->frameStart) / 2 > int(animTime * 30.0f);
if (!canPassGap) {
pos = p; // TODO: use smart ejection
// hit the wall
switch (stand) {
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;
if (c <= 0 && offset.y < 0.0f) offset.y = velocity.y = 0.0f;
}
if (velocity.x != 0.0f || velocity.z != 0.0f) {
setAnimation(ANIM_SMASH_JUMP);
velocity.x = -velocity.x * 0.5f;
velocity.z = -velocity.z * 0.5f;
velocity.y = 0.0f;
} else {
velocity.x = velocity.z = 0.0f;
pos.y = p.y + offset.y;
updateEntity();
if (offset.y < 0.0f)
offset.y = velocity.y = 0.0f;
}
pos.y = p.y + offset.y;
break;
case STAND_GROUND :
if (delta <= -256 * 4 && state == STATE_RUN)
setAnimation(left ? ANIM_SMASH_RUN_LEFT : ANIM_SMASH_RUN_RIGHT);
else
case STAND_HANG :
if (f <= -(256 * 3 - 128) && state == STATE_RUN)
setAnimation(isLeftFoot ? ANIM_SMASH_RUN_LEFT : ANIM_SMASH_RUN_RIGHT);
else if (stand == STAND_HANG)
setAnimation(ANIM_HANG, -21);
else if (state != STATE_ROLL_1 && state != STATE_ROLL_2)
setAnimation(ANIM_STAND);
velocity.x = velocity.z = 0.0f;
break;
default : ;// no smash animation
}
} else {
if (state == STATE_RUN || state == STATE_WALK || state == STATE_BACK || state == STATE_ROLL) {
if (h <= -128 && h >= -256) { // ascend
if (state == STATE_RUN) setAnimation(left ? ANIM_RUN_ASCEND_LEFT : ANIM_RUN_ASCEND_RIGHT);
if (state == STATE_WALK) setAnimation(left ? ANIM_WALK_ASCEND_LEFT : ANIM_WALK_ASCEND_RIGHT);
pos.y = info.floor;
}
} else
if (stand == STAND_GROUND) {
int h = int(info.floor - pos.y);
if (h >= 128 && h <= 256 && (state == STATE_WALK || state == STATE_BACK)) { // descend
if (state == STATE_WALK) setAnimation(left ? ANIM_WALK_DESCEND_LEFT : ANIM_WALK_DESCEND_RIGHT);
if (state == STATE_BACK) setAnimation(left ? ANIM_BACK_DESCEND_LEFT : ANIM_BACK_DESCEND_RIGHT);
pos.y = info.floor;
}
if (h > 0 && (state == STATE_RUN || state == STATE_ROLL)) {
pos.y += DESCENT_SPEED * Core::deltaTime;
}
}
if (state == STATE_FAST_BACK) {
if (h >= 255) {
if (h >= 256 && state == STATE_FAST_BACK) {
stand = STAND_AIR;
setAnimation(ANIM_FALL);
} else if (h >= 128 && (state == STATE_WALK || state == STATE_BACK)) { // descend
if (state == STATE_WALK) setAnimation(isLeftFoot ? ANIM_WALK_DESCEND_LEFT : ANIM_WALK_DESCEND_RIGHT);
if (state == STATE_BACK) setAnimation(isLeftFoot ? ANIM_BACK_DESCEND_LEFT : ANIM_BACK_DESCEND_RIGHT);
pos.y = info.floor;
} else if (h > -1.0f) {
pos.y = min((float)info.floor, pos.y += DESCENT_SPEED * Core::deltaTime);
} else if (h > -128) {
pos.y = info.floor;
} else if (h >= -(256 + 128) && (state == STATE_RUN || state == STATE_WALK)) { // ascend
if (state == STATE_RUN) setAnimation(isLeftFoot ? ANIM_RUN_ASCEND_LEFT : ANIM_RUN_ASCEND_RIGHT);
if (state == STATE_WALK) setAnimation(isLeftFoot ? ANIM_WALK_ASCEND_LEFT : ANIM_WALK_ASCEND_RIGHT);
pos.y = info.floor;
} else
pos.y += DESCENT_SPEED * Core::deltaTime;
pos.y = info.floor;
}
updateEntity();
}
updateEntity();
checkRoom();
}
};

View File

@ -42,7 +42,7 @@ struct Level {
for (int i = 0; i < level.entitiesCount; i++) {
TR::Entity &entity = level.entities[i];
switch (entity.id) {
switch (entity.type) {
case TR::Entity::LARA :
case TR::Entity::LARA_CUT :
entity.controller = (lara = new Lara(&level, i));
@ -100,6 +100,7 @@ struct Level {
case TR::Entity::HOLE_KEY :
entity.controller = new Trigger(&level, i, false);
break;
default : ;
}
}
@ -208,7 +209,7 @@ struct Level {
}
Shader *setRoomShader(const TR::Room &room, float intensity) {
if (room.flags & TR::ROOM_FLAG_WATER) {
if (room.flags.water) {
Core::color = vec4(0.6f * intensity, 0.9f * intensity, 0.9f * intensity, 1.0f);
return shaders[shCaustics];
} else {
@ -231,7 +232,7 @@ struct Level {
// room static meshes
for (int i = 0; i < room.meshesCount; i++) {
TR::Room::Mesh &rMesh = room.meshes[i];
if ((rMesh.flags & TR::ROOM_FLAG_VISIBLE)) continue; // skip if already rendered
if (rMesh.flags.rendered) continue; // skip if already rendered
TR::StaticMesh *sMesh = level.getMeshByID(rMesh.meshID);
ASSERT(sMesh != NULL);
@ -241,7 +242,7 @@ struct Level {
sMesh->getBox(false, rMesh.rotation, min, max);
if (!camera->frustum->isVisible(offset + min, offset + max))
continue;
rMesh.flags |= TR::ROOM_FLAG_VISIBLE;
rMesh.flags.rendered = true;
// set light parameters
getLight(offset, roomIndex);
@ -255,8 +256,8 @@ struct Level {
}
// room geometry & sprites
if (!(room.flags & TR::ROOM_FLAG_VISIBLE)) { // skip if already rendered
room.flags |= TR::ROOM_FLAG_VISIBLE;
if (!room.flags.rendered) { // skip if already rendered
room.flags.rendered = true;
Core::lightColor = vec4(0.0f, 0.0f, 0.0f, 1.0f);
Core::ambient = vec3(1.0f);
@ -494,10 +495,10 @@ struct Level {
}
void renderEntity(const TR::Entity &entity) {
if (entity.id < 0) return;
if (entity.type == TR::Entity::NONE) return;
TR::Room &room = level.rooms[entity.room];
if (!(room.flags & TR::ROOM_FLAG_VISIBLE)) // check for room visibility
if (!room.flags.rendered || entity.flags.invisible) // check for room visibility
return;
mat4 m = Core::mModel;
@ -529,7 +530,7 @@ struct Level {
time += Core::deltaTime;
for (int i = 0; i < level.entitiesCount; i++)
if (level.entities[i].id > -1) {
if (level.entities[i].type != TR::Entity::NONE) {
Controller *controller = (Controller*)level.entities[i].controller;
if (controller)
controller->update();
@ -565,10 +566,10 @@ struct Level {
// clear visible flags for rooms & static meshes
for (int i = 0; i < level.roomsCount; i++) {
TR::Room &room = level.rooms[i];
room.flags &= ~TR::ROOM_FLAG_VISIBLE; // clear visible flag for room geometry & sprites
room.flags.rendered = false; // clear visible flag for room geometry & sprites
for (int j = 0; j < room.meshesCount; j++)
room.meshes[j].flags &= ~TR::ROOM_FLAG_VISIBLE; // clear visible flag for room static meshes
room.meshes[j].flags.rendered = false; // clear visible flag for room static meshes
}
// TODO: collision detection for camera
@ -582,18 +583,30 @@ struct Level {
static int modelIndex = 0;
static bool lastStateK = false;
static int lastEntity = -1;
if (Input::down[ikM]) {
if (!lastStateK) {
lastStateK = true;
// modelIndex = (modelIndex + 1) % level.modelsCount;
modelIndex = (modelIndex + 1) % level.spriteSequencesCount;
LOG("model: %d %d\n", modelIndex, level.spriteSequences[modelIndex].id);
if (lastEntity > -1) {
delete level.entities[lastEntity].controller;
level.entityRemove(lastEntity);
}
lastEntity = level.entityAdd(level.models[modelIndex].id, lara->getRoomIndex(), lara->pos.x + 1024, lara->pos.y - 1024, lara->pos.z, lara->getEntity().rotation, -1);
level.entities[lastEntity].controller = new Controller(&level, lastEntity);
}
} else
lastStateK = false;
Core::mModel.translate(lara->pos + vec3(512, -512, 0));
//renderModel(level.models[modelIndex], level.entities[4]);
if (lastEntity > -1)
renderEntity(level.entities[lastEntity]);
// renderModel(level.models[modelIndex], level.entities[4]);
*/
/*
TR::Entity seq;
seq.modelIndex = -(modelIndex + 1);
seq.controller = NULL;
@ -607,7 +620,7 @@ struct Level {
// Debug::Level::portals(level);
// Debug::Level::meshes(level);
// Debug::Level::entities(level);
Debug::Level::info(level, lara->getEntity());
Debug::Level::info(level, lara->getEntity(), (int)lara->state, lara->animIndex, int(lara->animTime * 30.0f));
Debug::end();
#endif
}

View File

@ -309,12 +309,14 @@ int main() {
if (time <= lastTime)
continue;
float slow = Input::down[ikR] ? 8.0f : 1.0f;
float delta = (time - lastTime) * 0.001f;
EnterCriticalSection(&sndCS);
while (delta > EPS) {
Core::deltaTime = min(delta, 1.0f / 30.0f);
Core::deltaTime = min(delta, 1.0f / 30.0f) / slow;
Game::update();
delta -= Core::deltaTime;
delta -= Core::deltaTime * slow;
}
LeaveCriticalSection(&sndCS);
lastTime = time;

View File

@ -14,7 +14,7 @@ struct Trigger : Controller {
}
bool inState() {
return (state != baseState) == (getEntity().flags & ENTITY_FLAG_ACTIVE) > 0;
return (state != baseState) == (getEntity().flags.active != 0);
}
virtual bool activate(ActionCommand *cmd) {
@ -22,7 +22,7 @@ struct Trigger : Controller {
Controller::activate(cmd);
this->timer = cmd->timer;
getEntity().flags ^= ENTITY_FLAG_ACTIVE;
getEntity().flags.active ^= 0x1F;
if (immediate)
activateNext();
@ -37,7 +37,7 @@ struct Trigger : Controller {
timer -= Core::deltaTime;
if (timer <= 0.0f) {
timer = 0.0f;
entity.flags &= ~ENTITY_FLAG_ACTIVE;
entity.flags.active ^= 0x1F;
}
}
@ -45,12 +45,12 @@ struct Trigger : Controller {
timer += Core::deltaTime;
if (timer >= 0.0f) {
timer = 0.0f;
entity.flags |= ENTITY_FLAG_ACTIVE;
entity.flags.active ^= 0x1F;
}
}
if (!inState())
setState(state != baseState ? baseState : (entity.id == TR::Entity::TRAP_BLADE ? 2 : (baseState ^ 1)));
setState(state != baseState ? baseState : (entity.type == TR::Entity::TRAP_BLADE ? 2 : (baseState ^ 1)));
updateAnimation(true);
updateEntity();
@ -77,9 +77,7 @@ struct Dart : Controller {
TR::Entity &e = getEntity();
vec3 p = pos - dir * 64.0f; // wall offset = 64
int sparkIndex = level->entityAdd(TR::Entity::SPARK, e.room, (int)p.x, (int)p.y, (int)p.z, e.rotation, -1);
if (sparkIndex > -1)
level->entities[sparkIndex].controller = new SpriteController(level, sparkIndex, true, SpriteController::FRAME_RANDOM);
addSprite(level, TR::Entity::SPARK, e.room, (int)p.x, (int)p.y, (int)p.z, SpriteController::FRAME_RANDOM);
level->entityRemove(entity);
delete this;
@ -108,12 +106,8 @@ struct Dartgun : Trigger {
if (dartIndex > -1)
level->entities[dartIndex].controller = new Dart(level, dartIndex);
int smokeIndex = level->entityAdd(TR::Entity::SMOKE, entity.room, (int)pos.x, (int)pos.y, (int)pos.z, entity.rotation, -1);
if (smokeIndex > -1) {
level->entities[smokeIndex].intensity = 0x1FFF - level->rooms[entity.room].ambient;
level->entities[smokeIndex].controller = new SpriteController(level, smokeIndex);
}
addSprite(level, TR::Entity::SMOKE, entity.room, (int)pos.x, (int)pos.y, (int)pos.z);
playSound(151);
return true;
@ -126,7 +120,7 @@ struct Boulder : Trigger {
Boulder(TR::Level *level, int entity) : Trigger(level, entity, true) {}
virtual void update() {
if (getEntity().flags & ENTITY_FLAG_ACTIVE) {
if (getEntity().flags.active) {
updateAnimation(true);
updateEntity();
}