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:
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
|
407
src/controller.h
407
src/controller.h
@@ -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;
|
||||
|
78
src/debug.h
78
src/debug.h
@@ -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);
|
||||
|
||||
|
84
src/enemy.h
84
src/enemy.h
@@ -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;
|
||||
}
|
||||
|
||||
|
248
src/format.h
248
src/format.h
@@ -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
|
||||
}
|
||||
|
||||
|
10
src/game.h
10
src/game.h
@@ -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();
|
||||
|
||||
|
232
src/lara.h
232
src/lara.h
@@ -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;
|
||||
|
89
src/level.h
89
src/level.h
@@ -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
|
||||
|
12
src/sprite.h
12
src/sprite.h
@@ -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)];
|
||||
}
|
||||
|
213
src/trigger.h
213
src/trigger.h
@@ -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
|
Reference in New Issue
Block a user