1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-14 00:54:05 +02:00

#22 refactoring, WIP save games implementation

This commit is contained in:
XProger
2017-11-07 05:54:11 +03:00
parent 701cc46978
commit 9abce0d28b
14 changed files with 775 additions and 659 deletions

View File

@@ -6,7 +6,7 @@
struct Animation {
TR::Level *level;
TR::Model *model;
const TR::Model *model;
TR::Animation *anims;
int state;
float time, timeMax, delta, dir;
@@ -22,8 +22,8 @@ struct Animation {
Animation() : overrides(NULL) {}
Animation(TR::Level *level, TR::Model *model) : level(level), model(model), anims(model ? &level->anims[model->animation] : NULL), time(0), delta(0), dir(1.0f),
index(-1), prev(0), next(0), overrides(NULL), overrideMask(0) {
Animation(TR::Level *level, const TR::Model *model) : level(level), model(model), anims(model ? &level->anims[model->animation] : NULL), time(0), delta(0), dir(1.0f),
index(-1), prev(0), next(0), overrides(NULL), overrideMask(0) {
if (anims) setAnim(0);
}

View File

@@ -383,7 +383,6 @@ struct WaterCache {
}
void init(IGame *game) {
TR::Level *level = game->getLevel();
TR::Room &r = level->rooms[to]; // underwater room
@@ -419,7 +418,7 @@ struct WaterCache {
bool hasFlow = false;
if (hasWater) {
TR::Level::FloorInfo info;
level->getFloorInfo(to, x + r.info.x, r.info.yBottom, z + r.info.z, info);
game->getLara()->getFloorInfo(to, vec3(float(x + r.info.x), float(r.info.yBottom), float(z + r.info.z)), info);
if (info.trigCmdCount && info.trigger == TR::Level::Trigger::ACTIVATE)
for (int i = 0; i < info.trigCmdCount; i++)
if (info.trigCmd[i].action == TR::Action::FLOW) {

View File

@@ -41,7 +41,7 @@ struct Camera : ICamera {
Camera(IGame *game, Character *owner) : ICamera(), game(game), level(game->getLevel()), owner(owner), frustum(new Frustum()), timer(-1.0f), viewIndex(-1), viewIndexLast(-1), viewTarget(NULL) {
changeView(false);
if (owner->getEntity().type != TR::Entity::LARA && level->cameraFrames) {
if (!owner->getEntity().isLara() && level->cameraFrames) {
state = STATE_CUTSCENE;
room = level->entities[level->cutEntity].room;
timer = 0.0f;
@@ -71,7 +71,7 @@ struct Camera : ICamera {
}
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z, info);
owner->getFloorInfo(getRoomIndex(), pos, info);
if (info.roomNext != TR::NO_ROOM)
room = info.roomNext;
@@ -257,7 +257,7 @@ struct Camera : ICamera {
if (state != STATE_STATIC) {
if (!owner->viewTarget) {
if (viewTarget && !viewTarget->getEntity().flags.invisible) {
if (viewTarget && !viewTarget->flags.invisible) {
vec3 targetVec = (viewTarget->pos - owner->pos).normal();
if (targetVec.dot(owner->getDir()) > 0.5f)
lookAt = viewTarget;

View File

@@ -2,6 +2,7 @@
#define H_CHARACTER
#include "controller.h"
#include "collision.h"
struct Character : Controller {
float health;
@@ -87,16 +88,15 @@ struct Character : Controller {
virtual void checkRoom() {
TR::Level::FloorInfo info;
TR::Entity &e = getEntity();
level->getFloorInfo(e.room, e.x, e.y, e.z, info);
getFloorInfo(getRoomIndex(), pos, info);
if (info.roomNext != TR::NO_ROOM)
e.room = info.roomNext;
roomIndex = info.roomNext;
if (info.roomBelow != TR::NO_ROOM && e.y > info.roomFloor)
e.room = info.roomBelow;
if (info.roomBelow != TR::NO_ROOM && pos.y > info.roomFloor)
roomIndex = info.roomBelow;
if (info.roomAbove != TR::NO_ROOM && e.y <= info.roomCeiling) {
if (info.roomAbove != TR::NO_ROOM && pos.y <= info.roomCeiling) {
TR::Room *room = &level->rooms[info.roomAbove];
if (level->isFlipped && room->alternateRoom > -1)
room = &level->rooms[room->alternateRoom];
@@ -104,11 +104,10 @@ struct Character : Controller {
if (stand == STAND_UNDERWATER && !room->flags.water) {
stand = STAND_ONWATER;
velocity.y = 0;
pos.y = float(info.roomCeiling);
updateEntity();
pos.y = info.roomCeiling;
} else
if (stand != STAND_ONWATER)
e.room = info.roomAbove;
roomIndex = info.roomAbove;
}
}
@@ -179,7 +178,7 @@ struct Character : Controller {
updateState();
Controller::update();
if (getEntity().flags.active) {
if (flags.active) {
updateVelocity();
updatePosition();
if (p != pos) {

View File

@@ -4,17 +4,19 @@
#include "core.h"
#include "utils.h"
#include "format.h"
#include "controller.h"
struct Collision {
enum Side { NONE, LEFT, RIGHT, FRONT, BACK, TOP, BOTTOM } side;
struct Info {
int room, roomAbove, roomBelow, floor, ceiling;
int room, roomAbove, roomBelow;
float floor, ceiling;
} info[4];
Collision() : side(NONE) {}
Collision(TR::Level *level, int roomIndex, vec3 &pos, const vec3 &offset, const vec3 &velocity, float radius, float angle, int minHeight, int maxHeight, int maxAscent, int maxDescent) {
Collision(Controller *controller, int roomIndex, vec3 &pos, const vec3 &offset, const vec3 &velocity, float radius, float angle, int minHeight, int maxHeight, int maxAscent, int maxDescent) {
if (velocity.x > 0.0f || velocity.z > 0.0f)
angle = normalizeAngle(PI2 + vec2(velocity.z, velocity.x).angle());
pos += velocity;
@@ -38,16 +40,16 @@ struct Collision {
int height = maxHeight - minHeight;
getFloor(level, roomIndex, vec3(pos.x, hpos.y, pos.z));
getFloor(controller->level, roomIndex, vec3(pos.x, hpos.y, pos.z));
if (checkHeight(level, roomIndex, hpos, vec2(0.0f), height, 0xFFFFFF, 0xFFFFFF, side = NONE)) {
if (checkHeight(controller, roomIndex, hpos, vec2(0.0f), height, 0xFFFFFF, 0xFFFFFF, side = NONE)) {
pos.x -= velocity.x;
pos.z -= velocity.z;
side = FRONT;
return;
}
int hCell = info[NONE].ceiling - (int(hpos.y) - maxHeight);
float hCell = info[NONE].ceiling - (hpos.y - maxHeight);
if (hCell > 0) {
if (hCell > 128) {
pos.x -= velocity.x;
@@ -59,19 +61,19 @@ struct Collision {
}
}
int hFloor = info[NONE].floor - (int(hpos.y) + minHeight);
float hFloor = info[NONE].floor - (hpos.y + minHeight);
if (hFloor < 0 && hFloor > -256) {
pos.y = info[NONE].floor - minHeight - offset.y;
side = BOTTOM;
}
if (checkHeight(level, roomIndex, hpos, f, height, maxAscent, maxDescent, FRONT)) {
if (checkHeight(controller, roomIndex, hpos, f, height, maxAscent, maxDescent, FRONT)) {
d = vec2(-velocity.x, -velocity.z);
q ^= 1;
d[q] = getOffset(p[q] + f[q], p[q]);
} else if (checkHeight(level, roomIndex, hpos, l, height, maxAscent, maxDescent, LEFT)) {
} else if (checkHeight(controller, roomIndex, hpos, l, height, maxAscent, maxDescent, LEFT)) {
d[q] = getOffset(p[q] + l[q], p[q] + f[q]);
} else if (checkHeight(level, roomIndex, hpos, r, height, maxAscent, maxDescent, RIGHT)) {
} else if (checkHeight(controller, roomIndex, hpos, r, height, maxAscent, maxDescent, RIGHT)) {
d[q] = getOffset(p[q] + r[q], p[q] + f[q]);
} else
return;
@@ -79,10 +81,9 @@ struct Collision {
pos += vec3(d.x, 0.0f, d.y);
}
inline bool checkHeight(TR::Level *level, int roomIndex, const vec3 &pos, const vec2 &offset, int height, int maxAscent, int maxDescent, Side side) {
inline bool checkHeight(Controller *controller, int roomIndex, const vec3 &pos, const vec2 &offset, int height, int maxAscent, int maxDescent, Side side) {
TR::Level::FloorInfo info;
int py = int(pos.y);
level->getFloorInfo(roomIndex, int(pos.x + offset.x), py, int(pos.z + offset.y), info);
controller->getFloorInfo(roomIndex, pos + vec3(offset.x, 0.0f, offset.y), info);
Info &inf = this->info[side];
inf.room = info.roomNext != TR::NO_ROOM ? info.roomNext : roomIndex;
@@ -91,7 +92,7 @@ struct Collision {
inf.floor = info.floor;
inf.ceiling = info.ceiling;
if ((info.ceiling == info.floor) || (info.floor - info.ceiling < height) || (py - info.floor > maxAscent) || (info.floor - py > maxDescent) || (info.ceiling > py) ||
if ((info.ceiling == info.floor) || (info.floor - info.ceiling < height) || (pos.y - info.floor > maxAscent) || (info.floor - pos.y > maxDescent) || (info.ceiling > pos.y) ||
(maxAscent == maxDescent && (maxAscent <= 256 + 128) && (abs(info.slantX) > 2 || abs(info.slantZ) > 2))) {
this->side = side;
return true;

View File

@@ -5,7 +5,6 @@
#include "frustum.h"
#include "mesh.h"
#include "animation.h"
#include "collision.h"
#define GRAVITY (6.0f * 30.0f)
#define SPRITE_FPS 10.0f
@@ -58,8 +57,7 @@ struct IGame {
virtual void checkTrigger(Controller *controller, bool heavy) {}
virtual int addSprite(TR::Entity::Type type, int room, int x, int y, int z, int frame = -1, bool empty = false) { return -1; }
virtual Controller* addEntity(TR::Entity::Type type, int room, const vec3 &pos, float angle) { return NULL; }
virtual Controller* addEntity(TR::Entity::Type type, int room, const vec3 &pos, float angle = 0.0f) { return NULL; }
virtual bool invUse(TR::Entity::Type type) { return false; }
virtual void invAdd(TR::Entity::Type type, int count = 1) {}
@@ -72,9 +70,38 @@ struct IGame {
};
struct Controller {
struct SaveData {
// base
int32 x, y, z;
uint16 rotation;
uint16 type;
uint16 flags;
int16 timer;
// animation
uint16 animIndex;
uint16 animFrame;
// common
uint8 room;
uint8 extraSize;
union Extra {
struct {
float velX, velY, velZ;
uint16 angleX;
uint16 health;
uint16 oxygen;
int8 curWeapon;
uint8 emptyHands;
} lara;
struct {
uint16 health;
uint16 mood;
} enemy;
} extra;
};
static Controller *first;
Controller *next;
enum ActiveState { asNone, asActive, asInactive };
IGame *game;
TR::Level *level;
@@ -86,6 +113,9 @@ struct Controller {
vec3 pos;
vec3 angle;
int16 roomIndex;
TR::Entity::Flags flags;
Basis *joints;
int frameIndex;
@@ -111,14 +141,17 @@ struct Controller {
int roomIndex;
} *explodeParts;
ActiveState activeState;
bool invertAim;
Controller(IGame *game, int entity) : next(NULL), game(game), level(game->getLevel()), entity(entity), animation(level, getModel()), state(animation.state), layers(0), explodeMask(0), explodeParts(0), activeState(asNone), invertAim(false) {
TR::Entity &e = getEntity();
pos = vec3(float(e.x), float(e.y), float(e.z));
angle = vec3(0.0f, e.rotation, 0.0f);
TR::Model *m = getModel();
Controller(IGame *game, int entity) : next(NULL), game(game), level(game->getLevel()), entity(entity), animation(level, getModel()), state(animation.state), layers(0), explodeMask(0), explodeParts(0), invertAim(false) {
const TR::Entity &e = getEntity();
pos = vec3(float(e.x), float(e.y), float(e.z));
angle = vec3(0.0f, e.rotation, 0.0f);
roomIndex = e.room;
flags = e.flags;
flags.state = TR::Entity::asNone;
const TR::Model *m = getModel();
joints = m ? new Basis[m->mCount] : NULL;
frameIndex = -1;
specular = 0.0f;
@@ -128,14 +161,14 @@ struct Controller {
updateLights(false);
visibleMask = 0xFFFFFFFF;
if (e.flags.once) {
e.flags.invisible = true;
e.flags.once = false;
if (flags.once) {
flags.invisible = true;
flags.once = false;
}
if (e.flags.active == TR::ACTIVE) {
e.flags.active = 0;
e.flags.reverse = true;
if (flags.active == TR::ACTIVE) {
flags.active = 0;
flags.reverse = true;
activate();
}
@@ -150,39 +183,292 @@ struct Controller {
deactivate(true);
}
bool isActive() {
TR::Entity &e = getEntity();
void getFloorInfo(int roomIndex, const vec3 &pos, TR::Level::FloorInfo &info) const {
int x = int(pos.x);
int y = int(pos.y);
int z = int(pos.z);
if (e.flags.active != TR::ACTIVE)
return e.flags.reverse;
int dx, dz;
TR::Room::Sector &s = level->getSector(roomIndex, x, z, dx, dz);
info.roomFloor = float(256 * s.floor);
info.roomCeiling = float(256 * s.ceiling);
info.floor = info.roomFloor;
info.ceiling = info.roomCeiling;
info.slantX = 0;
info.slantZ = 0;
info.roomNext = TR::NO_ROOM;
info.roomBelow = s.roomBelow;
info.roomAbove = s.roomAbove;
info.floorIndex = s.floorIndex;
info.boxIndex = s.boxIndex;
info.lava = false;
info.trigger = TR::Level::Trigger::ACTIVATE;
info.trigCmdCount = 0;
if (s.floor == TR::NO_FLOOR)
return;
TR::Room::Sector *sBelow = &s;
while (sBelow->roomBelow != TR::NO_ROOM) sBelow = &level->getSector(sBelow->roomBelow, x, z, dx, dz);
info.floor = float(256 * sBelow->floor);
parseFloorData(info, sBelow->floorIndex, dx, dz);
if (info.roomNext == TR::NO_ROOM) {
TR::Room::Sector *sAbove = &s;
while (sAbove->roomAbove != TR::NO_ROOM) sAbove = &level->getSector(sAbove->roomAbove, x, z, dx, dz);
if (sAbove != sBelow) {
info.ceiling = float(256 * sAbove->ceiling);
parseFloorData(info, sAbove->floorIndex, dx, dz);
}
} else {
int tmp = info.roomNext;
getFloorInfo(tmp, pos, info);
info.roomNext = tmp;
}
// entities collide
if (info.trigCmdCount) {
int sx = x / 1024;
int sz = z / 1024;
int dx = x % 1024;
int dz = z % 1024;
for (int i = 0; i < info.trigCmdCount; i++) {
TR::FloorData::TriggerCommand cmd = info.trigCmd[i];
if (cmd.action == TR::Action::CAMERA_SWITCH) {
i++;
continue;
}
if (cmd.action != TR::Action::ACTIVATE) continue;
TR::Entity &e = level->entities[cmd.args];
Controller *controller = (Controller*)e.controller;
if (!controller) continue; // Block UpdateFloor issue while entities initialization
if (!controller->flags.collision) continue;
switch (e.type) {
case TR::Entity::TRAP_DOOR_1 :
case TR::Entity::TRAP_DOOR_2 : {
int dirX, dirZ;
e.getAxis(dirX, dirZ);
int ex = int(controller->pos.x) / 1024;
int ey = int(controller->pos.y);
int ez = int(controller->pos.z) / 1024;
if ((ex == sx && ez == sz) || (ex + dirX == sx && ez + dirZ == sz)) {
if (ey >= y - 128 && controller->pos.y < info.floor) {
info.floor = controller->pos.y;
info.slantX = info.slantZ = 0;
info.lava = false;
}
if (ey < y - 128 && controller->pos.y > info.ceiling)
info.ceiling = controller->pos.y + 256;
}
break;
}
case TR::Entity::TRAP_FLOOR : {
if (sx != int(controller->pos.x) / 1024 || sz != int(controller->pos.z) / 1024)
break;
int ey = int(controller->pos.y) - 512;
if (ey >= y - 128 && ey < info.floor) {
info.floor = float(ey);
info.slantX = info.slantZ = 0;
info.lava = false;
}
if (ey < y - 128 && ey > info.ceiling)
info.ceiling = float(ey);
break;
}
case TR::Entity::DRAWBRIDGE : {
if (controller->flags.active != TR::ACTIVE) continue;
int dirX, dirZ;
e.getAxis(dirX, dirZ);
int ex = int(controller->pos.x) / 1024;
int ez = int(controller->pos.z) / 1024;
if ((ex - dirX * 1 == sx && ez - dirZ * 1 == sz) ||
(ex - dirX * 2 == sx && ez - dirZ * 2 == sz)) {
int ey = int(controller->pos.y);
if (ey >= y - 128 && controller->pos.y < info.floor) {
info.floor = controller->pos.y;
info.slantX = info.slantZ = 0;
info.lava = false;
}
if (ey < y - 128 && controller->pos.y > info.ceiling)
info.ceiling = controller->pos.y + 256;
}
break;
}
case TR::Entity::HAMMER_HANDLE : {
int dirX, dirZ;
e.getAxis(dirX, dirZ);
if (abs(int(controller->pos.x) + dirX * 1024 * 3 - x) < 512 && abs(int(controller->pos.z) + dirZ * 1024 * 3 - z) < 512)
info.floor -= 1024 * 3;
break;
}
case TR::Entity::BRIDGE_0 :
case TR::Entity::BRIDGE_1 :
case TR::Entity::BRIDGE_2 : {
if (sx != int(controller->pos.x) / 1024 || sz != int(controller->pos.z) / 1024)
break;
int s = (e.type == TR::Entity::BRIDGE_1) ? 1 :
(e.type == TR::Entity::BRIDGE_2) ? 2 : 0;
int ey = int(controller->pos.y), sx = 0, sz = 0;
if (s > 0) {
switch (e.rotation.value / 0x4000) { // get slantXZ by direction
case 0 : sx = s; break;
case 1 : sz = -s; break;
case 2 : sx = -s; break;
case 3 : sz = s; break;
}
ey -= sx * (sx > 0 ? (dx - 1024) : dx) >> 2;
ey -= sz * (sz > 0 ? (dz - 1024) : dz) >> 2;
}
if (y - 128 <= ey) {
info.floor = float(ey);
info.slantX = sx;
info.slantZ = sz;
info.lava = false;
}
if (ey < y - 128)
info.ceiling = float(ey + 64);
break;
}
default : ;
}
}
}
}
void parseFloorData(TR::Level::FloorInfo &info, int floorIndex, int dx, int dz) const {
if (!floorIndex) return;
TR::FloorData *fd = &level->floors[floorIndex];
TR::FloorData::Command cmd;
do {
cmd = (*fd++).cmd;
switch (cmd.func) {
case TR::FloorData::PORTAL :
info.roomNext = (*fd++).data;
break;
case TR::FloorData::FLOOR : // floor & ceiling
case TR::FloorData::CEILING : {
TR::FloorData::Slant slant = (*fd++).slant;
int sx = (int)slant.x;
int sz = (int)slant.z;
if (cmd.func == TR::FloorData::FLOOR) {
info.slantX = sx;
info.slantZ = sz;
info.floor -= sx * (sx > 0 ? (dx - 1024) : dx) >> 2;
info.floor -= sz * (sz > 0 ? (dz - 1024) : dz) >> 2;
} else {
info.ceiling -= sx * (sx < 0 ? (dx - 1024) : dx) >> 2;
info.ceiling += sz * (sz > 0 ? (dz - 1024) : dz) >> 2;
}
break;
}
case TR::FloorData::TRIGGER : {
info.trigger = (TR::Level::Trigger)cmd.sub;
info.trigCmdCount = 0;
info.trigInfo = (*fd++).triggerInfo;
TR::FloorData::TriggerCommand trigCmd;
do {
ASSERT(info.trigCmdCount < MAX_TRIGGER_COMMANDS);
trigCmd = (*fd++).triggerCmd; // trigger action
info.trigCmd[info.trigCmdCount++] = trigCmd;
} while (!trigCmd.end);
break;
}
case TR::FloorData::LAVA :
info.lava = true;
break;
default : LOG("unknown func: %d\n", cmd.func);
}
} while (!cmd.end);
}
virtual void getSaveData(SaveData &data) {
const TR::Entity &e = getEntity();
const TR::Model *m = getModel();
// base
data.x = e.x ^ int32(pos.x);
data.y = e.y ^ int32(pos.y);
data.z = e.z ^ int32(pos.z);
data.rotation = e.rotation.value ^ TR::angle(normalizeAngle(angle.y)).value;
data.type = entity >= level->entitiesBaseCount ? int16(e.type) : 0;
data.flags = e.flags.value ^ flags.value;
data.timer = timer == 0.0f ? 0 : (timer < 0.0f ? -1 : int16(timer * 30.0f));
// animation
data.animIndex = m ? (m->animation ^ animation.index) : 0;
data.animFrame = m ? animation.frameIndex : 0;
// common
data.room = e.room ^ roomIndex;
data.extraSize = 0;
}
virtual void setSaveData(const SaveData &data) {
const TR::Entity &e = getEntity();
const TR::Model *m = getModel();
// base
pos.x = float(e.x ^ data.x);
pos.y = float(e.y ^ data.y);
pos.z = float(e.z ^ data.z);
angle.y = TR::angle(uint16(e.rotation.value ^ data.rotation));
flags.value = e.flags.value ^ data.flags;
timer = data.timer == -1 ? -1.0f : (timer / 30.0f);
// animation
if (m) animation.setAnim(m->animation ^ data.animIndex, -data.animFrame);
roomIndex = e.room ^ data.room;
}
bool isActive() {
if (flags.active != TR::ACTIVE)
return flags.reverse;
if (timer == 0.0f)
return !e.flags.reverse;
return !flags.reverse;
if (timer == -1.0f)
return e.flags.reverse;
return flags.reverse;
timer = max(0.0f, timer - Core::deltaTime);
if (timer == 0.0f)
timer = -1.0f;
return !e.flags.reverse;
return !flags.reverse;
}
virtual bool activate() {
if (activeState != asNone)
if (flags.state != TR::Entity::asNone)
return false;
getEntity().flags.invisible = false;
activeState = asActive;
flags.invisible = false;
flags.state = TR::Entity::asActive;
next = first;
first = this;
return true;
}
virtual void deactivate(bool removeFromList = false) {
activeState = asInactive;
flags.state = TR::Entity::asInactive;
if (removeFromList) {
flags.state = TR::Entity::asNone;
Controller *prev = NULL;
Controller *c = first;
while (c) {
@@ -191,7 +477,6 @@ struct Controller {
prev->next = c->next;
else
first = c->next;
c->activeState = asNone;
break;
} else
prev = c;
@@ -204,12 +489,12 @@ struct Controller {
Controller *prev = NULL;
Controller *c = first;
while (c) {
if (c->activeState == asInactive) {
if (c->flags.state == TR::Entity::asInactive) {
if (prev)
prev->next = c->next;
else
first = c->next;
c->activeState = asNone;
c->flags.state = TR::Entity::asNone;
} else
prev = c;
c = c->next;
@@ -270,15 +555,6 @@ struct Controller {
return false;
}
void updateEntity() {
TR::Entity &e = getEntity();
e.x = int(pos.x);
e.y = int(pos.y);
e.z = int(pos.z);
angle.y = normalizeAngle(angle.y);
e.rotation = angle.y;
}
bool insideRoom(const vec3 &pos, int roomIndex) const {
TR::Room &r = level->rooms[roomIndex];
vec3 min = vec3((float)r.info.x + 1024, (float)r.info.yTop, (float)r.info.z + 1024);
@@ -289,23 +565,23 @@ struct Controller {
pos.z >= min.z && pos.z <= max.z;
}
TR::Model* getModel() const {
const TR::Model* getModel() const {
int index = getEntity().modelIndex;
return index > 0 ? &level->models[index - 1] : NULL;
}
TR::Entity& getEntity() const {
const TR::Entity& getEntity() const {
return level->entities[entity];
}
TR::Room& getRoom() const {
const TR::Room& getRoom() const {
int index = getRoomIndex();
ASSERT(index >= 0 && index < level->roomsCount);
return level->rooms[index];
}
virtual int getRoomIndex() const {
int index = getEntity().room;
int index = roomIndex;
if (level->isFlipped && level->rooms[index].alternateRoom > -1)
index = level->rooms[index].alternateRoom;
return index;
@@ -359,7 +635,6 @@ struct Controller {
}
angle.y = q * (PI * 0.5f);
updateEntity();
return true;
}
@@ -372,7 +647,7 @@ struct Controller {
}
void getSpheres(Sphere *spheres, int &count) {
TR::Model *m = getModel();
const TR::Model *m = getModel();
ASSERT(m->mCount <= MAX_SPHERES);
Basis basis(getMatrix());
@@ -387,8 +662,8 @@ struct Controller {
}
int collide(Controller *controller, bool checkBoxes = true) {
TR::Model *a = getModel();
TR::Model *b = controller->getModel();
const TR::Model *a = getModel();
const TR::Model *b = controller->getModel();
if (!a || !b)
return 0;
@@ -435,10 +710,10 @@ struct Controller {
sz = pz / 1024 * 1024 + 512;
if (lr != room || lx != sx || lz != sz) {
level->getFloorInfo(room, sx, py, sz, info);
getFloorInfo(room, vec3(float(sx), float(py), float(sz)), info);
if (info.roomNext != TR::NO_ROOM) {
room = info.roomNext;
level->getFloorInfo(room, sx, py, sz, info);
getFloorInfo(room, vec3(float(sx), float(py), float(sz)), info);
}
lr = room;
lx = sx;
@@ -498,7 +773,6 @@ struct Controller {
virtual void cmdOffset(const vec3 &offset) {
pos = pos + offset.rotateY(-angle.y);
updateEntity();
checkRoom();
}
@@ -560,7 +834,7 @@ struct Controller {
void updateExplosion() {
if (!explodeMask) return;
TR::Model *model = getModel();
const TR::Model *model = getModel();
for (int i = 0; i < model->mCount; i++)
if (explodeMask & (1 << i)) {
ExplodePart &part = explodeParts[i];
@@ -572,7 +846,7 @@ struct Controller {
vec3 p = part.basis.pos;
//TR::Room::Sector *s = level->getSector(part.roomIndex, int(p.x), int(p.y), int(p.z));
TR::Level::FloorInfo info;
level->getFloorInfo(part.roomIndex, int(p.x), int(p.y), int(p.z), info);
getFloorInfo(part.roomIndex, p, info);
if (info.roomNext != TR::NO_ROOM)
part.roomIndex = info.roomNext;
@@ -601,9 +875,7 @@ struct Controller {
if (explode) {
explodeMask &= ~(1 << i);
game->addSprite(TR::Entity::EXPLOSION, part.roomIndex, int(p.x), int(p.y), int(p.z));
game->playSound(TR::SND_EXPLOSION, pos, 0); // Sound::Flags::PAN ?
game->addEntity(TR::Entity::EXPLOSION, part.roomIndex, p);
}
}
@@ -620,7 +892,7 @@ struct Controller {
void updateLights(bool lerp = true) {
if (getModel()) {
TR::Room &room = getRoom();
const TR::Room &room = getRoom();
vec3 center = getBoundingBox().center();
float maxAtt = 0.0f;
@@ -662,8 +934,7 @@ struct Controller {
mat4 getMatrix() {
mat4 matrix;
TR::Entity &e = getEntity();
if (!e.isActor()) {
if (!getEntity().isActor()) {
matrix.identity();
matrix.translate(pos);
if (angle.y != 0.0f) matrix.rotateY(angle.y - (animation.flip ? PI * animation.delta : 0.0f));
@@ -676,7 +947,7 @@ struct Controller {
}
void explode(int32 mask) {
TR::Model *model = getModel();
const TR::Model *model = getModel();
if (!layers) initMeshOverrides();
@@ -704,12 +975,9 @@ struct Controller {
}
void renderShadow(MeshBuilder *mesh) {
TR::Entity &entity = getEntity();
const TR::Entity &entity = getEntity();
if (Core::pass != Core::passCompose || !entity.castShadow())
return;
if (entity.isActor()) // cutscene entities have no blob shadow
if (Core::pass != Core::passCompose || !entity.castShadow() || entity.isActor())
return;
Box boxL = getBoundingBoxLocal();
@@ -718,7 +986,7 @@ struct Controller {
vec3 center = boxA.center();
TR::Level::FloorInfo info;
level->getFloorInfo(entity.room, int(center.x), int(center.y), int(center.z), info);
getFloorInfo(getRoomIndex(), center, info);
const vec3 size = boxL.size() * (1.0f / 1024.0f);
@@ -758,11 +1026,10 @@ struct Controller {
if (!explodeMask && frustum && !frustum->isVisible(matrix, box.min, box.max))
return;
TR::Entity &entity = getEntity();
TR::Model *model = getModel();
const TR::Model *model = getModel();
ASSERT(model);
entity.flags.rendered = true;
flags.rendered = true;
if (Core::stats.frame != frameIndex)
animation.getJoints(matrix, -1, true, joints);
@@ -781,7 +1048,7 @@ struct Controller {
if (explodeMask) {
ASSERT(explodeParts);
TR::Model *model = getModel();
const TR::Model *model = getModel();
for (int i = 0; i < model->mCount; i++)
if (explodeMask & (1 << i))
joints[i] = explodeParts[i].basis;
@@ -795,7 +1062,7 @@ struct Controller {
}
} else {
Core::active.shader->setParam(uBasis, joints[0], model->mCount);
mesh->renderModel(entity.modelIndex - 1);
mesh->renderModel(getEntity().modelIndex - 1);
}
frameIndex = Core::stats.frame;

View File

@@ -200,7 +200,7 @@ namespace Debug {
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);
p.xyz = (p.xyz * 0.5f + vec3(0.5f)) * vec3(float(Core::width), float(Core::height), 1.0f);
text(vec2(p.x, p.y), color, str);
}
}
@@ -208,11 +208,13 @@ namespace Debug {
namespace Level {
void debugFloor(const TR::Level &level, int roomIndex, int x, int y, int z, int zone = -1) {
void debugFloor(IGame *game, int roomIndex, int x, int y, int z, int zone = -1) {
TR::Level *level = game->getLevel();
if (zone != -1) {
int dx, dz;
TR::Room::Sector &s = level.getSector(roomIndex, x, z, dx, dz);
if (zone != level.zones[0].ground1[s.boxIndex])
TR::Room::Sector &s = level->getSector(roomIndex, x, z, dx, dz);
if (zone != level->zones[0].ground1[s.boxIndex])
return;
}
@@ -220,10 +222,10 @@ namespace Debug {
vec3 rf[4], rc[4], f[4], c[4];
int offsets[4][2] = { { 1, 1 }, { 1023, 1 }, { 1023, 1023 }, { 1, 1023 } };
float offsets[4][2] = { { 1, 1 }, { 1023, 1 }, { 1023, 1023 }, { 1, 1023 } };
for (int i = 0; i < 4; i++) {
level.getFloorInfo(roomIndex, x + offsets[i][0], y, z + offsets[i][1], info);
game->getLara()->getFloorInfo(roomIndex, vec3(float(x + offsets[i][0]), float(y), float(z + offsets[i][1])), info);
rf[i] = vec3( x + offsets[i][0], info.roomFloor - 4, z + offsets[i][1] );
rc[i] = vec3( x + offsets[i][0], info.roomCeiling + 4, z + offsets[i][1] );
f[i] = vec3( x + offsets[i][0], info.floor - 4, z + offsets[i][1] );
@@ -293,10 +295,10 @@ namespace Debug {
void debugBox(const TR::Box &b) {
glBegin(GL_QUADS);
float y = b.floor - 16.0f;
glVertex3f(b.minX, y, b.maxZ);
glVertex3f(b.maxX, y, b.maxZ);
glVertex3f(b.maxX, y, b.minZ);
glVertex3f(b.minX, y, b.minZ);
glVertex3f(float(b.minX), y, float(b.maxZ));
glVertex3f(float(b.maxX), y, float(b.maxZ));
glVertex3f(float(b.maxX), y, float(b.minZ));
glVertex3f(float(b.minX), y, float(b.minZ));
glEnd();
}
@@ -317,7 +319,7 @@ namespace Debug {
if (blockable || block) {
sprintf(buf, "blocked: %s", block ? "true" : "false");
Debug::Draw::text(vec3(r.info.x + x * 1024 + 512, floor, r.info.z + z * 1024 + 512), vec4(1, 1, 0, 1), buf);
Debug::Draw::text(vec3(float(r.info.x + x * 1024 + 512), float(floor), float(r.info.z + z * 1024 + 512)), vec4(1, 1, 0, 1), buf);
}
}
}
@@ -331,7 +333,7 @@ namespace Debug {
TR::Box &b = level.boxes[boxIndex];
sprintf(str, "%d", boxIndex);
Draw::text(vec3((b.maxX + b.minX) * 0.5, b.floor, (b.maxZ + b.minZ) * 0.5), vec4(0, 1, 0, 1), str);
Draw::text(vec3((b.maxX + b.minX) * 0.5f, b.floor, (b.maxZ + b.minZ) * 0.5f), vec4(0, 1, 0, 1), str);
glColor4f(0.0f, 1.0f, 0.0f, 0.25f);
Core::setBlending(bmAlpha);
debugBox(b);
@@ -340,7 +342,7 @@ namespace Debug {
do {
TR::Box &b = level.boxes[o->boxIndex];
sprintf(str, "%d", o->boxIndex);
Draw::text(vec3((b.maxX + b.minX) * 0.5, b.floor, (b.maxZ + b.minZ) * 0.5), vec4(0, 0, 1, 1), str);
Draw::text(vec3((b.maxX + b.minX) * 0.5f, b.floor, (b.maxZ + b.minZ) * 0.5f), vec4(0, 0, 1, 1), str);
glColor4f(0.0f, 0.0f, 1.0f, 0.25f);
Core::setBlending(bmAlpha);
debugBox(b);
@@ -359,12 +361,12 @@ namespace Debug {
Core::setDepthTest(true);
}
void sectors(const TR::Level &level, int roomIndex, int y, int zone = -1) {
TR::Room &room = level.rooms[roomIndex];
void sectors(IGame *game, int roomIndex, int y, int zone = -1) {
TR::Room &room = game->getLevel()->rooms[roomIndex];
for (int z = 0; z < room.zSectors; z++)
for (int x = 0; x < room.xSectors; x++)
debugFloor(level, roomIndex, room.info.x + x * 1024, y, room.info.z + z * 1024, zone);
debugFloor(game, roomIndex, room.info.x + x * 1024, y, room.info.z + z * 1024, zone);
}
void rooms(const TR::Level &level, const vec3 &pos, int roomIndex) {
@@ -372,7 +374,7 @@ namespace Debug {
for (int i = 0; i < level.roomsCount; i++) {
TR::Room &r = level.rooms[i];
vec3 p = vec3(r.info.x, r.info.yTop, r.info.z);
vec3 p = vec3(float(r.info.x), float(r.info.yTop), float(r.info.z));
if (i == roomIndex) {
//if (lara->insideRoom(Core::viewPos, i)) {
@@ -399,7 +401,7 @@ namespace Debug {
TR::Room::Portal &p = r.portals[j];
for (int k = 0; k < 4; k++) {
TR::Vertex &v = p.vertices[k];
glVertex3f(v.x + r.info.x, v.y, v.z + r.info.z);
glVertex3f(float(v.x + r.info.x), float(v.y), float(v.z + r.info.z));
}
}
}
@@ -414,16 +416,18 @@ namespace Debug {
for (int i = 0; i < level.entitiesCount; i++) {
TR::Entity &e = level.entities[i];
Controller *controller = (Controller*)e.controller;
if (!controller) return;
sprintf(buf, "%d (%d)", (int)e.type, i);
Debug::Draw::text(vec3(e.x, e.y, e.z), e.flags.active ? vec4(0, 0, 0.8, 1) : vec4(0.8, 0, 0, 1), buf);
Debug::Draw::text(controller->pos, controller->flags.active ? vec4(0, 0, 0.8f, 1) : vec4(0.8f, 0, 0, 1), buf);
}
for (int i = 0; i < level.camerasCount; i++) {
TR::Camera &c = level.cameras[i];
sprintf(buf, "%d (%d)", i, c.room);
Debug::Draw::text(vec3(c.x, c.y, c.z), vec4(0, 0.8, 0, 1), buf);
Debug::Draw::text(vec3(float(c.x), float(c.y), float(c.z)), vec4(0, 0.8f, 0, 1), buf);
}
}
@@ -445,21 +449,23 @@ namespace Debug {
Core::setDepthTest(true);
}
void zones(const TR::Level &level, Lara *lara) {
void zones(IGame *game, Lara *lara) {
TR::Level *level = game->getLevel();
Core::setDepthTest(false);
for (int i = 0; i < level.roomsCount; i++)
sectors(level, i, int(lara->pos.y), lara->zone);
for (int i = 0; i < level->roomsCount; i++)
sectors(game, i, int(lara->pos.y), lara->zone);
Core::setDepthTest(true);
char buf[64];
for (int i = 0; i < level.entitiesCount; i++) {
TR::Entity &e = level.entities[i];
for (int i = 0; i < level->entitiesCount; i++) {
TR::Entity &e = level->entities[i];
if (e.type < TR::Entity::LARA || e.type > TR::Entity::ENEMY_GIANT_MUTANT)
continue;
if (!e.controller || !e.isEnemy()) continue;
sprintf(buf, "zone: %d", ((Character*)e.controller)->zone );
Debug::Draw::text(vec3(e.x, e.y - 128, e.z), vec4(0, 1.0, 0.8, 1), buf);
Character *controller = (Character*)e.controller;
sprintf(buf, "zone: %d", controller->zone);
Debug::Draw::text(controller->pos - vec3(0, 128, 0), vec4(0, 1, 0.8f, 1), buf);
}
}
@@ -469,14 +475,14 @@ namespace Debug {
for (int j = 0; j < level.rooms[i].lightsCount; j++) {
TR::Room::Light &l = level.rooms[i].lights[j];
float a = 1.0f - intensityf(l.intensity);
vec3 p = vec3(l.x, l.y, l.z);
vec3 p = vec3(float(l.x), float(l.y), float(l.z));
vec4 color = vec4(a, a, a, 1);
// if (i == room) color.x = color.z = 0;
Debug::Draw::point(p, color);
//if (i == roomIndex && j == lightIndex)
// color = vec4(0, 1, 0, 1);
Debug::Draw::sphere(p, l.radius, color);
Debug::Draw::sphere(p, float(l.radius), color);
}
vec4 color = vec4(lara->mainLightColor.x, 0.0f, 0.0f, 1.0f);
@@ -494,7 +500,7 @@ namespace Debug {
TR::StaticMesh *sm = &level.staticMeshes[m.meshIndex];
Box box;
vec3 offset = vec3(m.x, m.y, m.z);
vec3 offset = vec3(float(m.x), float(m.y), float(m.z));
sm->getBox(false, m.rotation, box); // visible box
Debug::Draw::box(offset + box.min, offset + box.max, vec4(1, 1, 0, 0.25));
@@ -525,7 +531,7 @@ namespace Debug {
mat4 matrix = controller->getMatrix();
Basis basis(matrix);
TR::Model *m = controller->getModel();
const TR::Model *m = controller->getModel();
if (!m) continue;
bool bboxIntersect = false;
@@ -535,8 +541,9 @@ namespace Debug {
int mask = 0;
for (int j = 0; j < level.entitiesCount; j++) {
TR::Entity &t = level.entities[j];
if (j == i || ((!t.isEnemy() || !t.flags.active) && t.type != TR::Entity::LARA)) continue;
Controller *enemy = (Controller*)t.controller;
if (!enemy) continue;
if (j == i || ((!t.isEnemy() || !enemy->flags.active) && !t.isLara())) continue;
if (!enemy || !controller->getBoundingBox().intersect(enemy->getBoundingBox()))
continue;
bboxIntersect = true;
@@ -677,7 +684,8 @@ namespace Debug {
Debug::Draw::text(vec2(16, y += 16), vec4(1.0f), buf);
TR::Level::FloorInfo info;
level.getFloorInfo(((Controller*)entity.controller)->getRoomIndex(), entity.x, entity.y, entity.z, info);
Controller *controller = (Controller*)entity.controller;
controller->getFloorInfo(controller->getRoomIndex(), controller->pos, info);
sprintf(buf, "floor = %d, roomBelow = %d, roomAbove = %d, height = %d", info.floorIndex, info.roomBelow, info.roomAbove, info.floor - info.ceiling);
Debug::Draw::text(vec2(16, y += 16), vec4(1.0f), buf);

View File

@@ -77,12 +77,21 @@ struct Enemy : Character {
delete path;
}
virtual void getSaveData(SaveData &data) {
Character::getSaveData(data);
data.extraSize = sizeof(data.extra.enemy);
data.extra.enemy.health = uint16(health);
data.extra.enemy.mood = uint16(mood);
}
virtual void setSaveData(const SaveData &data) {
Character::setSaveData(data);
health = float(data.extra.enemy.health);
mood = Mood(data.extra.enemy.mood);
}
virtual bool activate() {
if (health > 0.0f && Character::activate()) {
target = (Character*)game->getLara();
return true;
}
return false;
return health > 0.0f && Character::activate();
}
virtual void updateVelocity() {
@@ -136,7 +145,7 @@ struct Enemy : Character {
}
virtual void updatePosition() {
if (!getEntity().flags.active) return;
if (!flags.active) return;
vec3 p = pos;
pos += velocity * (30.0f * Core::deltaTime);
@@ -144,10 +153,10 @@ struct Enemy : Character {
clipByBox(pos);
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info);
getFloorInfo(getRoomIndex(), pos, info);
if (stand == STAND_AIR && !flying && info.floor < pos.y) {
stand = STAND_GROUND;
pos.y = float(info.floor);
pos.y = info.floor;
}
if (info.boxIndex != 0xFFFF && zone == getZones()[info.boxIndex] && !level->boxes[info.boxIndex].overlap.block) {
@@ -158,14 +167,13 @@ struct Enemy : Character {
break;
}
case STAND_AIR :
pos.y = clamp(pos.y, float(info.ceiling), float(info.floor));
pos.y = clamp(pos.y, info.ceiling, info.floor);
break;
default : ;
}
} else
pos = p;
updateEntity();
checkRoom();
}
@@ -223,7 +231,6 @@ struct Enemy : Character {
speed *= Core::deltaTime;
decrease(delta, pos.y, speed);
if (speed != 0.0f) {
updateEntity();
return speed < 0 ? FORTH : BACK;
}
return 0;
@@ -239,7 +246,7 @@ struct Enemy : Character {
void bite(const vec3 &pos, float damage) {
ASSERT(target);
target->hit(damage, this);
Sprite::add(game, TR::Entity::BLOOD, target->getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z, Sprite::FRAME_ANIMATED);
game->addEntity(TR::Entity::BLOOD, target->getRoomIndex(), pos);
}
#define STALK_BOX (1024 * 3)
@@ -280,6 +287,9 @@ struct Enemy : Character {
}
bool think(bool fixedLogic) {
if (!target)
target = (Character*)game->getLara();
thinkTime += Core::deltaTime;
if (thinkTime < 1.0f / 30.0f)
return false;
@@ -398,9 +408,8 @@ struct Enemy : Character {
if (zone != getZones()[box])
return false;
TR::Entity &e = getEntity();
TR::Box &b = game->getLevel()->boxes[box];
TR::Entity::Type type = e.type;
TR::Entity::Type type = getEntity().type;
if (b.overlap.block)
return false;
@@ -412,17 +421,16 @@ struct Enemy : Character {
if (b.overlap.block)
return false;
return e.x < int(b.minX) || e.x > int(b.maxX) || e.z < int(b.minZ) || e.z > int(b.maxZ);
return int(pos.x) < int(b.minX) || int(pos.x) > int(b.maxX) || int(pos.z) < int(b.minZ) || int(pos.z) > int(b.maxZ);
}
bool isStalkBox(int box) {
TR::Entity &t = target->getEntity();
TR::Box &b = game->getLevel()->boxes[box];
int x = (b.minX + b.maxX) / 2 - t.x;
int x = (b.minX + b.maxX) / 2 - int(target->pos.x);
if (abs(x) > STALK_BOX) return false;
int z = (b.minZ + b.maxZ) / 2 - t.z;
int z = (b.minZ + b.maxZ) / 2 - int(target->pos.z);
if (abs(z) > STALK_BOX) return false;
// TODO: check for some quadrant shit
@@ -431,17 +439,15 @@ struct Enemy : Character {
}
bool isEscapeBox(int box) {
TR::Entity &e = getEntity();
TR::Entity &t = target->getEntity();
TR::Box &b = game->getLevel()->boxes[box];
int x = (b.minX + b.maxX) / 2 - t.x;
int x = (b.minX + b.maxX) / 2 - int(target->pos.x);
if (abs(x) < ESCAPE_BOX) return false;
int z = (b.minZ + b.maxZ) / 2 - t.z;
int z = (b.minZ + b.maxZ) / 2 - int(target->pos.z);
if (abs(z) < ESCAPE_BOX) return false;
return !((e.x > t.x) ^ (x > 0)) || !((e.z > t.z) ^ (z > 0));
return !((pos.x > target->pos.x) ^ (x > 0)) || !((pos.z > target->pos.z) ^ (z > 0));
}
bool findPath(int ascend, int descend, bool big) {
@@ -506,8 +512,7 @@ struct Wolf : Enemy {
}
virtual int getStateGround() {
TR::Entity &e = getEntity();
if (!e.flags.active)
if (!flags.active)
return (state == STATE_STOP || state == STATE_SLEEP) ? STATE_SLEEP : STATE_STOP;
if (!think(false))
@@ -672,7 +677,7 @@ struct Bear : Enemy {
}
virtual int getStateGround() {
if (!getEntity().flags.active)
if (!flags.active)
return state;
if (!think(true))
@@ -808,7 +813,7 @@ struct Bat : Enemy {
}
virtual int getStateAir() {
if (!getEntity().flags.active) {
if (!flags.active) {
animation.time = 0.0f;
animation.dir = 0.0f;
return STATE_AWAKE;
@@ -854,10 +859,10 @@ struct Bat : Enemy {
virtual void deactivate(bool removeFromList = false) {
if (health <= 0.0f) {
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info);
getFloorInfo(getRoomIndex(), pos, info);
if (info.floor > pos.y)
return;
pos.y = float(info.floor);
pos.y = info.floor;
}
Enemy::deactivate(removeFromList);
}
@@ -896,7 +901,7 @@ struct Rex : Enemy {
}
virtual int getStateGround() {
if (!getEntity().flags.active)
if (!flags.active)
return state;
if (!think(true))
@@ -1015,7 +1020,7 @@ struct Raptor : Enemy {
}
virtual int getStateGround() {
if (!getEntity().flags.active)
if (!flags.active)
return state;
if (!think(true))
@@ -1098,8 +1103,7 @@ struct Raptor : Enemy {
struct Mutant : Enemy {
Mutant(IGame *game, int entity) : Enemy(game, entity, 50, 341, 150.0f, 1.0f) {
TR::Entity &e = getEntity();
if (e.type != TR::Entity::ENEMY_MUTANT_1) {
if (getEntity().type != TR::Entity::ENEMY_MUTANT_1) {
initMeshOverrides();
layers[0].mask = 0xffe07fff;
aggression = 0.25f;
@@ -1122,7 +1126,7 @@ struct Mutant : Enemy {
if (exploded && !explodeMask) {
deactivate(true);
getEntity().flags.invisible = true;
flags.invisible = true;
}
}
@@ -1187,7 +1191,7 @@ struct GiantMutant : Enemy {
if (exploded && !explodeMask) {
game->checkTrigger(this, true);
deactivate(true);
getEntity().flags.invisible = true;
flags.invisible = true;
}
}
};
@@ -1273,11 +1277,10 @@ struct Doppelganger : Enemy {
angle.y -= PI;
}
updateEntity();
Enemy::checkRoom();
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info);
getFloorInfo(getRoomIndex(), pos, info);
if (stand != STAND_AIR && lara->stand == Character::STAND_GROUND && pos.y < info.floor - 1024) {
animation = Animation(level, lara->getModel());
@@ -1289,7 +1292,7 @@ struct Doppelganger : Enemy {
if (stand == STAND_AIR) {
if (pos.y > info.floor) {
game->checkTrigger(this, true);
getEntity().flags.invisible = true;
flags.invisible = true;
deactivate(true);
} else {
updateAnimation(true);
@@ -1327,7 +1330,7 @@ struct ScionTarget : Enemy {
if (health <= 0.0f) {
if (timer == 0.0f) {
getEntity().flags.invisible = true;
flags.invisible = true;
game->checkTrigger(this, true);
timer = 3.0f;
}
@@ -1338,8 +1341,7 @@ struct ScionTarget : Enemy {
if (index != int(timer / 0.3f)) {
vec3 p = pos + vec3((randf() * 2.0f - 1.0f) * 512.0f, (randf() * 2.0f - 1.0f) * 64.0f - 500.0f, (randf() * 2.0f - 1.0f) * 512.0f);
game->addSprite(TR::Entity::EXPLOSION, getRoomIndex(), int(p.x), int(p.y), int(p.z));
game->playSound(TR::SND_EXPLOSION, pos, 0);
game->addEntity(TR::Entity::EXPLOSION, getRoomIndex(), p);
game->getCamera()->shake = 0.5f;
}

View File

@@ -333,6 +333,7 @@ namespace TR {
enum HitType {
HIT_DEFAULT,
HIT_FALL,
HIT_DART,
HIT_BLADE,
HIT_BOULDER,
HIT_SPIKES,
@@ -420,6 +421,7 @@ namespace TR {
uint16 value;
angle() {}
angle(uint16 value) : value(value) {}
angle(float value) : value(uint16(value / (PI * 0.5f) * 16384.0f)) {}
operator float() const { return value / 16384.0f * PI * 0.5f; };
};
@@ -644,15 +646,16 @@ namespace TR {
};
struct Entity {
enum ActiveState : uint16 { asNone, asActive, asInactive };
enum Type : int16 { NONE = -1, TR1_TYPES(DECL_ENUM) };
enum Type : int16 { NONE = -1, TR1_TYPES(DECL_ENUM) } type;
Type type;
int16 room;
int32 x, y, z;
angle rotation;
int16 intensity;
union Flags {
struct { uint16 unused:6, collision:1, invisible:1, once:1, active:5, reverse:1, rendered:1; };
struct { ActiveState state:2; uint16 unused:4, collision:1, invisible:1, once:1, active:5, reverse:1, rendered:1; };
uint16 value;
} flags;
// not exists in file
@@ -704,6 +707,12 @@ namespace TR {
return type == LARA;
}
bool isSprite() const {
return type == EXPLOSION || type == WATER_SPLASH || type == BUBBLE ||
type == BLOOD || type == SMOKE || type == FLAME ||
type == RICOCHET || type == SPARKLES || type == LAVA_PARTICLE;
}
bool castShadow() const {
return isLara() || isEnemy() || isActor() || type == DART || type == TRAP_SWORD;
}
@@ -1158,9 +1167,9 @@ namespace TR {
};
struct FloorInfo {
int roomFloor, roomCeiling;
float roomFloor, roomCeiling;
int roomNext, roomBelow, roomAbove;
int floor, ceiling;
float floor, ceiling;
int slantX, slantZ;
int floorIndex;
int boxIndex;
@@ -2096,16 +2105,16 @@ namespace TR {
return 0;
}
int entityAdd(Entity::Type type, int16 room, int32 x, int32 y, int32 z, angle rotation, int16 intensity) {
int entityAdd(Entity::Type type, int16 room, const vec3 &pos, float rotation, int16 intensity) {
for (int i = entitiesBaseCount; i < entitiesCount; i++)
if (entities[i].type == Entity::NONE) {
Entity &e = entities[i];
e.type = type;
e.room = room;
e.x = x;
e.y = y;
e.z = z;
e.rotation = rotation;
e.x = int(pos.x);
e.y = int(pos.y);
e.z = int(pos.z);
e.rotation = angle(normalizeAngle(rotation));
e.intensity = intensity;
e.flags.value = 0;
e.modelIndex = getModelIndex(e.type);
@@ -2162,7 +2171,7 @@ namespace TR {
return room.sectors[sectorIndex = (x * room.zSectors + z)];
}
Room::Sector* getSector(int &roomIndex, int x, int y, int z) const {
Room::Sector* getSector(int16 &roomIndex, int x, int y, int z) const {
ASSERT(roomIndex >= 0 && roomIndex <= roomsCount);
Room::Sector *sector = NULL;
@@ -2202,223 +2211,6 @@ namespace TR {
return sector;
}
void getFloorInfo(int roomIndex, int x, int y, int z, FloorInfo &info) const {
int dx, dz;
Room::Sector &s = getSector(roomIndex, x, z, dx, dz);
info.roomFloor = 256 * s.floor;
info.roomCeiling = 256 * s.ceiling;
info.floor = info.roomFloor;
info.ceiling = info.roomCeiling;
info.slantX = 0;
info.slantZ = 0;
info.roomNext = NO_ROOM;
info.roomBelow = s.roomBelow;
info.roomAbove = s.roomAbove;
info.floorIndex = s.floorIndex;
info.boxIndex = s.boxIndex;
info.lava = false;
info.trigger = Trigger::ACTIVATE;
info.trigCmdCount = 0;
if (s.floor == NO_FLOOR)
return;
Room::Sector *sBelow = &s;
while (sBelow->roomBelow != NO_ROOM) sBelow = &getSector(sBelow->roomBelow, x, z, dx, dz);
info.floor = 256 * sBelow->floor;
parseFloorData(info, sBelow->floorIndex, dx, dz);
if (info.roomNext == NO_ROOM) {
Room::Sector *sAbove = &s;
while (sAbove->roomAbove != NO_ROOM) sAbove = &getSector(sAbove->roomAbove, x, z, dx, dz);
if (sAbove != sBelow) {
info.ceiling = 256 * sAbove->ceiling;
parseFloorData(info, sAbove->floorIndex, dx, dz);
}
} else {
int tmp = info.roomNext;
getFloorInfo(tmp, x, y, z, info);
info.roomNext = tmp;
}
// entities collide
if (info.trigCmdCount) {
int sx = x / 1024;
int sz = z / 1024;
int dx = x % 1024;
int dz = z % 1024;
for (int i = 0; i < info.trigCmdCount; i++) {
FloorData::TriggerCommand cmd = info.trigCmd[i];
if (cmd.action == Action::CAMERA_SWITCH) {
i++;
continue;
}
if (cmd.action != Action::ACTIVATE) continue;
Entity &e = entities[cmd.args];
if (!e.flags.collision) continue;
switch (e.type) {
case Entity::TRAP_DOOR_1 :
case Entity::TRAP_DOOR_2 : {
int dirX, dirZ;
e.getAxis(dirX, dirZ);
int ex = e.x / 1024;
int ez = e.z / 1024;
if ((ex == sx && ez == sz) || (ex + dirX == sx && ez + dirZ == sz)) {
if (e.y >= y - 128 && e.y < info.floor) {
info.floor = e.y;
info.slantX = info.slantZ = 0;
info.lava = false;
}
if (e.y < y - 128 && e.y > info.ceiling)
info.ceiling = e.y + 256;
}
break;
}
case Entity::TRAP_FLOOR : {
if (sx != e.x / 1024 || sz != e.z / 1024)
break;
int ey = e.y - 512;
if (ey >= y - 128 && ey < info.floor) {
info.floor = ey;
info.slantX = info.slantZ = 0;
info.lava = false;
}
if (ey < y - 128 && ey > info.ceiling)
info.ceiling = ey;
break;
}
case Entity::DRAWBRIDGE : {
if (e.flags.active != TR::ACTIVE) continue;
int dirX, dirZ;
e.getAxis(dirX, dirZ);
int ex = e.x / 1024;
int ez = e.z / 1024;
if ((ex - dirX * 1 == sx && ez - dirZ * 1 == sz) ||
(ex - dirX * 2 == sx && ez - dirZ * 2 == sz)) {
int ey = e.y;
if (ey >= y - 128 && ey < info.floor) {
info.floor = ey;
info.slantX = info.slantZ = 0;
info.lava = false;
}
if (ey < y - 128 && ey > info.ceiling)
info.ceiling = ey + 256;
}
break;
}
case Entity::HAMMER_HANDLE : {
int dirX, dirZ;
e.getAxis(dirX, dirZ);
if (abs(e.x + dirX * 1024 * 3 - x) < 512 && abs(e.z + dirZ * 1024 * 3 - z) < 512)
info.floor -= 1024 * 3;
break;
}
case Entity::BRIDGE_0 :
case Entity::BRIDGE_1 :
case Entity::BRIDGE_2 : {
if (sx != e.x / 1024 || sz != e.z / 1024)
break;
int s = (e.type == Entity::BRIDGE_1) ? 1 :
(e.type == Entity::BRIDGE_2) ? 2 : 0;
int ey = e.y, sx = 0, sz = 0;
if (s > 0) {
switch (e.rotation.value / 0x4000) { // get slantXZ by direction
case 0 : sx = s; break;
case 1 : sz = -s; break;
case 2 : sx = -s; break;
case 3 : sz = s; break;
}
ey -= sx * (sx > 0 ? (dx - 1024) : dx) >> 2;
ey -= sz * (sz > 0 ? (dz - 1024) : dz) >> 2;
}
if (y - 128 <= ey) {
info.floor = ey;
info.slantX = sx;
info.slantZ = sz;
info.lava = false;
}
if (ey < y - 128)
info.ceiling = ey + 64;
break;
}
default : ;
}
}
}
}
void parseFloorData(FloorInfo &info, int floorIndex, int dx, int dz) const {
if (!floorIndex) return;
FloorData *fd = &floors[floorIndex];
FloorData::Command cmd;
do {
cmd = (*fd++).cmd;
switch (cmd.func) {
case FloorData::PORTAL :
info.roomNext = (*fd++).data;
break;
case FloorData::FLOOR : // floor & ceiling
case FloorData::CEILING : {
FloorData::Slant slant = (*fd++).slant;
int sx = (int)slant.x;
int sz = (int)slant.z;
if (cmd.func == FloorData::FLOOR) {
info.slantX = sx;
info.slantZ = sz;
info.floor -= sx * (sx > 0 ? (dx - 1024) : dx) >> 2;
info.floor -= sz * (sz > 0 ? (dz - 1024) : dz) >> 2;
} else {
info.ceiling -= sx * (sx < 0 ? (dx - 1024) : dx) >> 2;
info.ceiling += sz * (sz > 0 ? (dz - 1024) : dz) >> 2;
}
break;
}
case FloorData::TRIGGER : {
info.trigger = (Trigger)cmd.sub;
info.trigCmdCount = 0;
info.trigInfo = (*fd++).triggerInfo;
FloorData::TriggerCommand trigCmd;
do {
ASSERT(info.trigCmdCount < MAX_TRIGGER_COMMANDS);
trigCmd = (*fd++).triggerCmd; // trigger action
info.trigCmd[info.trigCmdCount++] = trigCmd;
} while (!trigCmd.end);
break;
}
case FloorData::LAVA :
info.lava = true;
break;
default : LOG("unknown func: %d\n", cmd.func);
}
} while (!cmd.end);
}
}; // struct Level
}

View File

@@ -95,6 +95,16 @@ namespace Game {
}
}
if (Input::down[ikS]) {
level->saveGame(0);
Input::down[ikS] = false;
}
if (Input::down[ikL]) {
level->loadGame(0);
Input::down[ikL] = false;
}
Core::deltaTime = delta = min(0.2f, delta);
UI::update();

View File

@@ -227,7 +227,7 @@ struct Lara : Character {
TR::Entity::Type usedKey;
int pickupListCount;
TR::Entity *pickupList[32];
Controller *pickupList[32];
KeyHole *keyHole;
Lightning *lightning;
Texture *environment;
@@ -325,7 +325,7 @@ struct Lara : Character {
void collide() {
TR::Level *level = lara->level;
TR::Model *model = lara->getModel();
const TR::Model *model = lara->getModel();
#define BRAID_RADIUS 16.0f
@@ -421,7 +421,7 @@ struct Lara : Character {
lightning = NULL;
environment = NULL;
getEntity().flags.active = 1;
flags.active = 1;
initMeshOverrides();
if (level->isHomeLevel)
@@ -492,7 +492,7 @@ struct Lara : Character {
dbgBoxes = NULL;
#endif
if (getEntity().type == TR::Entity::LARA) {
if (getEntity().isLara()) {
if (getRoom().flags.water)
animation.setAnim(ANIM_UNDERWATER);
else
@@ -507,6 +507,35 @@ struct Lara : Character {
delete environment;
}
virtual void getSaveData(SaveData &data) {
Character::getSaveData(data);
data.extraSize = sizeof(data.extra.lara);
data.extra.lara.velX = velocity.x;
data.extra.lara.velY = velocity.y;
data.extra.lara.velZ = velocity.z;
data.extra.lara.angleX = TR::angle(normalizeAngle(angle.x)).value;
data.extra.lara.health = uint16(health);
data.extra.lara.oxygen = uint16(oxygen);
data.extra.lara.curWeapon = int8(wpnCurrent);
data.extra.lara.emptyHands = emptyHands();
}
virtual void setSaveData(const SaveData &data) {
Character::setSaveData(data);
velocity = vec3(data.extra.lara.velX, data.extra.lara.velY, data.extra.lara.velZ);
angle.x = TR::angle(data.extra.lara.angleX);
health = float(data.extra.lara.health);
oxygen = float(data.extra.lara.oxygen);
layers[1].mask = layers[2].mask = layers[3].mask = 0;
wpnState = Weapon::IS_HIDDEN;
wpnCurrent = Weapon::EMPTY;
wpnSet(Weapon::Type(data.extra.lara.curWeapon));
if (!data.extra.lara.emptyHands)
wpnDraw(true);
}
int getRoomByPos(const vec3 &pos) {
int x = int(pos.x),
y = int(pos.y),
@@ -543,9 +572,9 @@ struct Lara : Character {
velocity = vec3(0.0f);
getEntity().room = room;
this->pos = pos;
this->angle = vec3(0.0f, angle, 0.0f);
roomIndex = room;
this->pos = pos;
this->angle = vec3(0.0f, angle, 0.0f);
if (forceStand != STAND_GROUND) {
stand = forceStand;
@@ -556,7 +585,6 @@ struct Lara : Character {
}
}
updateEntity();
updateLights(false);
}
@@ -732,15 +760,15 @@ struct Lara : Character {
return arms[0].anim != Weapon::Anim::PREPARE && arms[0].anim != Weapon::Anim::UNHOLSTER && arms[0].anim != Weapon::Anim::HOLSTER;
}
void wpnDraw() {
void wpnDraw(bool instant = false) {
if (!canDrawWeapon()) return;
if (wpnReady() && emptyHands()) {
if (wpnCurrent != Weapon::SHOTGUN) {
wpnSetAnim(arms[0], wpnState, Weapon::Anim::PREPARE, 0.0f, 1.0f);
wpnSetAnim(arms[1], wpnState, Weapon::Anim::PREPARE, 0.0f, 1.0f);
wpnSetAnim(arms[0], wpnState, instant ? Weapon::Anim::AIM : Weapon::Anim::PREPARE, 0.0f, 1.0f);
wpnSetAnim(arms[1], wpnState, instant ? Weapon::Anim::AIM : Weapon::Anim::PREPARE, 0.0f, 1.0f);
} else
wpnSetAnim(arms[0], wpnState, Weapon::Anim::UNHOLSTER, 0.0f, 1.0f);
wpnSetAnim(arms[0], wpnState, instant ? Weapon::Anim::AIM : Weapon::Anim::UNHOLSTER, 0.0f, 1.0f);
}
}
@@ -868,10 +896,10 @@ struct Lara : Character {
((Character*)arm->target)->hit(wpnGetDamage(), this);
hit -= d * 64.0f;
if (type != TR::Entity::SCION_TARGET)
Sprite::add(game, TR::Entity::BLOOD, room, (int)hit.x, (int)hit.y, (int)hit.z, Sprite::FRAME_ANIMATED);
game->addEntity(TR::Entity::BLOOD, room, hit);
} else {
hit -= d * 64.0f;
Sprite::add(game, TR::Entity::RICOCHET, room, (int)hit.x, (int)hit.y, (int)hit.z, Sprite::FRAME_RANDOM);
game->addEntity(TR::Entity::RICOCHET, room, hit);
float dist = (hit - p).length();
if (dist < nearDist) {
@@ -1225,7 +1253,7 @@ struct Lara : Character {
Controller *c = Controller::first;
do {
if (!level->entities[c->entity].isEnemy())
if (!c->getEntity().isEnemy())
continue;
Character *enemy = (Character*)c;
@@ -1318,11 +1346,8 @@ struct Lara : Character {
if (!count) return;
game->playSound(TR::SND_BUBBLE, pos, Sound::Flags::PAN);
vec3 head = animation.getJoints(getMatrix(), 14, true) * vec3(0.0f, 0.0f, 50.0f);
for (int i = 0; i < count; i++) {
int index = Sprite::add(game, TR::Entity::BUBBLE, getRoomIndex(), int(head.x), int(head.y), int(head.z), Sprite::FRAME_RANDOM, true);
if (index > -1)
level->entities[index].controller = new Bubble(game, index);
}
for (int i = 0; i < count; i++)
game->addEntity(TR::Entity::BUBBLE, getRoomIndex(), head, 0);
}
virtual void cmdEffect(int fx) {
@@ -1342,14 +1367,14 @@ struct Lara : Character {
for (int i = 0; i < count; i++)
if (mask & (1 << i)) {
vec3 sprPos = spheres[i].center + (vec3(randf(), randf(), randf()) * 2.0f - 1.0f) * spheres[i].radius;
Sprite::add(game, TR::Entity::SPARKLES, getRoomIndex(), int(sprPos.x), int(sprPos.y), int(sprPos.z), Sprite::FRAME_ANIMATED);
game->addEntity(TR::Entity::SPARKLES, getRoomIndex(), sprPos);
}
}
void addBlood(const vec3 &sprPos, const vec3 &sprVel) {
int index = Sprite::add(game, TR::Entity::BLOOD, getRoomIndex(), int(sprPos.x), int(sprPos.y), int(sprPos.z), Sprite::FRAME_ANIMATED);
if (index > -1)
((Sprite*)level->entities[index].controller)->velocity = sprVel;
Sprite *sprite = (Sprite*)game->addEntity(TR::Entity::BLOOD, getRoomIndex(), sprPos, 0);
if (sprite)
sprite->velocity = sprVel;
}
void addBlood(float radius, float height, const vec3 &sprVel) {
@@ -1373,14 +1398,14 @@ struct Lara : Character {
}
void bakeEnvironment() {
getEntity().flags.invisible = true;
flags.invisible = true;
if (!environment)
environment = new Texture(256, 256, Texture::RGBA, true, NULL, true, true);
Core::beginFrame();
game->renderEnvironment(getRoomIndex(), pos - vec3(0.0f, 384.0f, 0.0f), &environment, 0, Core::passCompose);
environment->generateMipMap();
Core::endFrame();
getEntity().flags.invisible = false;
flags.invisible = false;
}
virtual void hit(float damage, Controller *enemy = NULL, TR::HitType hitType = TR::HIT_DEFAULT) {
@@ -1393,6 +1418,7 @@ struct Lara : Character {
Character::hit(damage, enemy, hitType);
switch (hitType) {
case TR::HIT_DART : addBlood(enemy->pos, vec3(0));
case TR::HIT_BLADE : addBloodBlade(); break;
case TR::HIT_SPIKES : addBloodSpikes(); break;
case TR::HIT_SWORD : addBloodBlade(); break;
@@ -1425,7 +1451,7 @@ struct Lara : Character {
if (enemy && enemy->getEntity().type == TR::Entity::TRAP_BOULDER) {
angle = enemy->angle;
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info);
getFloorInfo(getRoomIndex(), pos, info);
vec3 d = getDir();
v = info.getSlant(d);
float dp = d.dot(v);
@@ -1468,11 +1494,10 @@ struct Lara : Character {
}
if (hitType != TR::HIT_LAVA) {
TR::Entity &e = getEntity();
TR::Level::FloorInfo info;
level->getFloorInfo(e.room, e.x, e.y, e.z, info);
getFloorInfo(getRoomIndex(), pos, info);
if (info.lava && info.floor == e.y)
if (info.lava && info.floor == pos.y)
hitType = TR::HIT_LAVA;
}
@@ -1533,22 +1558,21 @@ struct Lara : Character {
vec3 dst = pos + getDir() * (LARA_RADIUS + 32.0f);
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info);
getFloorInfo(getRoomIndex(), pos, info);
int roomAbove = info.roomAbove;
if (roomAbove == TR::NO_ROOM)
return false;
level->getFloorInfo(roomAbove, int(dst.x), int(dst.y), int(dst.z), info);
getFloorInfo(roomAbove, dst, info);
int h = int(pos.y - info.floor);
if (h >= 0 && h <= (256 + 128) && (state == STATE_SURF_TREAD || animation.setState(STATE_SURF_TREAD)) && animation.setState(STATE_STOP)) {
alignToWall(LARA_RADIUS);
roomPrev = getRoomIndex();
getEntity().room = roomAbove;
pos.y = float(info.floor);
roomIndex = roomAbove;
pos.y = info.floor;
specular = LARA_WET_SPECULAR;
updateEntity();
move();
return true;
}
@@ -1576,12 +1600,13 @@ struct Lara : Character {
continue;
Controller *controller = (Controller*)entity.controller;
if (!controller) continue;
if (controller->getRoomIndex() != room || entity.flags.invisible || !canPickup(controller))
if (controller->getRoomIndex() != room || controller->flags.invisible || !canPickup(controller))
continue;
ASSERT(pickupListCount < COUNT(pickupList));
pickupList[pickupListCount++] = &entity;
pickupList[pickupListCount++] = controller;
}
if (pickupListCount > 0) {
@@ -1593,11 +1618,11 @@ struct Lara : Character {
}
bool canPickup(Controller *controller) {
TR::Entity &entity = controller->getEntity();
TR::Entity::Type type = controller->getEntity().type;
// get limits
TR::Limits::Limit *limit;
switch (entity.type) {
switch (type) {
case TR::Entity::SCION_PICKUP_QUALOPEC : limit = &TR::Limits::SCION; break;
case TR::Entity::SCION_PICKUP_HOLDER : limit = &TR::Limits::SCION_HOLDER; break;
default : limit = level->rooms[getRoomIndex()].flags.water ? &TR::Limits::PICKUP_UNDERWATER : &TR::Limits::PICKUP;
@@ -1610,7 +1635,7 @@ struct Lara : Character {
angle.x = -25 * DEG2RAD;
// set new state
switch (entity.type) {
switch (type) {
case TR::Entity::SCION_PICKUP_QUALOPEC :
animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation);
game->getCamera()->doCutscene(pos, angle.y);
@@ -1710,11 +1735,10 @@ struct Lara : Character {
}
void checkTrigger(Controller *controller, bool heavy) {
TR::Entity &e = controller->getEntity();
TR::Level::FloorInfo info;
level->getFloorInfo(e.room, e.x, e.y, e.z, info);
getFloorInfo(getRoomIndex(), controller->pos, info);
if (e.type == TR::Entity::LARA && info.lava && info.floor == e.y) {
if (getEntity().isLara() && info.lava && info.floor == pos.y) {
hit(LARA_MAX_HEALTH + 1, NULL, TR::HIT_LAVA);
return;
}
@@ -1733,7 +1757,7 @@ struct Lara : Character {
case TR::Level::Trigger::SWITCH : {
Switch *controller = (Switch*)level->entities[info.trigCmd[cmdIndex++].args].controller;
if (controller->activeState == asNone) {
if (controller->flags.state == TR::Entity::asNone) {
limit = state == STATE_STOP ? &TR::Limits::SWITCH : &TR::Limits::SWITCH_UNDERWATER;
if (checkInteraction(controller, limit, Input::state[cAction])) {
actionState = (controller->state == Switch::STATE_DOWN && stand == STAND_GROUND) ? STATE_SWITCH_UP : STATE_SWITCH_DOWN;
@@ -1753,8 +1777,8 @@ struct Lara : Character {
TR::Entity &entity = level->entities[info.trigCmd[cmdIndex++].args];
KeyHole *controller = (KeyHole*)entity.controller;
if (controller->activeState == asNone) {
if (entity.flags.active == TR::ACTIVE || state != STATE_STOP)
if (controller->flags.state == TR::Entity::asNone) {
if (controller->flags.active == TR::ACTIVE || state != STATE_STOP)
return;
actionState = entity.isPuzzleHole() ? STATE_USE_PUZZLE : STATE_USE_KEY;
@@ -1782,7 +1806,7 @@ struct Lara : Character {
animation.setState(actionState);
}
if (controller->activeState != asInactive)
if (controller->flags.state != TR::Entity::asInactive)
return;
break;
@@ -1799,7 +1823,7 @@ struct Lara : Character {
case TR::Level::Trigger::PAD :
case TR::Level::Trigger::ANTIPAD :
if (e.y != info.floor) return;
if (pos.y != info.floor) return;
break;
case TR::Level::Trigger::HEAVY :
@@ -1822,11 +1846,13 @@ struct Lara : Character {
switch (cmd.action) {
case TR::Action::ACTIVATE : {
TR::Entity &e = level->entities[cmd.args];
TR::Entity::Flags &flags = e.flags;
Controller *controller = (Controller*)e.controller;
ASSERT(controller);
TR::Entity::Flags &flags = controller->flags;
if (flags.once)
break;
((Controller*)e.controller)->timer = timer;
controller->timer = timer;
if (info.trigger == TR::Level::Trigger::SWITCH)
flags.active ^= info.trigInfo.mask;
@@ -1840,7 +1866,7 @@ struct Lara : Character {
flags.once |= info.trigInfo.once;
((Controller*)e.controller)->activate();
controller->activate();
break;
}
case TR::Action::CAMERA_SWITCH : {
@@ -1959,7 +1985,6 @@ struct Lara : Character {
animation.setAnim(ANIM_FALL_HANG);
velocity = vec3(0.0f);
pos.y += 128.0f;
updateEntity();
return STAND_AIR;
}
@@ -1981,14 +2006,12 @@ struct Lara : Character {
return STAND_UNDERWATER;
}
TR::Entity &e = getEntity();
TR::Level::FloorInfo info;
level->getFloorInfo(e.room, e.x, e.y, e.z, info);
getFloorInfo(getRoomIndex(), pos, info);
if ((stand == STAND_SLIDE || stand == STAND_GROUND) && (state != STATE_FORWARD_JUMP && state != STATE_BACK_JUMP)) {
if (e.y + 8 >= info.floor && (abs(info.slantX) > 2 || abs(info.slantZ) > 2)) {
pos.y = float(info.floor);
updateEntity();
if (pos.y + 8 >= info.floor && (abs(info.slantX) > 2 || abs(info.slantZ) > 2)) {
pos.y = info.floor;
if (stand == STAND_GROUND)
slideStart();
@@ -1999,10 +2022,9 @@ struct Lara : Character {
int extra = (stand != STAND_AIR && stand != STAND_SLIDE) ? 256 : 0;
if (e.y + extra >= info.floor && !(stand == STAND_AIR && velocity.y < 0)) {
if (pos.y + extra >= info.floor && !(stand == STAND_AIR && velocity.y < 0)) {
if (stand != STAND_GROUND) {
pos.y = float(info.floor);
updateEntity();
pos.y = info.floor;
// get damage from falling
if (velocity.y > 0.0f) {
stopScreaming();
@@ -2052,27 +2074,25 @@ struct Lara : Character {
vec3 p = vec3(pos.x, bounds.min.y, pos.z);
Collision c = Collision(level, getRoomIndex(), p, getDir() * 128.0f, vec3(0.0f), LARA_RADIUS, angleExt, 0, 0, 0, 0);
Collision c = Collision(this, getRoomIndex(), p, getDir() * 128.0f, vec3(0.0f), LARA_RADIUS, angleExt, 0, 0, 0, 0);
if (c.side != Collision::FRONT)
return state;
int floor = c.info[Collision::FRONT].floor;
int ceiling = c.info[Collision::FRONT].ceiling;
int hands = int(bounds.min.y);
float floor = c.info[Collision::FRONT].floor;
float ceiling = c.info[Collision::FRONT].ceiling;
float hands = bounds.min.y;
if (abs(floor - hands) < 64 && floor != ceiling) {
alignToWall(-LARA_RADIUS);
pos.y = float(floor + LARA_HANG_OFFSET);
stand = STAND_HANG;
updateEntity();
if (state == STATE_REACH) {
velocity = vec3(0.0f);
vec3 p = pos + getDir() * 256.0f;
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), int(p.x), int(p.y), int(p.z), info);
int h = info.ceiling - floor;
getFloorInfo(getRoomIndex(), pos + getDir() * 256.0f, info);
int h = int(info.ceiling - floor);
return animation.setAnim((h > 0 && h < 400) ? ANIM_HANG_SWING : ANIM_HANG);
} else
return animation.setAnim(ANIM_HANG, -15);
@@ -2140,10 +2160,10 @@ struct Lara : Character {
return state;
if ((input & (FORTH | ACTION)) == (FORTH | ACTION) && (animation.index == ANIM_STAND || animation.index == ANIM_STAND_NORMAL) && emptyHands() && collision.side == Collision::FRONT) { // TODO: get rid of animation.index
int floor = collision.info[Collision::FRONT].floor;
int ceiling = collision.info[Collision::FRONT].ceiling;
float floor = collision.info[Collision::FRONT].floor;
float ceiling = collision.info[Collision::FRONT].ceiling;
int h = (int)pos.y - floor;
float h = pos.y - floor;
int aIndex = animation.index;
if (floor == ceiling || h < 256)
@@ -2181,7 +2201,7 @@ struct Lara : Character {
if (res != STATE_UP_JUMP) {
vec3 p = pos;
collision = Collision(level, getRoomIndex(), p, vec3(0.0f), vec3(0.0f), LARA_RADIUS * 2.5f, ext, 0, LARA_HEIGHT, 256 + 128, 0xFFFFFF);
collision = Collision(this, getRoomIndex(), p, vec3(0.0f), vec3(0.0f), LARA_RADIUS * 2.5f, ext, 0, LARA_HEIGHT, 256 + 128, 0xFFFFFF);
if (collision.side == Collision::FRONT)
res = STATE_UP_JUMP;
}
@@ -2228,7 +2248,7 @@ struct Lara : Character {
if (state == STATE_STOP && res != STATE_STOP) {
vec3 p = pos;
collision = Collision(level, getRoomIndex(), p, vec3(0.0f), vec3(0.0f), LARA_RADIUS * 1.1f, ext, 0, LARA_HEIGHT, maxAscent, maxDescent);
collision = Collision(this, getRoomIndex(), p, vec3(0.0f), vec3(0.0f), LARA_RADIUS * 1.1f, ext, 0, LARA_HEIGHT, maxAscent, maxDescent);
if (collision.side == Collision::FRONT)
res = STATE_STOP;
}
@@ -2264,7 +2284,7 @@ struct Lara : Character {
if (state == STATE_STOP && res != STATE_STOP) {
float ext = angle.y + (res == STATE_RUN ? 0.0f : PI);
vec3 p = pos;
collision = Collision(level, getRoomIndex(), p, vec3(0.0f), vec3(0.0f), LARA_RADIUS * 1.1f, ext, 0, LARA_HEIGHT, 256 + 128, 0xFFFFFF);
collision = Collision(this, getRoomIndex(), p, vec3(0.0f), vec3(0.0f), LARA_RADIUS * 1.1f, ext, 0, LARA_HEIGHT, 256 + 128, 0xFFFFFF);
if (collision.side == Collision::FRONT)
res = STATE_STOP;
}
@@ -2273,11 +2293,9 @@ struct Lara : Character {
}
void slideStart() {
TR::Entity &e = getEntity();
if (state != STATE_SLIDE && state != STATE_SLIDE_BACK) {
TR::Level::FloorInfo info;
level->getFloorInfo(e.room, e.x, e.y, e.z, info);
getFloorInfo(getRoomIndex(), pos, info);
int sx = abs(info.slantX), sz = abs(info.slantZ);
// get direction
@@ -2295,7 +2313,6 @@ struct Lara : Character {
}
angle.y = dir;
updateEntity();
animation.setAnim(aIndex);
}
}
@@ -2312,8 +2329,7 @@ struct Lara : Character {
if (input & FORTH) {
// possibility check
TR::Level::FloorInfo info;
vec3 p = pos + getDir() * (LARA_RADIUS + 2.0f);
level->getFloorInfo(getRoomIndex(), (int)p.x, (int)p.y, (int)p.z, info);
getFloorInfo(getRoomIndex(), pos + getDir() * (LARA_RADIUS + 2.0f), info);
if (info.floor - info.ceiling >= LARA_HEIGHT)
return (input & WALK) ? STATE_HANDSTAND : STATE_HANG_UP;
}
@@ -2326,7 +2342,7 @@ struct Lara : Character {
if (state == STATE_FORWARD_JUMP || state == STATE_UP_JUMP || state == STATE_BACK_JUMP || state == STATE_LEFT_JUMP || state == STATE_RIGHT_JUMP || state == STATE_FALL || state == STATE_REACH || state == STATE_SLIDE || state == STATE_SLIDE_BACK) {
game->waterDrop(pos, 256.0f, 0.2f);
Sprite::add(game, TR::Entity::WATER_SPLASH, getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z);
game->addEntity(TR::Entity::WATER_SPLASH, getRoomIndex(), pos);
pos.y += 100.0f;
angle.x = -45.0f * DEG2RAD;
return animation.setAnim(ANIM_WATER_FALL); // TODO: wronng animation
@@ -2335,7 +2351,7 @@ struct Lara : Character {
if (state == STATE_SWAN_DIVE || state == STATE_FAST_DIVE) {
angle.x = -PI * 0.5f;
game->waterDrop(pos, 128.0f, 0.2f);
Sprite::add(game, TR::Entity::WATER_SPLASH, getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z);
game->addEntity(TR::Entity::WATER_SPLASH, getRoomIndex(), pos);
return STATE_DIVE;
}
@@ -2493,10 +2509,10 @@ struct Lara : Character {
int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER;
if (animation.isFrameActive(pickupFrame)) {
for (int i = 0; i < pickupListCount; i++) {
if (pickupList[i]->type == TR::Entity::SCION_PICKUP_HOLDER)
if (pickupList[i]->getEntity().type == TR::Entity::SCION_PICKUP_HOLDER)
continue;
pickupList[i]->flags.invisible = true;
game->invAdd(pickupList[i]->type, 1);
game->invAdd(pickupList[i]->getEntity().type, 1);
}
pickupListCount = 0;
}
@@ -2516,7 +2532,7 @@ struct Lara : Character {
virtual void update() {
Character::update();
if (getEntity().type != TR::Entity::LARA)
if (!getEntity().isLara())
return;
if (damageTime > 0.0f)
@@ -2560,7 +2576,7 @@ struct Lara : Character {
virtual void updateVelocity() {
flowVelocity = vec3(0);
if (getEntity().type != TR::Entity::LARA)
if (!getEntity().isLara())
return;
if (!(input & DEATH))
@@ -2650,15 +2666,14 @@ struct Lara : Character {
velocity.z = cosf(angleExt) * speed;
velocity.y = 0.0f;
} else {
TR::Entity &e = getEntity();
TR::Level::FloorInfo info;
if (stand == STAND_HANG) {
vec3 p = pos + getDir() * (LARA_RADIUS + 2.0f);
level->getFloorInfo(e.room, (int)p.x, (int)p.y, (int)p.z, info);
if (info.roomAbove != TR::NO_ROOM && info.floor >= e.y - LARA_HANG_OFFSET)
level->getFloorInfo(info.roomAbove, (int)p.x, (int)p.y, (int)p.z, info);
getFloorInfo(getRoomIndex(), p, info);
if (info.roomAbove != TR::NO_ROOM && info.floor >= pos.y - LARA_HANG_OFFSET)
getFloorInfo(info.roomAbove, p, info);
} else
level->getFloorInfo(e.room, e.x, e.y, e.z, info);
getFloorInfo(getRoomIndex(), pos, info);
vec3 v(sinf(angleExt), 0.0f, cosf(angleExt));
velocity = info.getSlant(v) * speed;
@@ -2683,7 +2698,7 @@ struct Lara : Character {
}
virtual void updatePosition() { // TODO: sphere / bbox collision
if (getEntity().type != TR::Entity::LARA)
if (!getEntity().isLara())
return;
// tilt control
vec2 vTilt(LARA_TILT_SPEED * Core::deltaTime, LARA_TILT_MAX);
@@ -2697,14 +2712,11 @@ struct Lara : Character {
if (checkCollisions() || (velocity + flowVelocity + collisionOffset).length2() >= 1.0f) // TODO: stop & smash anim
move();
if (getEntity().type != TR::Entity::LARA) {
TR::Entity &e = getEntity();
vec3 &p = getPos();
e.x = int(p.x);
e.y = int(p.y);
e.z = int(p.z);
if (!getEntity().isLara()) {
vec3 p = pos;
pos = chestOffset;
checkRoom();
updateEntity();
pos = p;
}
if (braid)
@@ -2712,12 +2724,12 @@ struct Lara : Character {
}
virtual vec3& getPos() {
return getEntity().type == TR::Entity::LARA ? pos : chestOffset;
return getEntity().isLara() ? pos : chestOffset;
}
bool checkCollisions() {
// check static objects (TODO: check linked rooms?)
TR::Room &room = getRoom();
const TR::Room &room = getRoom();
Box box(pos - vec3(LARA_RADIUS, LARA_HEIGHT, LARA_RADIUS), pos + vec3(LARA_RADIUS, 0.0f, LARA_RADIUS));
for (int i = 0; i < room.meshesCount; i++) {
@@ -2734,19 +2746,18 @@ struct Lara : Character {
// check enemies & doors
for (int i = 0; i < level->entitiesCount; i++) {
TR::Entity &e = level->entities[i];
const TR::Entity &e = level->entities[i];
if (!e.isCollider()) continue;
Controller *controller = (Controller*)e.controller;
if (e.isEnemy()) {
if (e.type != TR::Entity::ENEMY_REX && (e.flags.active != TR::ACTIVE || ((Character*)controller)->health <= 0)) continue;
if (e.type != TR::Entity::ENEMY_REX && (controller->flags.active != TR::ACTIVE || ((Character*)controller)->health <= 0)) continue;
} else {
// fast distance check for object
TR::Entity &entity = getEntity();
if (e.type != TR::Entity::HAMMER_HANDLE && e.type != TR::Entity::HAMMER_BLOCK && e.type != TR::Entity::SCION_HOLDER)
if (abs(entity.x - e.x) > 1024 || abs(entity.z - e.z) > 1024 || abs(entity.y - e.y) > 2048) continue;
if (abs(pos.x - controller->pos.x) > 1024 || abs(pos.z - controller->pos.z) > 1024 || abs(pos.y - controller->pos.y) > 2048) continue;
}
vec3 dir = pos - vec3(0.0f, 128.0f, 0.0f) - controller->pos;
@@ -2832,7 +2843,7 @@ struct Lara : Character {
offset.y += LARA_HEIGHT_WATER * 0.5f;
}
collision = Collision(level, room, pos, offset, vel, radius, angleExt, minHeight, maxHeight, maxAscent, maxDescent);
collision = Collision(this, room, pos, offset, vel, radius, angleExt, minHeight, maxHeight, maxAscent, maxDescent);
if (!standHang && (collision.side == Collision::LEFT || collision.side == Collision::RIGHT)) {
float rot = TURN_WALL_Y * Core::deltaTime;
@@ -2845,7 +2856,7 @@ struct Lara : Character {
maxDescent = 0xFFFFFF;
maxAscent = -LARA_HANG_OFFSET;
vec3 p = pos;
collision = Collision(level, room, p, offset, vec3(0.0f), radius, angleExt, minHeight, maxHeight, maxAscent, maxDescent);
collision = Collision(this, room, p, offset, vec3(0.0f), radius, angleExt, minHeight, maxHeight, maxAscent, maxDescent);
if (collision.side == Collision::FRONT)
pos = opos;
}
@@ -2869,7 +2880,7 @@ struct Lara : Character {
velocity.y = 30.0f;
if (collision.side == Collision::FRONT) {
int floor = collision.info[Collision::FRONT].floor;
float floor = collision.info[Collision::FRONT].floor;
/*
switch (angleQuadrant(angleExt - angle.y)) {
case 0 : collision.side = Collision::FRONT; LOG("FRONT\n"); break;
@@ -2915,8 +2926,8 @@ struct Lara : Character {
}
} else {
if (stand == STAND_GROUND) {
int floor = collision.info[Collision::NONE].floor;
int h = int(floor - opos.y);
float floor = collision.info[Collision::NONE].floor;
float h = floor - opos.y;
if (h >= 128 && (state == STATE_WALK || state == STATE_BACK)) { // descend
if (state == STATE_WALK) animation.setAnim(isLeftFoot ? ANIM_WALK_DESCEND_LEFT : ANIM_WALK_DESCEND_RIGHT);
@@ -2936,7 +2947,6 @@ struct Lara : Character {
collision.side = Collision::NONE;
}
updateEntity();
if (dozy) stand = STAND_GROUND;
checkRoom();
if (dozy) stand = STAND_UNDERWATER;

View File

@@ -86,11 +86,51 @@ struct Level : IGame {
}
virtual void loadGame(int slot) {
//
Stream stream("savegame.dat");
char *data;
stream.read(data, stream.size);
char *ptr = data;
Controller::first = NULL;
for (int i = level.entitiesBaseCount; i < level.entitiesCount; i++) {
TR::Entity &e = level.entities[i];
if (e.controller) {
delete (Controller*)e.controller;
e.controller = NULL;
}
}
for (int i = 0; i < level.entitiesBaseCount; i++) {
ASSERT(int(ptr - data) < stream.size);
Controller *controller = (Controller*)level.entities[i].controller;
ASSERT(controller);
Controller::SaveData *saveData = (Controller::SaveData*)ptr;
controller->setSaveData(*saveData);
if (controller->flags.state != TR::Entity::asNone) {
controller->next = Controller::first;
Controller::first = controller;
}
ptr += (sizeof(Controller::SaveData) - sizeof(Controller::SaveData::Extra)) + saveData->extraSize;
}
delete[] data;
// camera->room = lara->getRoomIndex();
// camera->pos = camera->destPos = lara->pos;
}
virtual void saveGame(int slot) {
//
char *data = new char[sizeof(Controller::SaveData) * level.entitiesBaseCount];
char *ptr = data;
for (int i = 0; i < level.entitiesBaseCount; i++) {
Controller::SaveData *saveData = (Controller::SaveData*)ptr;
Controller *controller = (Controller*)level.entities[i].controller;
ASSERT(controller);
controller->getSaveData(*saveData);
ptr += (sizeof(Controller::SaveData) - sizeof(Controller::SaveData::Extra)) + saveData->extraSize;
}
Stream::write("savegame.dat", data, int(ptr - data));
delete[] data;
}
virtual void applySettings(const Core::Settings &settings) {
@@ -263,24 +303,28 @@ struct Level : IGame {
lara->checkTrigger(controller, heavy);
}
virtual int addSprite(TR::Entity::Type type, int room, int x, int y, int z, int frame = -1, bool empty = false) {
return Sprite::add(this, type, room, x, y, z, frame, empty);
}
virtual Controller* addEntity(TR::Entity::Type type, int room, const vec3 &pos, float angle) {
int index = level.entityAdd(type, room, int(pos.x), int(pos.y), int(pos.z), TR::angle(angle), -1);
int index = level.entityAdd(type, room, pos, angle, -1);
if (index > -1) {
TR::Entity &e = level.entities[index];
Controller *controller = initController(index);
e.controller = controller;
if (e.isEnemy()) {
e.flags.active = TR::ACTIVE;
if (e.isEnemy() || e.isSprite()) {
controller->flags.active = TR::ACTIVE;
controller->activate();
}
if (e.isPickup())
e.intensity = 4096;
if (e.type == TR::Entity::LAVA_PARTICLE || e.type == TR::Entity::FLAME)
e.intensity = 0; // emissive
else
if (e.isSprite()) {
if (e.type == TR::Entity::LAVA_PARTICLE || e.type == TR::Entity::FLAME)
e.intensity = 0; // emissive
else
e.intensity = 0x1FFF - level.rooms[room].ambient;
}
return controller;
}
return NULL;
@@ -550,6 +594,13 @@ struct Level : IGame {
case TR::Entity::SCION_TARGET : return new ScionTarget(this, index);
case TR::Entity::WATERFALL : return new Waterfall(this, index);
case TR::Entity::TRAP_LAVA : return new TrapLava(this, index);
case TR::Entity::BUBBLE : return new Bubble(this, index);
case TR::Entity::EXPLOSION : return new Explosion(this, index);
case TR::Entity::WATER_SPLASH :
case TR::Entity::BLOOD :
case TR::Entity::SMOKE :
case TR::Entity::SPARKLES : return new Sprite(this, index, true, Sprite::FRAME_ANIMATED);
case TR::Entity::RICOCHET : return new Sprite(this, index, true, Sprite::FRAME_RANDOM);
case TR::Entity::CENTAUR_STATUE : return new CentaurStatue(this, index);
case TR::Entity::CABIN : return new Cabin(this, index);
case TR::Entity::TRAP_FLAME_EMITTER : return new TrapFlameEmitter(this, index);
@@ -898,7 +949,7 @@ struct Level : IGame {
TR::Room &room = level.rooms[roomIndex];
if (!entity.isLara() && !entity.isActor())
if (!room.flags.visible || entity.flags.invisible || entity.flags.rendered)
if (!room.flags.visible || controller->flags.invisible || controller->flags.rendered)
return;
int16 lum = entity.intensity == -1 ? room.ambient : entity.intensity;
@@ -947,7 +998,7 @@ struct Level : IGame {
TR::Entity &e = level.entities[i];
if (e.type == TR::Entity::CRYSTAL) {
Crystal *crystal = (Crystal*)e.controller;
if (crystal->activeState != Controller::asActive && !level.rooms[crystal->getRoomIndex()].flags.visible)
if (crystal->flags.state != TR::Entity::asActive && !level.rooms[crystal->getRoomIndex()].flags.visible)
continue;
if (camera->frustum->isVisible(crystal->lightPos, CRYSTAL_LIGHT_RADIUS + 1024.0f)) { // 1024.0f because of vertex lighting
@@ -1078,8 +1129,9 @@ struct Level : IGame {
for (int i = 0; i < level.entitiesCount; i++) {
TR::Entity &entity = level.entities[i];
if (entity.controller && entity.flags.rendered)
((Controller*)entity.controller)->renderShadow(mesh);
Controller *controller = (Controller*)entity.controller;
if (controller && controller->flags.rendered)
controller->renderShadow(mesh);
}
}
@@ -1208,8 +1260,11 @@ struct Level : IGame {
// clear visibility flag for rooms
if (Core::pass != Core::passAmbient)
for (int i = 0; i < level.entitiesCount; i++)
level.entities[i].flags.rendered = false;
for (int i = 0; i < level.entitiesCount; i++) {
Controller *controller = (Controller*)level.entities[i].controller;
if (controller)
controller->flags.rendered = false;
}
if (water) {
Core::setTarget(NULL, !Core::settings.detail.stereo); // render to back buffer

View File

@@ -27,18 +27,6 @@ struct Sprite : Controller {
}
}
static int add(IGame *game, TR::Entity::Type type, int room, int x, int y, int z, int frame = -1, bool empty = false) {
TR::Level *level = game->getLevel();
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 = empty ? NULL : new Sprite(game, index, true, frame);
if (level->entities[index].controller)
((Controller*)level->entities[index].controller)->activate();
}
return index;
}
TR::SpriteSequence& getSequence() {
return level->spriteSequences[-(getEntity().modelIndex + 1)];
}

View File

@@ -15,10 +15,10 @@ struct Switch : Controller {
Switch(IGame *game, int entity) : Controller(game, entity) {}
bool setTimer(float t) {
if (activeState == asInactive) {
if (flags.state == TR::Entity::asInactive) {
if (state == STATE_DOWN && t > 0.0f) {
timer = t;
activeState = asActive;
flags.state = TR::Entity::asActive;
} else
deactivate(true);
return true;
@@ -36,7 +36,7 @@ struct Switch : Controller {
virtual void update() {
updateAnimation(true);
getEntity().flags.active = TR::ACTIVE;
flags.active = TR::ACTIVE;
if (!isActive())
animation.setState(STATE_UP);
}
@@ -71,23 +71,17 @@ struct Dart : Controller {
virtual void update() {
velocity = dir * animation.getSpeed();
pos = pos + velocity * (Core::deltaTime * 30.0f);
updateEntity();
Controller *lara = (Controller*)level->laraController;
if (armed && collide(lara)) {
Sprite::add(game, TR::Entity::BLOOD, getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z, Sprite::FRAME_ANIMATED);
lara->hit(DART_DAMAGE, this);
lara->hit(DART_DAMAGE, this, TR::HIT_DART);
armed = false;
}
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), (int)pos.x, (int)pos.y, (int)pos.z, info);
getFloorInfo(getRoomIndex(), pos, info);
if (pos.y > info.floor || pos.y < info.ceiling || !insideRoom(pos, getRoomIndex())) {
TR::Entity &e = getEntity();
vec3 p = pos - dir * 64.0f; // wall offset = 64
Sprite::add(game, TR::Entity::RICOCHET, e.room, (int)p.x, (int)p.y, (int)p.z, Sprite::FRAME_RANDOM);
game->addEntity(TR::Entity::RICOCHET, getRoomIndex(), pos - dir * 64.0f); // with wall offset
level->entityRemove(entity);
delete this;
}
@@ -109,12 +103,10 @@ struct TrapDartEmitter : Controller {
ASSERT(false);
if (state == STATE_FIRE && animation.framePrev == -1) {
TR::Entity &entity = getEntity();
vec3 p = pos + vec3(0.0f, -512.0f, 256.0f).rotateY(PI - angle.y);
vec3 p = pos + vec3(0.0f, -512.0f, 256.0f).rotateY(PI - entity.rotation);
game->addEntity(TR::Entity::DART, getRoomIndex(), p, entity.rotation);
Sprite::add(game, TR::Entity::SMOKE, entity.room, (int)p.x, (int)p.y, (int)p.z);
game->addEntity(TR::Entity::DART, getRoomIndex(), p, angle.y);
game->addEntity(TR::Entity::SMOKE, getRoomIndex(), p);
game->playSound(TR::SND_DART, p, Sound::Flags::PAN);
}
@@ -134,7 +126,7 @@ struct Flame : Sprite {
int roomIndex = controller->getRoomIndex();
vec3 pos = controller->pos;
int index = level->entityAdd(TR::Entity::FLAME, roomIndex, int(pos.x), int(pos.y), int(pos.z), 0, 0);
int index = level->entityAdd(TR::Entity::FLAME, roomIndex, pos, 0, 0);
if (index > -1) {
flame = new Flame(game, index, jointIndex);
level->entities[index].controller = flame;
@@ -228,7 +220,7 @@ struct LavaParticle : Sprite {
Sprite::update();
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info);
getFloorInfo(getRoomIndex(), pos, info);
bool hit = false;
if (!bounces) {
@@ -243,10 +235,10 @@ struct LavaParticle : Sprite {
delete this;
return;
}
getEntity().intensity = bounces * 2048 - 1;
if (pos.y > info.floor) pos.y = float(info.floor);
if (pos.y < info.ceiling) pos.y = float(info.ceiling);
// getEntity().intensity = bounces * 2048 - 1; // TODO: updateEntity()
if (pos.y > info.floor) pos.y = info.floor;
if (pos.y < info.ceiling) pos.y = info.ceiling;
velocity = velocity.reflect(vec3(0, 1, 0)) * 0.5f;
}
@@ -288,10 +280,8 @@ struct TrapBoulder : Controller {
TrapBoulder(IGame *game, int entity) : Controller(game, entity), velocity(0) {}
virtual void update() {
if (activeState != asActive) return;
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info);
getFloorInfo(getRoomIndex(), pos, info);
vec3 dir = getDir();
@@ -299,7 +289,7 @@ struct TrapBoulder : Controller {
if (pos.y >= info.floor - 256) {
onGround = true;
pos.y = float(info.floor);
pos.y = info.floor;
velocity = dir * animation.getSpeed();
if (state != STATE_ROLL)
animation.setState(STATE_ROLL);
@@ -314,18 +304,18 @@ struct TrapBoulder : Controller {
pos += velocity * (30.0f * Core::deltaTime);
if (info.roomNext != TR::NO_ROOM)
getEntity().room = info.roomNext;
roomIndex = info.roomNext;
if (onGround) {
game->checkTrigger(this, true);
}
vec3 v = pos + getDir() * 512.0f;
level->getFloorInfo(getRoomIndex(), int(v.x), int(v.y), int(v.z), info);
getFloorInfo(getRoomIndex(), v, info);
if (pos.y > info.floor) {
if (onGround) {
pos = p;
deactivate();
deactivate(true);
return;
} else {
pos.x = p.x;
@@ -334,7 +324,6 @@ struct TrapBoulder : Controller {
}
}
Character *lara = (Character*)level->laraController;
if (lara->health > 0.0f && collide(lara)) {
if (lara->stand == Character::STAND_GROUND)
@@ -344,7 +333,6 @@ struct TrapBoulder : Controller {
}
updateAnimation(true);
updateEntity();
}
};
@@ -362,47 +350,50 @@ struct Block : Controller {
updateFloor(true);
}
virtual void setSaveData(const SaveData &data) {
updateFloor(false);
Controller::setSaveData(data);
if (state == STATE_STAND)
updateFloor(true);
}
void updateFloor(bool rise) {
TR::Entity &e = getEntity();
TR::Level::FloorInfo info;
level->getFloorInfo(e.room, e.x, e.y, e.z, info);
getFloorInfo(getRoomIndex(), pos, info);
if (info.roomNext != 0xFF)
e.room = info.roomNext;
roomIndex = info.roomNext;
int dx, dz;
TR::Room::Sector &s = level->getSector(e.room, e.x, e.z, dx, dz);
TR::Room::Sector &s = level->getSector(getRoomIndex(), int(pos.x), int(pos.z), dx, dz);
s.floor += rise ? -4 : 4;
}
bool doMove(bool push) {
// check floor height of next floor
vec3 dir = getDir() * (push ? 1024.0f : -1024.0f);
TR::Entity &e = getEntity();
TR::Level::FloorInfo info;
int px = e.x + (int)dir.x;
int pz = e.z + (int)dir.z;
level->getFloorInfo(e.room, px, e.y, pz, info);
vec3 p = pos + dir;
getFloorInfo(getRoomIndex(), p, info);
if ((info.slantX | info.slantZ) || info.floor != e.y || info.floor - info.ceiling < 1024)
if ((info.slantX | info.slantZ) || info.floor != pos.y || info.floor - info.ceiling < 1024)
return false;
// check for trapdoor
px /= 1024;
pz /= 1024;
int px = int(p.x) / 1024;
int pz = int(p.z) / 1024;
for (int i = 0; i < info.trigCmdCount; i++)
if (info.trigCmd[i].action == TR::Action::ACTIVATE) {
TR::Entity &obj = level->entities[info.trigCmd[i].args];
if ((obj.type == TR::Entity::TRAP_DOOR_1 || obj.type == TR::Entity::TRAP_DOOR_2) && px == obj.x / 1024 && pz == obj.z / 1024)
TR::Entity &e = level->entities[info.trigCmd[i].args];
vec3 objPos = ((Controller*)e.controller)->pos;
if ((e.type == TR::Entity::TRAP_DOOR_1 || e.type == TR::Entity::TRAP_DOOR_2) && px == int(objPos.x) / 1024 && pz == int(objPos.z) / 1024)
return false;
}
// check Laras destination position
if (!push) {
dir = getDir() * (-2048.0f);
px = e.x + (int)dir.x;
pz = e.z + (int)dir.z;
level->getFloorInfo(e.room, px, e.y, pz, info);
if ((info.slantX | info.slantZ) || info.floor != e.y || info.floor - info.ceiling < 1024)
getFloorInfo(getRoomIndex(), pos + dir, info);
if ((info.slantX | info.slantZ) || info.floor != pos.y || info.floor - info.ceiling < 1024)
return false;
}
@@ -414,30 +405,27 @@ struct Block : Controller {
}
virtual void update() {
TR::Entity &e = getEntity();
TR::Level::FloorInfo info;
level->getFloorInfo(e.room, e.x, e.y, e.z, info);
getFloorInfo(getRoomIndex(), pos, info);
if (pos.y < info.floor) {
if (info.roomBelow != TR::NO_ROOM)
e.room = info.roomBelow;
roomIndex = info.roomBelow;
velocity += Core::deltaTime * GRAVITY;
pos.y += Core::deltaTime * velocity * 30.0f;
if (pos.y >= info.floor) {
velocity = 0.0f;
pos.y = float(info.floor);
pos.y = info.floor;
game->setEffect(this, TR::Effect::FLOOR_SHAKE);
game->playSound(TR::SND_BOULDER, pos, Sound::PAN);
deactivate(true);
updateFloor(true);
}
updateEntity();
} else {
if (state == STATE_STAND) return;
updateAnimation(true);
if (state == STATE_STAND) {
updateEntity();
updateFloor(true);
deactivate();
game->checkTrigger(this, true);
@@ -456,19 +444,17 @@ struct MovingBlock : Controller {
};
MovingBlock(IGame *game, int entity) : Controller(game, entity) {
if (!getEntity().flags.invisible)
if (!flags.invisible)
updateFloor(true);
}
void updateFloor(bool rise) {
updateEntity();
TR::Entity &e = getEntity();
TR::Level::FloorInfo info;
level->getFloorInfo(e.room, e.x, e.y, e.z, info);
getFloorInfo(getRoomIndex(), pos, info);
if (info.roomNext != 0xFF)
e.room = info.roomNext;
roomIndex = info.roomNext;
int dx, dz;
TR::Room::Sector &s = level->getSector(e.room, e.x, e.z, dx, dz);
TR::Room::Sector &s = level->getSector(getRoomIndex(), int(pos.x), int(pos.z), dx, dz);
s.floor += rise ? -8 : 8;
}
@@ -487,9 +473,9 @@ struct MovingBlock : Controller {
}
}
if (activeState == asInactive) {
if (getEntity().flags.active == TR::ACTIVE)
activeState = asActive; // stay in active items list
if (flags.state == TR::Entity::asInactive) {
if (flags.active == TR::ACTIVE)
flags.state = TR::Entity::asActive; // stay in active items list
pos.x = int(pos.x / 1024.0f) * 1024.0f + 512.0f;
pos.z = int(pos.z / 1024.0f) * 1024.0f + 512.0f;
updateFloor(true);
@@ -572,10 +558,9 @@ struct Door : Controller {
} block[2];
Door(IGame *game, int entity) : Controller(game, entity) {
TR::Entity &e = getEntity();
vec3 p = pos - getDir() * 1024.0f;
block[0] = BlockInfo(level, e.room, e.x, e.z, int(p.x), int(p.z), false);
block[1] = BlockInfo(level, e.room, e.x, e.z, int(p.x), int(p.z), true);
block[0] = BlockInfo(level, getRoomIndex(), int(pos.x), int(pos.z), int(p.x), int(p.z), false);
block[1] = BlockInfo(level, getRoomIndex(), int(pos.x), int(pos.z), int(p.x), int(p.z), true);
updateBlock(false);
}
@@ -607,7 +592,7 @@ struct TrapDoor : Controller {
};
TrapDoor(IGame *game, int entity) : Controller(game, entity) {
getEntity().flags.collision = true;
flags.collision = true;
}
virtual void update() {
@@ -615,7 +600,7 @@ struct TrapDoor : Controller {
int targetState = isActive() ? STATE_OPEN : STATE_CLOSE;
if (state == targetState)
getEntity().flags.collision = targetState == STATE_CLOSE;
flags.collision = targetState == STATE_CLOSE;
else
animation.setState(targetState);
}
@@ -632,14 +617,13 @@ struct TrapFloor : Controller {
float speed;
TrapFloor(IGame *game, int entity) : Controller(game, entity), speed(0) {
getEntity().flags.collision = true;
flags.collision = true;
}
virtual bool activate() {
if (state != STATE_STATIC) return false;
TR::Entity &e = ((Controller*)level->laraController)->getEntity();
int ey = getEntity().y - 512; // real floor object position
if (abs(e.y - ey) <= 8 && Controller::activate()) {
vec3 &p = ((Controller*)level->laraController)->pos;
if (abs(p.y - (pos.y - 512.0f)) <= 8 && Controller::activate()) {
animation.setState(STATE_SHAKE);
return true;
}
@@ -649,28 +633,27 @@ struct TrapFloor : Controller {
virtual void update() {
updateAnimation(true);
if (state == STATE_FALL) {
getEntity().flags.collision = false;
flags.collision = false;
speed += GRAVITY * 30 * Core::deltaTime;
pos.y += speed * Core::deltaTime;
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info);
getFloorInfo(getRoomIndex(), pos, info);
if (pos.y > info.roomFloor && info.roomBelow != 0xFF)
getEntity().room = info.roomBelow;
roomIndex = info.roomBelow;
if (pos.y > info.floor) {
pos.y = (float)info.floor;
animation.setState(STATE_DOWN);
}
updateEntity();
}
}
};
struct Bridge : Controller {
Bridge(IGame *game, int entity) : Controller(game, entity) {
getEntity().flags.collision = true;
flags.collision = true;
}
};
@@ -681,7 +664,7 @@ struct Drawbridge : Controller {
};
Drawbridge(IGame *game, int entity) : Controller(game, entity) {
getEntity().flags.collision = true;
flags.collision = true;
}
virtual void update() {
@@ -803,16 +786,15 @@ struct TrapCeiling : Controller {
pos.y += speed * Core::deltaTime;
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info);
getFloorInfo(getRoomIndex(), pos, info);
if (pos.y > info.roomFloor && info.roomBelow != 0xFF)
getEntity().room = info.roomBelow;
roomIndex = info.roomBelow;
if (pos.y > info.floor) {
pos.y = (float)info.floor;
animation.setState(STATE_DOWN);
}
updateEntity();
Controller *lara = (Controller*)level->laraController;
if (collide(lara))
@@ -866,7 +848,7 @@ struct TrapSword : Controller {
virtual void update() {
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info);
getFloorInfo(getRoomIndex(), pos, info);
Controller *lara = game->getLara();
@@ -881,12 +863,11 @@ struct TrapSword : Controller {
angle.y += rot * Core::deltaTime;
applyGravity(dir.y);
pos += dir * (30.0f * Core::deltaTime);
if (pos.y > float(info.floor)) {
pos.y = float(info.floor);
if (pos.y > info.floor) {
pos.y = info.floor;
game->playSound(TR::SND_SWORD, pos, Sound::PAN);
deactivate(true);
}
updateEntity();
if (collide(lara))
lara->hit(SWORD_DAMAGE * 30.0f * Core::deltaTime, this, TR::HIT_SWORD); // TODO: push lara
@@ -906,13 +887,12 @@ struct ThorHammer : Controller {
Controller *block;
ThorHammer(IGame *game, int entity) : Controller(game, entity) {
TR::Entity &e = getEntity();
int index = level->entityAdd(TR::Entity::HAMMER_BLOCK, getRoomIndex(), e.x, e.y, e.z, e.rotation, -1);
int index = level->entityAdd(TR::Entity::HAMMER_BLOCK, getRoomIndex(), pos, angle.y, -1);
if (index > -1) {
block = new Controller(game, index);
level->entities[index].controller = block;
}
e.flags.collision = block->getEntity().flags.collision = false;
flags.collision = block->flags.collision = false;
}
virtual void update() {
@@ -932,7 +912,7 @@ struct ThorHammer : Controller {
case STATE_DOWN : {
game->checkTrigger(this, 1);
if (lara->health > 0.0f)
getEntity().flags.collision = block->getEntity().flags.collision = true;
flags.collision = block->flags.collision = true;
deactivate(true);
break;
}
@@ -1153,12 +1133,10 @@ struct TrapLava : Controller {
vec3 dir = getDir();
pos += dir * (25.0f * 30.0f * Core::deltaTime);
updateEntity();
int roomIndex = getRoomIndex();
roomIndex = getRoomIndex();
TR::Room::Sector *s = level->getSector(roomIndex, int(pos.x + dir.x * 2048.0f), int(pos.y), int(pos.z + dir.z * 2048.0f));
if (!s || s->floor * 256 != int(pos.y))
done = true;
getEntity().room = roomIndex;
}
};
@@ -1170,7 +1148,7 @@ struct MovingObject : Controller {
};
MovingObject(IGame *game, int entity) : Controller(game, entity) {
getEntity().flags.collision = true;
flags.collision = true;
}
virtual void update() {
@@ -1179,10 +1157,9 @@ struct MovingObject : Controller {
pos += getDir() * (animation.getSpeed() * Core::deltaTime * 30.0f);
TR::Level::FloorInfo info;
level->getFloorInfo(getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), info);
getFloorInfo(getRoomIndex(), pos, info);
if (info.roomNext != TR::NO_ROOM)
getEntity().room = info.roomNext;
updateEntity();
roomIndex = info.roomNext;
}
};
@@ -1224,16 +1201,14 @@ struct Cabin : Controller {
Cabin(IGame *game, int entity) : Controller(game, entity) {}
virtual void update() {
TR::Entity &e = getEntity();
if (e.flags.active == TR::ACTIVE) {
if (flags.active == TR::ACTIVE) {
if (state >= STATE_UP && state <= STATE_DOWN_2)
animation.setState(state + 1);
e.flags.active = 0;
flags.active = 0;
}
if (state == STATE_GROUND) {
e.flags.invisible = true;
flags.invisible = true;
level->flipmap[3].active = TR::ACTIVE;
level->isFlipped = !level->isFlipped;
deactivate(true);
@@ -1257,7 +1232,7 @@ struct Boat : Controller {
switch (state) {
case STATE_IDLE : animation.setState(STATE_MOVE); break;
case STATE_MOVE : animation.setState(STATE_STOP); break;
case STATE_STOP : deactivate(true); getEntity().flags.invisible = true; break;
case STATE_STOP : deactivate(true); flags.invisible = true; break;
}
updateAnimation(true);
pos = pos + getDir() * (animation.getSpeed() * Core::deltaTime * 30.0f);
@@ -1279,7 +1254,7 @@ struct MutantEgg : Controller {
initMeshOverrides();
layers[0].mask = 0xff0001ff; // hide dynamic meshes
switch (getEntity().flags.active) {
switch (flags.active) {
case 1 : enemy = TR::Entity::ENEMY_MUTANT_2; break;
case 2 : enemy = TR::Entity::ENEMY_CENTAUR; break;
case 4 : enemy = TR::Entity::ENEMY_GIANT_MUTANT; break;
@@ -1291,8 +1266,7 @@ struct MutantEgg : Controller {
virtual void update() {
if (state != STATE_EXPLOSION) {
Box box = Box(pos + vec3(-MUTANT_EGG_RANGE), pos + vec3(MUTANT_EGG_RANGE));
TR::Entity &e = getEntity();
if ( e.flags.once || e.type == TR::Entity::MUTANT_EGG_BIG || box.contains((((Controller*)level->laraController)->pos)) ) {
if ( flags.once || getEntity().type == TR::Entity::MUTANT_EGG_BIG || box.contains((((Controller*)level->laraController)->pos)) ) {
animation.setState(STATE_EXPLOSION);
layers[0].mask = 0xffffffff & ~(1 << 24);
explode(0x00fffe00);
@@ -1301,6 +1275,11 @@ struct MutantEgg : Controller {
}
Controller::update();
}
virtual void setSaveData(const SaveData &data) {
Controller::setSaveData(data);
visibleMask = (state == STATE_IDLE) ? 0xff0001ff : (0xffffffff & ~(1 << 24));
}
};
@@ -1309,7 +1288,7 @@ struct KeyHole : Controller {
virtual bool activate() {
if (!Controller::activate()) return false;
getEntity().flags.active = TR::ACTIVE;
flags.active = TR::ACTIVE;
if (getEntity().isPuzzleHole()) {
int doneIdx = TR::Entity::convToInv(TR::Entity::getItemForHole(getEntity().type)) - TR::Entity::INV_PUZZLE_1;
meshSwap(0, level->extra.puzzleDone[doneIdx]);
@@ -1373,8 +1352,7 @@ struct Waterfall : Controller {
vec2 p = (vec2(randf(), randf()) * 2.0f - 1.0f) * (512.0f - dropRadius);
vec3 dropPos = pos + vec3(p.x, 0.0f, p.y);
game->waterDrop(dropPos, dropRadius, dropStrength);
Sprite::add(game, TR::Entity::WATER_SPLASH, getRoomIndex(), (int)dropPos.x, (int)dropPos.y, (int)dropPos.z);
game->addEntity(TR::Entity::WATER_SPLASH, getRoomIndex(), dropPos);
}
#undef SPLASH_TIMESTEP
@@ -1386,16 +1364,15 @@ struct Bubble : Sprite {
Bubble(IGame *game, int entity) : Sprite(game, entity, true, Sprite::FRAME_RANDOM) {
speed = (10.0f + randf() * 6.0f) * 30.0f;
// get water height => bubble life time
TR::Entity &e = getEntity();
int dx, dz;
int room = getRoomIndex();
int h = e.y;
int h = int(pos.y);
while (room != TR::NO_ROOM && level->rooms[room].flags.water) {
TR::Room::Sector &s = level->getSector(room, e.x, e.z, dx, dz);
TR::Room::Sector &s = level->getSector(room, int(pos.x), int(pos.z), dx, dz);
h = s.ceiling * 256;
room = s.roomAbove;
}
time -= (e.y - h) / speed - (1.0f / SPRITE_FPS);
time -= (pos.y - h) / speed - (1.0f / SPRITE_FPS);
activate();
}
@@ -1409,9 +1386,17 @@ struct Bubble : Sprite {
angle.y += 30.0f * 9.0f * DEG2RAD * Core::deltaTime;
pos.x += sinf(angle.y) * (11.0f * 30.0f * Core::deltaTime);
pos.z += cosf(angle.x) * (8.0f * 30.0f * Core::deltaTime);
updateEntity();
Sprite::update();
}
};
struct Explosion : Sprite {
Explosion(IGame *game, int entity) : Sprite(game, entity, true, Sprite::FRAME_ANIMATED) {
game->playSound(TR::SND_EXPLOSION, pos, 0);
}
};
#endif