mirror of
https://github.com/XProger/OpenLara.git
synced 2025-04-22 03:51:58 +02:00
#14 Rats AI; #23 flipmap and water surfaces rework, fix rooms visibility in cutscenes, fix water reflection for additive blended geometry
This commit is contained in:
parent
a89e3f8beb
commit
1c0fde576d
@ -22,19 +22,42 @@ struct Animation {
|
||||
|
||||
Animation() : overrides(NULL) {}
|
||||
|
||||
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);
|
||||
Animation(TR::Level *level, const TR::Model *model) : level(level), overrides(NULL), overrideMask(0) {
|
||||
setModel(model);
|
||||
}
|
||||
|
||||
~Animation() {
|
||||
delete[] overrides;
|
||||
}
|
||||
|
||||
void setModel(const TR::Model *model) {
|
||||
if (this->model == model)
|
||||
return;
|
||||
|
||||
this->model = model;
|
||||
anims = model ? &level->anims[model->animation] : NULL;
|
||||
time = 0;
|
||||
delta = 0;
|
||||
dir = 1.0f;
|
||||
index = -1;
|
||||
prev = 0;
|
||||
next = 0;
|
||||
overrideMask = 0;
|
||||
|
||||
if (overrides) {
|
||||
delete[] overrides;
|
||||
overrides = NULL;
|
||||
initOverrides();
|
||||
}
|
||||
|
||||
if (anims) setAnim(0);
|
||||
}
|
||||
|
||||
inline operator TR::Animation* () const { return anims + index; }
|
||||
|
||||
void initOverrides() {
|
||||
ASSERT(model);
|
||||
ASSERT(!overrides)
|
||||
overrides = new quat[model->mCount];
|
||||
overrideMask = 0;
|
||||
}
|
||||
@ -79,6 +102,10 @@ struct Animation {
|
||||
return (TR::AnimFrame*)&level->frameData[anim->frameOffset / 2 + index * frameSize]; // >> 1 (div 2) because frameData is array of shorts
|
||||
}
|
||||
|
||||
void goEnd(bool lerpToNext = true) {
|
||||
setAnim(index, -(framesCount - 1), lerpToNext);
|
||||
}
|
||||
|
||||
void updateInfo() {
|
||||
ASSERT(model);
|
||||
ASSERT(anims);
|
||||
|
73
src/cache.h
73
src/cache.h
@ -230,7 +230,7 @@ struct AmbientCache {
|
||||
enum int32 {
|
||||
BLANK, WAIT, READY
|
||||
} status;
|
||||
vec3 colors[6];
|
||||
vec3 colors[6]; // TODO: ubyte4[6]
|
||||
} *items;
|
||||
int *offsets;
|
||||
|
||||
@ -251,7 +251,7 @@ struct AmbientCache {
|
||||
for (int i = 0; i < level->roomsCount; i++) {
|
||||
TR::Room &r = level->rooms[i];
|
||||
offsets[i] = sectors;
|
||||
sectors += r.xSectors * r.zSectors;
|
||||
sectors += r.xSectors * r.zSectors * (r.alternateRoom > -1 ? 2 : 1); // x2 for flipped rooms
|
||||
}
|
||||
// init cache buffer
|
||||
items = new Cube[sectors];
|
||||
@ -270,11 +270,11 @@ struct AmbientCache {
|
||||
}
|
||||
|
||||
void addTask(int room, int sector) {
|
||||
if (tasksCount >= 32) return;
|
||||
if (tasksCount >= COUNT(tasks)) return;
|
||||
|
||||
Task &task = tasks[tasksCount++];
|
||||
task.room = room;
|
||||
task.flip = level->state.flags.flipped;
|
||||
task.flip = level->state.flags.flipped && level->rooms[room].alternateRoom > -1;
|
||||
task.sector = sector;
|
||||
task.cube = &items[offsets[room] + sector];
|
||||
task.cube->status = Cube::WAIT;
|
||||
@ -326,20 +326,33 @@ struct AmbientCache {
|
||||
for (int i = 0; i < tasksCount; i++) {
|
||||
Task &task = tasks[i];
|
||||
|
||||
bool oldFlip = level->state.flags.flipped;
|
||||
level->state.flags.flipped = task.flip != 0;
|
||||
renderAmbient(task.room, task.sector, &task.cube->colors[0]);
|
||||
level->state.flags.flipped = oldFlip;
|
||||
bool needFlip = task.flip != level->state.flags.flipped;
|
||||
|
||||
if (needFlip) game->flipMap();
|
||||
|
||||
int sector = task.sector;
|
||||
if (task.flip) {
|
||||
TR::Room &r = level->rooms[task.room];
|
||||
sector -= r.xSectors * r.zSectors;
|
||||
}
|
||||
|
||||
renderAmbient(task.room, sector, &task.cube->colors[0]);
|
||||
if (needFlip) game->flipMap();
|
||||
|
||||
task.cube->status = Cube::READY;
|
||||
}
|
||||
tasksCount = 0;
|
||||
}
|
||||
|
||||
Cube* getAmbient(int room, int sector) {
|
||||
Cube *cube = &items[offsets[room] + sector];
|
||||
Cube* getAmbient(int roomIndex, int sector) {
|
||||
TR::Room &r = level->rooms[roomIndex];
|
||||
if (level->state.flags.flipped && r.alternateRoom > -1)
|
||||
sector += r.xSectors * r.zSectors;
|
||||
|
||||
Cube *cube = &items[offsets[roomIndex] + sector];
|
||||
if (cube->status == Cube::BLANK)
|
||||
addTask(room, sector);
|
||||
addTask(roomIndex, sector);
|
||||
|
||||
return cube->status == Cube::READY ? cube : NULL;
|
||||
}
|
||||
|
||||
@ -373,6 +386,8 @@ struct WaterCache {
|
||||
struct Item {
|
||||
int from, to, caust;
|
||||
float timer;
|
||||
float waterLevel;
|
||||
bool flip;
|
||||
bool visible;
|
||||
bool blank;
|
||||
vec3 pos, size;
|
||||
@ -395,8 +410,16 @@ struct WaterCache {
|
||||
TR::Level *level = game->getLevel();
|
||||
TR::Room &r = level->rooms[to]; // underwater room
|
||||
ASSERT(r.flags.water);
|
||||
int minX = r.xSectors, minZ = r.zSectors, maxX = 0, maxZ = 0, posY = level->rooms[to].waterLevel, caustY = posY;
|
||||
int minX = r.xSectors, minZ = r.zSectors, maxX = 0, maxZ = 0;
|
||||
|
||||
int posY = level->rooms[to].waterLevel;
|
||||
if (posY == -1)
|
||||
posY = level->rooms[from].waterLevel;
|
||||
|
||||
ASSERT(posY != -1); // underwater room without reaching the surface
|
||||
|
||||
int caustY = posY;
|
||||
|
||||
for (int z = 0; z < r.zSectors; z++)
|
||||
for (int x = 0; x < r.xSectors; x++) {
|
||||
TR::Room::Sector &s = r.sectors[x * r.zSectors + z];
|
||||
@ -411,8 +434,6 @@ struct WaterCache {
|
||||
if (floor > caustY) {
|
||||
caustY = floor;
|
||||
caust = caustRoom;
|
||||
if (level->state.flags.flipped && level->rooms[caust].alternateRoom > -1)
|
||||
caust = level->rooms[caust].alternateRoom;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -516,6 +537,20 @@ struct WaterCache {
|
||||
visible = 0;
|
||||
}
|
||||
|
||||
void flipMap() {
|
||||
for (int i = 0; i < level->roomsCount && count; i++)
|
||||
if (level->rooms[i].alternateRoom > -1) {
|
||||
int j = 0;
|
||||
while (j < count) {
|
||||
if (items[j].from == i || items[j].to == i) {
|
||||
items[j].deinit();
|
||||
items[j] = items[--count];
|
||||
} else
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setVisible(int roomIndex, int nextRoom = TR::NO_ROOM) {
|
||||
if (nextRoom == TR::NO_ROOM) { // setVisible(underwaterRoom) for caustics update
|
||||
for (int i = 0; i < count; i++)
|
||||
@ -539,6 +574,9 @@ struct WaterCache {
|
||||
to = nextRoom;
|
||||
}
|
||||
|
||||
if (level->rooms[to].waterLevel == -1 && level->rooms[from].waterLevel == -1) // not have water surface
|
||||
return;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
Item &item = items[i];
|
||||
if (item.from == from && item.to == to) {
|
||||
@ -735,6 +773,11 @@ struct WaterCache {
|
||||
item.init(game);
|
||||
}
|
||||
|
||||
if (!count) {
|
||||
visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// render mirror reflection
|
||||
Core::setTarget(reflect, CLEAR_ALL);
|
||||
Camera *camera = (Camera*)game->getCamera();
|
||||
@ -754,7 +797,7 @@ struct WaterCache {
|
||||
vec4 reflectPlane = vec4(n.x, n.y, n.z, -n.dot(p));
|
||||
bool underwater = level->rooms[camera->getRoomIndex()].flags.water;
|
||||
|
||||
//bool underwater = level->camera->pos.y > item.pos.y;
|
||||
//bool underwater = camera->eye.pos.y > item.pos.y;
|
||||
|
||||
camera->reflectPlane = &reflectPlane;
|
||||
camera->setup(true);
|
||||
|
40
src/camera.h
40
src/camera.h
@ -65,43 +65,7 @@ struct Camera : ICamera {
|
||||
}
|
||||
|
||||
virtual int getRoomIndex() const {
|
||||
return (level->state.flags.flipped && level->rooms[eye.room].alternateRoom > -1) ? level->rooms[eye.room].alternateRoom : eye.room;
|
||||
}
|
||||
|
||||
virtual void checkRoom() {
|
||||
// level->getSector(eye.room, eye.pos);
|
||||
// return;
|
||||
|
||||
if (mode == MODE_CUTSCENE) {
|
||||
for (int i = 0; i < level->roomsCount; i++)
|
||||
if (owner->insideRoom(eye.pos, i)) {
|
||||
eye.room = i;
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
TR::Level::FloorInfo info;
|
||||
owner->getFloorInfo(getRoomIndex(), eye.pos, info);
|
||||
|
||||
if (info.roomNext != TR::NO_ROOM)
|
||||
eye.room = info.roomNext;
|
||||
|
||||
if (eye.pos.y < info.roomCeiling) {
|
||||
if (info.roomAbove != TR::NO_ROOM)
|
||||
eye.room = info.roomAbove;
|
||||
else
|
||||
if (info.roomCeiling != 0xffff8100)
|
||||
eye.pos.y = (float)info.roomCeiling;
|
||||
}
|
||||
|
||||
if (eye.pos.y > info.roomFloor) {
|
||||
if (info.roomBelow != TR::NO_ROOM)
|
||||
eye.room = info.roomBelow;
|
||||
else
|
||||
if (info.roomFloor != 0xffff8100)
|
||||
eye.pos.y = (float)info.roomFloor;
|
||||
}
|
||||
return eye.room;
|
||||
}
|
||||
|
||||
void updateListener() {
|
||||
@ -387,7 +351,7 @@ struct Camera : ICamera {
|
||||
} else
|
||||
updateFirstPerson();
|
||||
|
||||
checkRoom();
|
||||
level->getSector(eye.room, eye.pos);
|
||||
} else {
|
||||
Controller *lookAt = NULL;
|
||||
|
||||
|
@ -126,8 +126,6 @@ struct Character : Controller {
|
||||
|
||||
if (info.roomAbove != TR::NO_ROOM && pos.y <= info.roomCeiling) {
|
||||
TR::Room *room = &level->rooms[info.roomAbove];
|
||||
if (level->state.flags.flipped && room->alternateRoom > -1)
|
||||
room = &level->rooms[room->alternateRoom];
|
||||
|
||||
if (stand == STAND_UNDERWATER && !room->flags.water) {
|
||||
stand = STAND_ONWATER;
|
||||
|
@ -64,6 +64,7 @@ struct IGame {
|
||||
virtual bool isCutscene() { return false; }
|
||||
virtual uint16 getRandomBox(uint16 zone, uint16 *zones) { return 0; }
|
||||
virtual uint16 findPath(int ascend, int descend, bool big, int boxStart, int boxEnd, uint16 *zones, uint16 **boxes) { return 0; }
|
||||
virtual void flipMap() {}
|
||||
virtual void setClipParams(float clipSign, float clipHeight) {}
|
||||
virtual void setWaterParams(float height) {}
|
||||
virtual void waterDrop(const vec3 &pos, float radius, float strength) {}
|
||||
@ -124,6 +125,7 @@ struct Controller {
|
||||
TR::Room::Light *targetLight;
|
||||
vec3 mainLightPos;
|
||||
vec4 mainLightColor;
|
||||
bool mainLightFlip;
|
||||
|
||||
struct MeshLayer {
|
||||
uint32 model;
|
||||
@ -153,9 +155,6 @@ struct Controller {
|
||||
joints = m ? new Basis[m->mCount] : NULL;
|
||||
jointsFrame = -1;
|
||||
|
||||
if (level->isCutsceneLevel())
|
||||
fixRoomIndex();
|
||||
|
||||
specular = 0.0f;
|
||||
intensity = e.intensity == -1 ? -1.0f : intensityf(e.intensity);
|
||||
timer = 0.0f;
|
||||
@ -186,8 +185,10 @@ struct Controller {
|
||||
deactivate(true);
|
||||
}
|
||||
|
||||
bool fixRoomIndex() {
|
||||
bool fixRoomIndex() { // TODO: remove this and fix braid
|
||||
vec3 p = getPos();
|
||||
if (insideRoom(p, roomIndex))
|
||||
return true;
|
||||
for (int i = 0; i < level->roomsCount; i++)
|
||||
if (insideRoom(p, i)) {
|
||||
roomIndex = i;
|
||||
@ -619,7 +620,7 @@ struct Controller {
|
||||
pos.z >= min.z && pos.z <= max.z;
|
||||
}
|
||||
|
||||
const TR::Model* getModel() const {
|
||||
virtual const TR::Model* getModel() {
|
||||
int index = getEntity().modelIndex;
|
||||
return index > 0 ? &level->models[index - 1] : NULL;
|
||||
}
|
||||
@ -635,10 +636,7 @@ struct Controller {
|
||||
}
|
||||
|
||||
virtual int getRoomIndex() const {
|
||||
int index = roomIndex;
|
||||
if (level->state.flags.flipped && level->rooms[index].alternateRoom > -1)
|
||||
index = level->rooms[index].alternateRoom;
|
||||
return index;
|
||||
return roomIndex;
|
||||
}
|
||||
|
||||
virtual vec3 getPos() {
|
||||
@ -1002,7 +1000,7 @@ struct Controller {
|
||||
case TR::Effect::ROTATE_180 : angle.y = angle.y + PI; break;
|
||||
case TR::Effect::FLOOR_SHAKE : game->setEffect(this, TR::Effect::Type(fx)); break;
|
||||
case TR::Effect::FINISH_LEVEL : game->loadNextLevel(); break;
|
||||
case TR::Effect::FLIP_MAP : level->state.flags.flipped = !level->state.flags.flipped; break;
|
||||
case TR::Effect::FLIP_MAP : game->flipMap(); break;
|
||||
default : cmdEffect(fx); break;
|
||||
}
|
||||
} else {
|
||||
@ -1131,6 +1129,12 @@ struct Controller {
|
||||
vec3 tpos = vec3(float(targetLight->x), float(targetLight->y), float(targetLight->z));
|
||||
vec4 tcolor = vec4(vec3(targetLight->color.r, targetLight->color.g, targetLight->color.b) * (1.0f / 255.0f), float(targetLight->radius));
|
||||
|
||||
if (mainLightFlip != level->state.flags.flipped) {
|
||||
if (getRoom().alternateRoom > -1)
|
||||
lerp = false;
|
||||
mainLightFlip = level->state.flags.flipped;
|
||||
}
|
||||
|
||||
if (lerp) {
|
||||
float t = Core::deltaTime * 2.0f;
|
||||
mainLightPos = mainLightPos.lerp(tpos, t);
|
||||
|
@ -481,9 +481,9 @@ namespace Debug {
|
||||
for (int i = 0; i < level.entitiesCount; i++) {
|
||||
TR::Entity &e = level.entities[i];
|
||||
Controller *controller = (Controller*)e.controller;
|
||||
if (!controller || controller->flags.invisible) return;
|
||||
if (!controller) continue;
|
||||
|
||||
sprintf(buf, "%s (%d)", getEntityName(level, e), i);
|
||||
sprintf(buf, "%s (%d) %s", getEntityName(level, e), i, controller->flags.invisible ? "INVISIBLE" : "");
|
||||
Debug::Draw::text(controller->getPos() + randf() * 64, controller->flags.active ? vec4(0, 0, 0.8f, 1) : vec4(0.8f, 0, 0, 1), buf);
|
||||
}
|
||||
|
||||
|
186
src/enemy.h
186
src/enemy.h
@ -745,9 +745,188 @@ struct Lion : Enemy {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#define RAT_TURN_SLOW (DEG2RAD * 90)
|
||||
#define RAT_TURN_FAST (DEG2RAD * 180)
|
||||
#define RAT_DIST_BITE 341.0f
|
||||
#define RAT_DIST_ATTACK 1536.0f
|
||||
#define RAT_WAIT 0.01f
|
||||
#define RAT_DAMAGE 20
|
||||
|
||||
struct Rat : Enemy {
|
||||
Rat(IGame *game, int entity) : Enemy(game, entity, 6, 341, 375.0f, 0.25f) {
|
||||
hitSound = TR::SND_HIT_RAT;
|
||||
|
||||
enum {
|
||||
HIT_MASK = 0x300018F,
|
||||
};
|
||||
|
||||
enum {
|
||||
ANIM_GROUND_DEATH = 8,
|
||||
ANIM_WATER_DEATH = 2,
|
||||
};
|
||||
|
||||
enum {
|
||||
STATE_NONE ,
|
||||
STATE_STOP ,
|
||||
STATE_ATTACK ,
|
||||
STATE_RUN ,
|
||||
STATE_BITE ,
|
||||
STATE_DEATH ,
|
||||
STATE_WAIT ,
|
||||
STATE_WATER_SWIM = 1,
|
||||
STATE_WATER_ATTACK ,
|
||||
STATE_WATER_DEATH ,
|
||||
};
|
||||
|
||||
int modelLand, modelWater;
|
||||
|
||||
Rat(IGame *game, int entity) : Enemy(game, entity, 5, 204, 200.0f, 0.25f) {
|
||||
hitSound = TR::SND_HIT_RAT;
|
||||
jointChest = 1;
|
||||
jointHead = 2;
|
||||
|
||||
modelLand = level->getModelIndex(TR::Entity::ENEMY_RAT_LAND) - 1;
|
||||
modelWater = level->getModelIndex(TR::Entity::ENEMY_RAT_WATER) - 1;
|
||||
}
|
||||
|
||||
const virtual TR::Model* getModel() {
|
||||
bool water = getRoom().flags.water;
|
||||
int modelIndex = water ? modelWater : modelLand;
|
||||
|
||||
ASSERT(modelIndex > -1);
|
||||
const TR::Model *model = &level->models[modelIndex];
|
||||
if (animation.model != model) {
|
||||
targetBox = -1;
|
||||
animation.setModel(model);
|
||||
stand = water ? STAND_ONWATER : STAND_GROUND;
|
||||
|
||||
int16 rIndex = getRoomIndex();
|
||||
if (water) {
|
||||
TR::Room::Sector *sector = level->getWaterLevelSector(rIndex, pos);
|
||||
if (sector) {
|
||||
pos.y = float(sector->ceiling * 256);
|
||||
roomIndex = rIndex;
|
||||
}
|
||||
} else {
|
||||
int16 rIndex = getRoomIndex();
|
||||
TR::Room::Sector *sector = level->getSector(rIndex, pos);
|
||||
if (sector) {
|
||||
pos.y = float(sector->floor * 256);
|
||||
roomIndex = rIndex;
|
||||
}
|
||||
}
|
||||
|
||||
nextState = STATE_NONE;
|
||||
state = STATE_NONE;
|
||||
|
||||
if (health <= 0.0f) {
|
||||
getStateDeath();
|
||||
animation.goEnd(false);
|
||||
}
|
||||
|
||||
updateZone();
|
||||
}
|
||||
return animation.model;
|
||||
}
|
||||
|
||||
virtual int getStateGround() {
|
||||
if (!think(false))
|
||||
return state;
|
||||
|
||||
float angle;
|
||||
getTargetInfo(0, NULL, NULL, &angle, NULL);
|
||||
|
||||
if (nextState == state)
|
||||
nextState = STATE_NONE;
|
||||
|
||||
bool isBite = targetInView && fabsf(target->pos.y - pos.y) < 256.0f;
|
||||
|
||||
switch (state) {
|
||||
case STATE_STOP :
|
||||
if (nextState != STATE_NONE)
|
||||
return nextState;
|
||||
if (isBite && targetDist < RAT_DIST_BITE)
|
||||
return STATE_BITE;
|
||||
return STATE_RUN;
|
||||
case STATE_RUN :
|
||||
if (targetInView && (collide(target) & HIT_MASK))
|
||||
return STATE_STOP;
|
||||
if (isBite && targetDist < RAT_DIST_ATTACK)
|
||||
return STATE_ATTACK;
|
||||
if (targetInView && randf() < RAT_WAIT) {
|
||||
nextState = STATE_WAIT;
|
||||
return STATE_STOP;
|
||||
}
|
||||
break;
|
||||
case STATE_ATTACK :
|
||||
case STATE_BITE :
|
||||
if (nextState == STATE_NONE && targetInView && (collide(target) & HIT_MASK)) {
|
||||
bite(getJoint(jointHead).pos, RAT_DAMAGE);
|
||||
nextState = state == STATE_ATTACK ? STATE_RUN : STATE_STOP;
|
||||
}
|
||||
break;
|
||||
case STATE_WAIT :
|
||||
if (mood == MOOD_SLEEP || randf() < RAT_WAIT)
|
||||
return STATE_STOP;
|
||||
default : ;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
virtual int getStateOnwater() {
|
||||
if (!think(false))
|
||||
return state;
|
||||
|
||||
float angle;
|
||||
getTargetInfo(0, NULL, NULL, &angle, NULL);
|
||||
|
||||
if (nextState == state)
|
||||
nextState = STATE_NONE;
|
||||
|
||||
if (animation.frameIndex % 4 == 0)
|
||||
game->waterDrop(getJoint(jointHead).pos, 96.0f, 0.02f);
|
||||
|
||||
switch (state) {
|
||||
case STATE_WATER_SWIM :
|
||||
if (targetInView && (collide(target) & HIT_MASK))
|
||||
return STATE_WATER_ATTACK;
|
||||
break;
|
||||
case STATE_WATER_ATTACK :
|
||||
if (nextState == STATE_NONE && targetInView && (collide(target) & HIT_MASK)) {
|
||||
game->waterDrop(getJoint(jointHead).pos, 256.0f, 0.2f);
|
||||
bite(getJoint(jointHead).pos, RAT_DAMAGE);
|
||||
nextState = STATE_WATER_SWIM;
|
||||
}
|
||||
return STATE_NONE;
|
||||
default : ;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
virtual void updatePosition() {
|
||||
float angleY = 0.0f;
|
||||
|
||||
if ((stand == STAND_GROUND && state == STATE_RUN) || (stand == STAND_ONWATER && state == STATE_WATER_SWIM))
|
||||
getTargetInfo(0, NULL, NULL, &angleY, NULL);
|
||||
|
||||
turn(angleY, RAT_TURN_FAST);
|
||||
|
||||
if (state == STATE_DEATH) {
|
||||
animation.overrideMask = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
Enemy::updatePosition();
|
||||
setOverrides(state != STATE_DEATH, jointChest, jointHead);
|
||||
lookAt(target);
|
||||
}
|
||||
|
||||
virtual int getStateDeath() {
|
||||
bool water = getRoom().flags.water;
|
||||
if ((water && state == STATE_WATER_DEATH) || (!water && state == STATE_DEATH))
|
||||
return state;
|
||||
return animation.setAnim(water ? ANIM_WATER_DEATH : ANIM_GROUND_DEATH);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1547,8 +1726,7 @@ struct Human : Enemy {
|
||||
game->addMuzzleFlash(this, jointGun, muzzleOffset, -1);
|
||||
|
||||
if (targetDist < HUMAN_DIST_SHOT && randf() < ((HUMAN_DIST_SHOT - targetDist) / HUMAN_DIST_SHOT - 0.25f)) {
|
||||
target->hit(damage, this);
|
||||
target->addBlood(target->getJoint(rand() % target->getModel()->mCount).pos, vec3(0));
|
||||
bite(target->getJoint(rand() % target->getModel()->mCount).pos, damage);
|
||||
game->playSound(target->stand == STAND_UNDERWATER ? TR::SND_HIT_UNDERWATER : TR::SND_HIT, target->pos, Sound::PAN);
|
||||
return true;
|
||||
}
|
||||
|
45
src/format.h
45
src/format.h
@ -2884,7 +2884,7 @@ namespace TR {
|
||||
cutMatrix.rotateY(16380.0f / float(0x4000) * PI * 0.5f);
|
||||
break;
|
||||
case LVL_TR1_CUT_3 :
|
||||
state.flags.flipped = true;
|
||||
flipMap();
|
||||
case LVL_TR1_CUT_4 :
|
||||
cutMatrix.translate(vec3(float(e.x), float(e.y), float(e.z)));
|
||||
cutMatrix.rotateY(PI * 0.5f);
|
||||
@ -3766,6 +3766,18 @@ namespace TR {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void flipMap() {
|
||||
for (int i = 0; i < roomsCount; i++)
|
||||
if (rooms[i].alternateRoom > -1) {
|
||||
TR::Room &src = rooms[i];
|
||||
TR::Room &dst = rooms[src.alternateRoom];
|
||||
|
||||
swap(src, dst);
|
||||
swap(src.alternateRoom, dst.alternateRoom);
|
||||
}
|
||||
state.flags.flipped = !state.flags.flipped;
|
||||
}
|
||||
|
||||
void floorSkipCommand(FloorData* &fd, int func) {
|
||||
switch (func) {
|
||||
case FloorData::PORTAL :
|
||||
@ -3819,9 +3831,6 @@ namespace TR {
|
||||
Room::Sector& getSector(int roomIndex, int x, int z, int &dx, int &dz) const {
|
||||
ASSERT(roomIndex >= 0 && roomIndex < roomsCount);
|
||||
|
||||
if (state.flags.flipped && rooms[roomIndex].alternateRoom > -1)
|
||||
roomIndex = rooms[roomIndex].alternateRoom;
|
||||
|
||||
Room &room = rooms[roomIndex];
|
||||
|
||||
int sx = x - room.info.x;
|
||||
@ -3983,6 +3992,34 @@ namespace TR {
|
||||
return float(ceiling);
|
||||
}
|
||||
|
||||
TR::Room::Sector* getWaterLevelSector(int16 &roomIndex, const vec3 &pos) {
|
||||
int x = int(pos.x);
|
||||
int z = int(pos.z);
|
||||
|
||||
TR::Room *room = &rooms[roomIndex];
|
||||
TR::Room::Sector *sector = room->getSector((x - room->info.x) / 1024, (z - room->info.z) / 1024);
|
||||
|
||||
if (room->flags.water) { // go up to the air
|
||||
while (sector->roomAbove != NO_ROOM) {
|
||||
room = &rooms[sector->roomAbove];
|
||||
if (!room->flags.water)
|
||||
break;
|
||||
roomIndex = sector->roomAbove;
|
||||
sector = room->getSector((x - room->info.x) / 1024, (z - room->info.z) / 1024);
|
||||
}
|
||||
return sector;
|
||||
} else { // go down to the water
|
||||
while (sector->roomBelow != NO_ROOM) {
|
||||
room = &rooms[roomIndex = sector->roomBelow];
|
||||
sector = room->getSector((x - room->info.x) / 1024, (z - room->info.z) / 1024);
|
||||
if (room->flags.water)
|
||||
break;
|
||||
}
|
||||
return sector;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool isBlocked(int16 &roomIndex, const vec3 &pos) {
|
||||
Room::Sector *sector = getSector(roomIndex, pos);
|
||||
return pos.y >= getFloor(sector, pos) || pos.y <= getCeiling(sector, pos);
|
||||
|
@ -299,7 +299,7 @@ struct Lara : Character {
|
||||
delete[] basis;
|
||||
}
|
||||
|
||||
TR::Model* getModel() const {
|
||||
TR::Model* getModel() {
|
||||
return &lara->level->models[lara->level->extra.braid];
|
||||
}
|
||||
|
||||
@ -2025,7 +2025,7 @@ struct Lara : Character {
|
||||
camera->viewIndex = cameraIndex;
|
||||
|
||||
if (needFlip) {
|
||||
level->state.flags.flipped = !level->state.flags.flipped;
|
||||
game->flipMap();
|
||||
game->setEffect(this, effect);
|
||||
}
|
||||
}
|
||||
@ -2616,6 +2616,9 @@ struct Lara : Character {
|
||||
//reset(44, vec3(62976, 1536, 23040), 0);
|
||||
reset(44, vec3(62976, 1536, 23040), 0);
|
||||
break;
|
||||
case TR::LVL_TR2_PLATFORM :
|
||||
reset(16, vec3(53029, -5120, 77359), 0);
|
||||
break;
|
||||
case TR::LVL_TR3_TEMPLE :
|
||||
reset(204, vec3(40562, 3584, 58694), 0);
|
||||
break;
|
||||
|
95
src/level.h
95
src/level.h
@ -315,6 +315,32 @@ struct Level : IGame {
|
||||
return zoneCache->findPath(ascend, descend, big, boxStart, boxEnd, zones, boxes);
|
||||
}
|
||||
|
||||
void updateBlocks(bool rise) {
|
||||
for (int i = 0; i < level.entitiesBaseCount; i++) {
|
||||
Controller *controller = (Controller*)level.entities[i].controller;
|
||||
switch (level.entities[i].type) {
|
||||
case TR::Entity::BLOCK_1 :
|
||||
case TR::Entity::BLOCK_2 :
|
||||
case TR::Entity::BLOCK_3 :
|
||||
case TR::Entity::BLOCK_4 :
|
||||
((Block*)controller)->updateFloor(rise);
|
||||
break;
|
||||
case TR::Entity::MOVING_BLOCK :
|
||||
((MovingBlock*)controller)->updateFloor(rise);
|
||||
break;
|
||||
default : ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void flipMap() {
|
||||
updateBlocks(false);
|
||||
if (waterCache) waterCache->flipMap();
|
||||
mesh->flipMap();
|
||||
level.flipMap();
|
||||
updateBlocks(true);
|
||||
}
|
||||
|
||||
virtual void setClipParams(float clipSign, float clipHeight) {
|
||||
params->clipSign = clipSign;
|
||||
params->clipHeight = clipHeight;
|
||||
@ -1520,7 +1546,7 @@ struct Level : IGame {
|
||||
case 4 : if (effectTimer > 4.1f) { effectIdx++; effect = TR::Effect::NONE; } break;
|
||||
}
|
||||
if (idx != effectIdx)
|
||||
level.state.flags.flipped = !level.state.flags.flipped;
|
||||
flipMap();
|
||||
break;
|
||||
}
|
||||
case TR::Effect::EARTHQUAKE : {
|
||||
@ -1742,9 +1768,6 @@ struct Level : IGame {
|
||||
return;
|
||||
}
|
||||
|
||||
if (level.rooms[to].alternateRoom > -1 && level.state.flags.flipped)
|
||||
to = level.rooms[to].alternateRoom;
|
||||
|
||||
TR::Room &room = level.rooms[to];
|
||||
|
||||
if (!room.flags.visible) {
|
||||
@ -1770,20 +1793,39 @@ struct Level : IGame {
|
||||
waterCache->reset();
|
||||
}
|
||||
|
||||
for (int i = 0; i < level.roomsCount; i++)
|
||||
level.rooms[i].flags.visible = false;
|
||||
|
||||
int roomsList[256];
|
||||
int roomsCount = 0;
|
||||
|
||||
getVisibleRooms(roomsList, roomsCount, TR::NO_ROOM, roomIndex, vec4(-1.0f, -1.0f, 1.0f, 1.0f), water);
|
||||
/*
|
||||
if (level.isCutsceneLevel()) {
|
||||
if (level.isCutsceneLevel()) { // render all rooms except flipped
|
||||
for (int i = 0; i < level.roomsCount; i++)
|
||||
roomsList[i] = i;
|
||||
roomsCount = level.roomsCount;
|
||||
level.rooms[i].flags.visible = true;
|
||||
|
||||
for (int i = 0; i < level.roomsCount; i++) {
|
||||
int flipIndex = level.rooms[i].alternateRoom;
|
||||
if (flipIndex > 0)
|
||||
level.rooms[flipIndex].flags.visible = false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < level.roomsCount; i++)
|
||||
if (level.rooms[i].flags.visible) {
|
||||
roomsList[roomsCount++] = i;
|
||||
if (Core::pass == Core::passCompose && water && waterCache) {
|
||||
TR::Room &r = level.rooms[i];
|
||||
for (int j = 0; j < r.portalsCount; j++) {
|
||||
int to = r.portals[j].roomIndex;
|
||||
if (level.rooms[to].flags.visible && (level.rooms[to].flags.water ^ r.flags.water))
|
||||
waterCache->setVisible(i, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
for (int i = 0; i < level.roomsCount; i++)
|
||||
level.rooms[i].flags.visible = false;
|
||||
|
||||
getVisibleRooms(roomsList, roomsCount, TR::NO_ROOM, roomIndex, vec4(-1.0f, -1.0f, 1.0f, 1.0f), water);
|
||||
}
|
||||
*/
|
||||
|
||||
if (water && waterCache) {
|
||||
for (int i = 0; i < roomsCount; i++)
|
||||
waterCache->setVisible(roomsList[i]);
|
||||
@ -1817,12 +1859,6 @@ struct Level : IGame {
|
||||
// alpha blending pass
|
||||
renderRooms(roomsList, roomsCount, 1);
|
||||
renderEntities(1);
|
||||
// additive blending pass
|
||||
vec4 oldFog = Core::fogParams;
|
||||
Core::fogParams = FOG_BLACK; // don't apply fog for additive
|
||||
renderRooms(roomsList, roomsCount, 2);
|
||||
renderEntities(2);
|
||||
Core::fogParams = oldFog;
|
||||
|
||||
Core::setBlending(bmNone);
|
||||
if (water && waterCache && waterCache->visible) {
|
||||
@ -1832,8 +1868,18 @@ struct Level : IGame {
|
||||
setMainLight(player);
|
||||
waterCache->render();
|
||||
Core::pass = pass;
|
||||
setupBinding();
|
||||
}
|
||||
|
||||
// additive blending pass
|
||||
vec4 oldFog = Core::fogParams;
|
||||
Core::fogParams = FOG_BLACK; // don't apply fog for additive
|
||||
renderRooms(roomsList, roomsCount, 2);
|
||||
renderEntities(2);
|
||||
Core::fogParams = oldFog;
|
||||
|
||||
Core::setBlending(bmNone);
|
||||
|
||||
if (showUI) {
|
||||
Core::Pass pass = Core::pass;
|
||||
renderUI();
|
||||
@ -1908,7 +1954,7 @@ struct Level : IGame {
|
||||
camera->setup(true);
|
||||
|
||||
if (Input::down[ikF]) {
|
||||
level.state.flags.flipped = !level.state.flags.flipped;
|
||||
flipMap();
|
||||
Input::down[ikF] = false;
|
||||
}
|
||||
|
||||
@ -2011,18 +2057,19 @@ struct Level : IGame {
|
||||
// Debug::Level::entities(level);
|
||||
// Debug::Level::zones(level, lara);
|
||||
// Debug::Level::blocks(level);
|
||||
// Debug::Level::path(level, (Enemy*)level.entities[86].controller);
|
||||
// Debug::Level::path(level, (Enemy*)level.entities[105].controller);
|
||||
// Debug::Level::debugOverlaps(level, lara->box);
|
||||
// Debug::Level::debugBoxes(level, lara->dbgBoxes, lara->dbgBoxesCount);
|
||||
Core::setDepthTest(true);
|
||||
Core::setBlending(bmNone);
|
||||
|
||||
/*
|
||||
Core::validateRenderState();
|
||||
|
||||
static int dbg_ambient = 0;
|
||||
dbg_ambient = int(params->time * 2) % 4;
|
||||
|
||||
shadow->unbind(sShadow);
|
||||
cube->unbind(sEnvironment);
|
||||
Core::whiteCube->unbind(sEnvironment);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
@ -2030,7 +2077,7 @@ struct Level : IGame {
|
||||
glColor3f(1, 1, 1);
|
||||
for (int j = 0; j < 6; j++) {
|
||||
glPushMatrix();
|
||||
glTranslatef(lara->pos.x, lara->pos.y - 1024, lara->pos.z);
|
||||
glTranslatef(player->pos.x, player->pos.y - 1024, player->pos.z);
|
||||
switch (j) {
|
||||
case 0 : glRotatef( 90, 0, 1, 0); break;
|
||||
case 1 : glRotatef(-90, 0, 1, 0); break;
|
||||
|
@ -63,7 +63,7 @@ struct MeshRange {
|
||||
|
||||
#define DYN_MESH_QUADS 1024
|
||||
#define DOUBLE_SIDED 2
|
||||
#define MAX_ROOM_DYN_FACES 256
|
||||
#define MAX_ROOM_DYN_FACES 512
|
||||
|
||||
struct Mesh {
|
||||
Index *iBuffer;
|
||||
@ -800,6 +800,12 @@ struct MeshBuilder {
|
||||
#endif
|
||||
}
|
||||
|
||||
void flipMap() {
|
||||
for (int i = 0; i < level->roomsCount; i++)
|
||||
if (level->rooms[i].alternateRoom > -1)
|
||||
swap(rooms[i], rooms[level->rooms[i].alternateRoom]);
|
||||
}
|
||||
|
||||
inline short4 rotate(const short4 &v, int dir) {
|
||||
if (dir == 0) return v;
|
||||
short4 res = v;
|
||||
|
@ -1047,12 +1047,14 @@ struct Lightning : Controller {
|
||||
|
||||
if (timer <= 0.0f) {
|
||||
if (flash) {
|
||||
level->state.flags.flipped = false;
|
||||
if (level->state.flags.flipped)
|
||||
game->flipMap();
|
||||
flash = false;
|
||||
armed = true;
|
||||
timer = (35.0f + randf() * 45.0f) / 30.0f;
|
||||
} else {
|
||||
level->state.flags.flipped = true;
|
||||
if (!level->state.flags.flipped)
|
||||
game->flipMap();
|
||||
flash = true;
|
||||
timer = 20.0f / 30.0f;
|
||||
|
||||
@ -1071,7 +1073,8 @@ struct Lightning : Controller {
|
||||
} else {
|
||||
timer = 0.0f;
|
||||
flash = false;
|
||||
level->state.flags.flipped = false;
|
||||
if (level->state.flags.flipped)
|
||||
game->flipMap();
|
||||
deactivate(true);
|
||||
}
|
||||
}
|
||||
@ -1306,7 +1309,7 @@ struct Cabin : Controller {
|
||||
if (state == STATE_GROUND) {
|
||||
flags.invisible = true;
|
||||
level->state.flipmaps[3].active = TR::ACTIVE;
|
||||
level->state.flags.flipped = !level->state.flags.flipped;
|
||||
game->flipMap();
|
||||
deactivate(true);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user