mirror of
https://github.com/XProger/OpenLara.git
synced 2025-04-22 03:51:58 +02:00
#14 add Larson AI; #3 hit and empty ammo sounds, fix T-Rex death; #22 fix waterfall and static sound sources for flipped rooms, fix muzzle flash; #11 fix value check in settings, skip cutscene if inventory button pressed, fix blackscreen if cutscene audio isn't available; #15 fix TR2 PSX format loader
This commit is contained in:
parent
13c436dbb1
commit
c996704243
124
src/camera.h
124
src/camera.h
@ -233,12 +233,19 @@ struct Camera : ICamera {
|
||||
}
|
||||
|
||||
void traceClip(float offset, TR::Location &to) {
|
||||
trace(target, to);
|
||||
owner->trace(target, to);
|
||||
|
||||
uint16 ownerBoxIndex = level->getSector(target.room, target.pos)->boxIndex;
|
||||
uint16 cameraBoxIndex = level->getSector(to.room, to.pos)->boxIndex;
|
||||
|
||||
TR::Box cBox = level->boxes[(cameraBoxIndex != TR::NO_BOX && !level->boxes[ownerBoxIndex].contains(int(to.pos.x), int(to.pos.z))) ? cameraBoxIndex : ownerBoxIndex];
|
||||
if (ownerBoxIndex == TR::NO_BOX) {
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
TR::Box cBox = level->boxes[ownerBoxIndex];
|
||||
if (cameraBoxIndex != TR::NO_BOX && !level->boxes[ownerBoxIndex].contains(int(to.pos.x), int(to.pos.z)))
|
||||
cBox = level->boxes[cameraBoxIndex];
|
||||
|
||||
clipBox(to.room, to.pos, cBox);
|
||||
cBox.expand(-256);
|
||||
@ -269,117 +276,6 @@ struct Camera : ICamera {
|
||||
level->getSector(to.room, to.pos);
|
||||
}
|
||||
|
||||
int traceX(const TR::Location &from, TR::Location &to) {
|
||||
vec3 d = to.pos - from.pos;
|
||||
if (fabsf(d.x) < EPS) return 1;
|
||||
|
||||
d.yz() *= 1024 / d.x;
|
||||
|
||||
vec3 p = from.pos;
|
||||
|
||||
p.x = float(int(p.x) / 1024 * 1024);
|
||||
if (d.x > 0) p.x += 1023;
|
||||
|
||||
p.yz() += d.yz() * ((p.x - from.pos.x) / 1024);
|
||||
|
||||
float s = float(sign(d.x));
|
||||
d.x = 1024;
|
||||
d *= s;
|
||||
|
||||
int16 roomIndex = from.room;
|
||||
while ((p.x - to.pos.x) * s < 0) {
|
||||
if (level->isBlocked(roomIndex, p)) {
|
||||
to.pos = p;
|
||||
to.room = roomIndex;
|
||||
return -1;
|
||||
}
|
||||
|
||||
to.room = roomIndex;
|
||||
if (level->isBlocked(roomIndex, vec3(p.x + s, p.y, p.z))) {
|
||||
to.pos = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += d;
|
||||
}
|
||||
|
||||
to.room = roomIndex;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int traceZ(const TR::Location &from, TR::Location &to) {
|
||||
vec3 d = to.pos - from.pos;
|
||||
if (fabsf(d.z) < EPS) return 1;
|
||||
|
||||
d.xy() *= 1024 / d.z;
|
||||
|
||||
vec3 p = from.pos;
|
||||
|
||||
p.z = float(int(p.z) / 1024 * 1024);
|
||||
if (d.z > 0) p.z += 1023;
|
||||
|
||||
p.xy() += d.xy() * ((p.z - from.pos.z) / 1024);
|
||||
|
||||
float s = float(sign(d.z));
|
||||
d.z = 1024;
|
||||
d *= s;
|
||||
|
||||
int16 roomIndex = from.room;
|
||||
while ((p.z - to.pos.z) * s < 0) {
|
||||
if (level->isBlocked(roomIndex, p)) {
|
||||
to.pos = p;
|
||||
to.room = roomIndex;
|
||||
return -1;
|
||||
}
|
||||
|
||||
to.room = roomIndex;
|
||||
if (level->isBlocked(roomIndex, vec3(p.x, p.y, p.z + s))) {
|
||||
to.pos = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += d;
|
||||
}
|
||||
|
||||
to.room = roomIndex;
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool trace(const TR::Location &from, TR::Location &to) {
|
||||
int rx, rz;
|
||||
|
||||
if (fabsf(to.pos.x - from.pos.x) < fabsf(to.pos.z - from.pos.z)) {
|
||||
rz = traceZ(from, to);
|
||||
if (!rz) return false;
|
||||
rx = traceX(from, to);
|
||||
} else {
|
||||
rx = traceX(from, to);
|
||||
if (!rx) return false;
|
||||
rz = traceZ(from, to);
|
||||
}
|
||||
TR::Room::Sector *sector = level->getSector(to.room, to.pos);
|
||||
return clipHeight(from, to, sector) && rx == 1 && rz == 1;
|
||||
}
|
||||
|
||||
bool clipHeight(const TR::Location &from, TR::Location &to, TR::Room::Sector *sector) {
|
||||
vec3 dir = to.pos - from.pos;
|
||||
|
||||
float y = level->getFloor(sector, to.pos);
|
||||
if (to.pos.y <= y || from.pos.y >= y) {
|
||||
y = level->getCeiling(sector, to.pos);
|
||||
if (to.pos.y >= y || from.pos.y <= y)
|
||||
return true;
|
||||
}
|
||||
|
||||
ASSERT(dir.y != 0.0f);
|
||||
|
||||
float d = (y - from.pos.y) / dir.y;
|
||||
to.pos.y = y;
|
||||
to.pos.x = from.pos.x + dir.x * d;
|
||||
to.pos.z = from.pos.z + dir.z * d;
|
||||
return false;
|
||||
}
|
||||
|
||||
void clipBox(int16 roomIndex, const vec3 &pos, TR::Box &box) {
|
||||
const TR::Room &room = level->rooms[roomIndex];
|
||||
|
||||
@ -431,7 +327,7 @@ struct Camera : ICamera {
|
||||
float floor = level->getFloor(sector, eye.pos) - 256;
|
||||
|
||||
if (to.pos.y >= floor && eye.pos.y >= floor) {
|
||||
trace(target, eye);
|
||||
owner->trace(target, eye);
|
||||
sector = level->getSector(eye.room, eye.pos);
|
||||
floor = level->getFloor(sector, eye.pos) - 256;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "controller.h"
|
||||
#include "collision.h"
|
||||
#include "sprite.h"
|
||||
|
||||
struct Character : Controller {
|
||||
float health;
|
||||
@ -49,6 +50,7 @@ struct Character : Controller {
|
||||
int box;
|
||||
|
||||
bool flying;
|
||||
bool fullChestRotation;
|
||||
|
||||
Collision collision;
|
||||
|
||||
@ -63,6 +65,7 @@ struct Character : Controller {
|
||||
rotHead = rotChest = quat(0, 0, 0, 1);
|
||||
|
||||
flying = getEntity().type == TR::Entity::ENEMY_BAT;
|
||||
fullChestRotation = false;
|
||||
updateZone();
|
||||
}
|
||||
|
||||
@ -234,9 +237,12 @@ struct Character : Controller {
|
||||
quat rot;
|
||||
|
||||
if (jointChest > -1) {
|
||||
if (aim(target, jointChest, rangeChest, rot))
|
||||
rotChest = rotChest.slerp(quat(0, 0, 0, 1).slerp(rot, 0.5f), speed);
|
||||
else
|
||||
if (aim(target, jointChest, rangeChest, rot)) {
|
||||
if (fullChestRotation)
|
||||
rotChest = rotChest.slerp(rot, speed);
|
||||
else
|
||||
rotChest = rotChest.slerp(quat(0, 0, 0, 1).slerp(rot, 0.5f), speed);
|
||||
} else
|
||||
rotChest = rotChest.slerp(quat(0, 0, 0, 1), speed);
|
||||
animation.overrides[jointChest] = rotChest * animation.overrides[jointChest];
|
||||
}
|
||||
@ -249,6 +255,49 @@ struct Character : Controller {
|
||||
animation.overrides[jointHead] = rotHead * animation.overrides[jointHead];
|
||||
}
|
||||
}
|
||||
|
||||
void addSparks(uint32 mask) {
|
||||
Sphere spheres[MAX_SPHERES];
|
||||
int count;
|
||||
getSpheres(spheres, count);
|
||||
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;
|
||||
game->addEntity(TR::Entity::SPARKLES, getRoomIndex(), sprPos);
|
||||
}
|
||||
}
|
||||
|
||||
void addBlood(const vec3 &sprPos, const vec3 &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) {
|
||||
vec3 p = pos + vec3((randf() * 2.0f - 1.0f) * radius, -randf() * height, (randf() * 2.0f - 1.0f) * radius);
|
||||
addBlood(p, sprVel);
|
||||
}
|
||||
|
||||
void addBloodSpikes() {
|
||||
float ang = randf() * PI * 2.0f;
|
||||
addBlood(64.0f, 512.0f, vec3(sinf(ang), 0.0f, cosf(ang)) * 20.0f);
|
||||
}
|
||||
|
||||
void addBloodBlade() {
|
||||
float ang = angle.y + (randf() - 0.5f) * 30.0f * DEG2RAD;
|
||||
addBlood(64.0f, 744.0f, vec3(sinf(ang), 0.0f, cosf(ang)) * speed);
|
||||
}
|
||||
|
||||
void addBloodSlam(Controller *trapSlam) {
|
||||
for (int i = 0; i < 6; i++)
|
||||
addBloodSpikes();
|
||||
}
|
||||
|
||||
void addRicochet(const vec3 &pos, bool sound) {
|
||||
game->addEntity(TR::Entity::RICOCHET, getRoomIndex(), pos);
|
||||
if (sound)
|
||||
game->playSound(TR::SND_RICOCHET, pos, Sound::PAN);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
114
src/controller.h
114
src/controller.h
@ -82,6 +82,8 @@ struct IGame {
|
||||
virtual Controller* addEntity(TR::Entity::Type type, int room, const vec3 &pos, float angle = 0.0f) { return NULL; }
|
||||
virtual void removeEntity(Controller *controller) {}
|
||||
|
||||
virtual void addMuzzleFlash(Controller *owner, int joint, const vec3 &offset, int lightIndex) {}
|
||||
|
||||
virtual bool invUse(int playerIndex, TR::Entity::Type type) { return false; }
|
||||
virtual void invAdd(TR::Entity::Type type, int count = 1) {}
|
||||
virtual int* invCount(TR::Entity::Type type) { return NULL; }
|
||||
@ -812,6 +814,118 @@ struct Controller {
|
||||
return pos;
|
||||
}
|
||||
|
||||
int traceX(const TR::Location &from, TR::Location &to) {
|
||||
vec3 d = to.pos - from.pos;
|
||||
if (fabsf(d.x) < EPS) return 1;
|
||||
|
||||
d.yz() *= 1024 / d.x;
|
||||
|
||||
vec3 p = from.pos;
|
||||
|
||||
p.x = float(int(p.x) / 1024 * 1024);
|
||||
if (d.x > 0) p.x += 1023;
|
||||
|
||||
p.yz() += d.yz() * ((p.x - from.pos.x) / 1024);
|
||||
|
||||
float s = float(sign(d.x));
|
||||
d.x = 1024;
|
||||
d *= s;
|
||||
|
||||
int16 roomIndex = from.room;
|
||||
while ((p.x - to.pos.x) * s < 0) {
|
||||
if (level->isBlocked(roomIndex, p)) {
|
||||
to.pos = p;
|
||||
to.room = roomIndex;
|
||||
return -1;
|
||||
}
|
||||
|
||||
to.room = roomIndex;
|
||||
if (level->isBlocked(roomIndex, vec3(p.x + s, p.y, p.z))) {
|
||||
to.pos = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += d;
|
||||
}
|
||||
|
||||
to.room = roomIndex;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int traceZ(const TR::Location &from, TR::Location &to) {
|
||||
vec3 d = to.pos - from.pos;
|
||||
if (fabsf(d.z) < EPS) return 1;
|
||||
|
||||
d.xy() *= 1024 / d.z;
|
||||
|
||||
vec3 p = from.pos;
|
||||
|
||||
p.z = float(int(p.z) / 1024 * 1024);
|
||||
if (d.z > 0) p.z += 1023;
|
||||
|
||||
p.xy() += d.xy() * ((p.z - from.pos.z) / 1024);
|
||||
|
||||
float s = float(sign(d.z));
|
||||
d.z = 1024;
|
||||
d *= s;
|
||||
|
||||
int16 roomIndex = from.room;
|
||||
while ((p.z - to.pos.z) * s < 0) {
|
||||
if (level->isBlocked(roomIndex, p)) {
|
||||
to.pos = p;
|
||||
to.room = roomIndex;
|
||||
return -1;
|
||||
}
|
||||
|
||||
to.room = roomIndex;
|
||||
if (level->isBlocked(roomIndex, vec3(p.x, p.y, p.z + s))) {
|
||||
to.pos = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += d;
|
||||
}
|
||||
|
||||
to.room = roomIndex;
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool trace(const TR::Location &from, TR::Location &to) {
|
||||
int rx, rz;
|
||||
|
||||
if (fabsf(to.pos.x - from.pos.x) < fabsf(to.pos.z - from.pos.z)) {
|
||||
rz = traceZ(from, to);
|
||||
if (!rz) return false;
|
||||
rx = traceX(from, to);
|
||||
} else {
|
||||
rx = traceX(from, to);
|
||||
if (!rx) return false;
|
||||
rz = traceZ(from, to);
|
||||
}
|
||||
TR::Room::Sector *sector = level->getSector(to.room, to.pos);
|
||||
return clipHeight(from, to, sector) && rx == 1 && rz == 1;
|
||||
}
|
||||
|
||||
bool clipHeight(const TR::Location &from, TR::Location &to, TR::Room::Sector *sector) {
|
||||
vec3 dir = to.pos - from.pos;
|
||||
|
||||
float y = level->getFloor(sector, to.pos);
|
||||
if (to.pos.y <= y || from.pos.y >= y) {
|
||||
y = level->getCeiling(sector, to.pos);
|
||||
if (to.pos.y >= y || from.pos.y <= y)
|
||||
return true;
|
||||
}
|
||||
|
||||
ASSERT(dir.y != 0.0f);
|
||||
|
||||
float d = (y - from.pos.y) / dir.y;
|
||||
to.pos.y = y;
|
||||
to.pos.x = from.pos.x + dir.x * d;
|
||||
to.pos.z = from.pos.z + dir.z * d;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool checkRange(Controller *target, float range) {
|
||||
vec3 d = target->pos - pos;
|
||||
return fabsf(d.x) < range && fabsf(d.z) < range && fabsf(d.y) < range;
|
||||
|
133
src/enemy.h
133
src/enemy.h
@ -306,6 +306,9 @@ struct Enemy : Character {
|
||||
|
||||
int targetBoxOld = targetBox;
|
||||
|
||||
if (target->health <= 0.0f)
|
||||
targetBox = -1;
|
||||
|
||||
// update mood
|
||||
bool inZone = zone == target->zone;
|
||||
|
||||
@ -667,7 +670,7 @@ struct Lion : Enemy {
|
||||
}
|
||||
|
||||
virtual int getStateGround() {
|
||||
if (!think(false))
|
||||
if (!think(true))
|
||||
return state;
|
||||
|
||||
float angle;
|
||||
@ -1462,6 +1465,11 @@ struct ScionTarget : Enemy {
|
||||
}
|
||||
};
|
||||
|
||||
#define HUMAN_WAIT 0.01f
|
||||
#define HUMAN_DIST_WALK (1024 * 3)
|
||||
#define HUMAN_DIST_SHOT (1024 * 7)
|
||||
#define HUMAN_TURN_SLOW (DEG2RAD * 90)
|
||||
#define HUMAN_TURN_FAST (DEG2RAD * 180)
|
||||
|
||||
struct Human : Enemy {
|
||||
enum {
|
||||
@ -1471,13 +1479,15 @@ struct Human : Enemy {
|
||||
STATE_RUN,
|
||||
STATE_AIM,
|
||||
STATE_DEATH,
|
||||
STATE_UNKNOWN,
|
||||
STATE_WAIT,
|
||||
STATE_FIRE
|
||||
};
|
||||
|
||||
int animDeath;
|
||||
int jointGun;
|
||||
int animDeath;
|
||||
|
||||
Human(IGame *game, int entity, float health) : Enemy(game, entity, health, 100, 375.0f, 1.0f), animDeath(-1) {
|
||||
jointGun = 0;
|
||||
jointChest = 7;
|
||||
jointHead = 8;
|
||||
}
|
||||
@ -1486,6 +1496,7 @@ struct Human : Enemy {
|
||||
if (health <= 0.0f)
|
||||
onDead();
|
||||
Enemy::deactivate(removeFromList);
|
||||
getRoom().removeDynLight(entity);
|
||||
}
|
||||
|
||||
virtual int getStateDeath() {
|
||||
@ -1499,19 +1510,135 @@ struct Human : Enemy {
|
||||
}
|
||||
|
||||
virtual void updatePosition() {
|
||||
fullChestRotation = state == STATE_FIRE || state == STATE_AIM;
|
||||
|
||||
if (state == STATE_RUN || state == STATE_WALK) {
|
||||
float angleY = 0.0f;
|
||||
getTargetInfo(0, NULL, NULL, &angleY, NULL);
|
||||
turn(angleY, state == STATE_RUN ? HUMAN_TURN_FAST : HUMAN_TURN_SLOW);
|
||||
} else
|
||||
turn(0, HUMAN_TURN_SLOW);
|
||||
|
||||
Enemy::updatePosition();
|
||||
setOverrides(true, jointChest, jointHead);
|
||||
lookAt(target);
|
||||
}
|
||||
|
||||
virtual void onDead() {}
|
||||
|
||||
bool targetIsVisible() {
|
||||
if (targetInView && targetDist < HUMAN_DIST_SHOT && target->health > 0.0f) {
|
||||
TR::Location from, to;
|
||||
from.room = getRoomIndex();
|
||||
from.pos = pos;
|
||||
to.pos = target->pos;
|
||||
|
||||
// vertical offset to ~gun/head height
|
||||
from.pos.y -= 768.0f;
|
||||
if (target->stand != STAND_UNDERWATER && target->stand != STAND_ONWATER)
|
||||
to.pos.y -= 768.0f;
|
||||
|
||||
return trace(from, to);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool doShot(float damage, const vec3 &muzzleOffset) {
|
||||
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));
|
||||
game->playSound(target->stand == STAND_UNDERWATER ? TR::SND_HIT_UNDERWATER : TR::SND_HIT, target->pos, Sound::PAN);
|
||||
return true;
|
||||
}
|
||||
|
||||
int16 roomIndex = getRoomIndex();
|
||||
TR::Room::Sector *sector = level->getSector(roomIndex, pos);
|
||||
float floor = level->getFloor(sector, pos) - 64.0f;
|
||||
vec3 p = vec3(target->pos.x + randf() * 512.0f - 256.0f, floor, target->pos.z + randf() * 512.0f - 256.0f);
|
||||
|
||||
target->addRicochet(p, true);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#define LARSON_DAMAGE 50
|
||||
|
||||
struct Larson : Human {
|
||||
|
||||
Larson(IGame *game, int entity) : Human(game, entity, 50) {
|
||||
animDeath = 15;
|
||||
jointGun = 14;
|
||||
}
|
||||
|
||||
virtual int getStateGround() {
|
||||
if (!think(false))
|
||||
return state;
|
||||
|
||||
float angle;
|
||||
getTargetInfo(0, NULL, NULL, &angle, NULL);
|
||||
|
||||
if (nextState == state)
|
||||
nextState = STATE_NONE;
|
||||
|
||||
switch (state) {
|
||||
case STATE_STOP :
|
||||
if (nextState != STATE_NONE)
|
||||
return nextState;
|
||||
if (mood == MOOD_SLEEP)
|
||||
return randf() < HUMAN_WAIT ? STATE_WAIT : STATE_WALK;
|
||||
if (mood == MOOD_ESCAPE)
|
||||
return STATE_RUN;
|
||||
return STATE_WALK;
|
||||
case STATE_WAIT :
|
||||
if (mood != MOOD_SLEEP)
|
||||
return STATE_STOP;
|
||||
if (randf() < HUMAN_WAIT) {
|
||||
nextState = STATE_WALK;
|
||||
return STATE_STOP;
|
||||
}
|
||||
break;
|
||||
case STATE_WALK :
|
||||
if (mood == MOOD_SLEEP && randf() < HUMAN_WAIT)
|
||||
nextState = STATE_WAIT;
|
||||
else if (mood == MOOD_ESCAPE)
|
||||
nextState = STATE_RUN;
|
||||
else if (targetIsVisible())
|
||||
nextState = STATE_AIM;
|
||||
else if (!targetInView || targetDist > HUMAN_DIST_WALK)
|
||||
nextState = STATE_RUN;
|
||||
else
|
||||
break;
|
||||
return STATE_STOP;
|
||||
case STATE_RUN :
|
||||
if (mood == MOOD_SLEEP && randf() < HUMAN_WAIT)
|
||||
nextState = STATE_WAIT;
|
||||
else if (targetIsVisible())
|
||||
nextState = STATE_AIM;
|
||||
else if (targetInView && targetDist < HUMAN_DIST_WALK)
|
||||
nextState = STATE_WALK;
|
||||
else
|
||||
break;
|
||||
return STATE_STOP;
|
||||
case STATE_AIM :
|
||||
if (nextState != STATE_NONE)
|
||||
return nextState;
|
||||
if (targetIsVisible())
|
||||
return STATE_FIRE;
|
||||
return STATE_STOP;
|
||||
case STATE_FIRE :
|
||||
if (nextState == STATE_NONE) {
|
||||
doShot(LARSON_DAMAGE, vec3(-50, 0, 20));
|
||||
nextState = STATE_AIM;
|
||||
}
|
||||
if (mood == MOOD_ESCAPE || target->health <= 0.0f)
|
||||
nextState = STATE_STOP;
|
||||
break;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
|
77
src/format.h
77
src/format.h
@ -926,6 +926,8 @@ namespace TR {
|
||||
SND_UZIS_SHOT = 43,
|
||||
SND_MAGNUMS_SHOT = 44,
|
||||
SND_SHOTGUN_SHOT = 45,
|
||||
SND_EMPTY = 48,
|
||||
SND_HIT_UNDERWATER = 50,
|
||||
|
||||
SND_UNDERWATER = 60,
|
||||
|
||||
@ -1295,11 +1297,11 @@ namespace TR {
|
||||
uint8 align;
|
||||
uint32 waterLevel;
|
||||
|
||||
struct DynLight {
|
||||
struct DynLight {
|
||||
int32 id;
|
||||
vec4 pos;
|
||||
vec4 color;
|
||||
} dynLights[2];
|
||||
} dynLights[2]; // 1 is reserved for main light
|
||||
|
||||
int32 dynLightsCount;
|
||||
|
||||
@ -1358,15 +1360,17 @@ namespace TR {
|
||||
DynLight *light = NULL;
|
||||
for (int i = 0; i < dynLightsCount; i++)
|
||||
if (dynLights[i].id == id) {
|
||||
float maxRadius = min(dynLights[i].color.w, color.w); // radius is invSqrt
|
||||
light = &dynLights[i];
|
||||
light->color.w = maxRadius;
|
||||
break;
|
||||
}
|
||||
// 1 is additional second light, can be overridden
|
||||
|
||||
// 1 is additional second light, can be overridden
|
||||
if (!light) {
|
||||
if (dynLightsCount < 2) {
|
||||
light = &dynLights[dynLightsCount];
|
||||
dynLightsCount = min(2, dynLightsCount + 1);
|
||||
} else
|
||||
if (dynLightsCount < COUNT(dynLights))
|
||||
light = &dynLights[dynLightsCount++];
|
||||
else
|
||||
light = &dynLights[1];
|
||||
}
|
||||
|
||||
@ -2948,6 +2952,7 @@ namespace TR {
|
||||
stream.read(r.info);
|
||||
// room data
|
||||
stream.read(d.size);
|
||||
int startOffset = stream.pos;
|
||||
if (version == VER_TR1_PSX) stream.seek(2);
|
||||
d.vertices = stream.read(d.vCount) ? new Room::Data::Vertex[d.vCount] : NULL;
|
||||
for (int i = 0; i < d.vCount; i++) {
|
||||
@ -3000,8 +3005,14 @@ namespace TR {
|
||||
stream.seek(2);
|
||||
|
||||
int tmp = stream.pos;
|
||||
stream.seek(stream.read(d.rCount) * FACE4_SIZE); // uint32 colored (not existing in file)
|
||||
stream.seek(stream.read(d.tCount) * FACE3_SIZE);
|
||||
if (version == VER_TR2_PSX) {
|
||||
stream.read(d.rCount);
|
||||
stream.seek(sizeof(uint16) * d.rCount);
|
||||
if ((stream.pos - startOffset) % 4) stream.seek(2);
|
||||
stream.seek(sizeof(uint16) * 4 * d.rCount);
|
||||
} else
|
||||
stream.seek(stream.read(d.rCount) * FACE4_SIZE); // uint32 colored (not existing in file)
|
||||
stream.read(d.tCount);
|
||||
stream.setPos(tmp);
|
||||
|
||||
d.fCount = d.rCount + d.tCount;
|
||||
@ -3009,23 +3020,23 @@ namespace TR {
|
||||
|
||||
int idx = 0;
|
||||
|
||||
stream.seek(sizeof(d.rCount));
|
||||
int16 tmpCount;
|
||||
stream.read(tmpCount);
|
||||
ASSERT(tmpCount == d.rCount);
|
||||
if (version == VER_TR2_PSX) {
|
||||
ASSERT(false); // TODO
|
||||
/*
|
||||
for (int i = 0; i < d.fCount; i++)
|
||||
stream.read(d.rectangles[i].flags.value);
|
||||
for (int i = 0; i < d.rCount; i++)
|
||||
stream.raw(&d.faces[i].flags.value, sizeof(uint16));
|
||||
if ((stream.pos - startOffset) % 4) stream.seek(2);
|
||||
for (int i = 0; i < d.rCount; i++) {
|
||||
Rectangle &v = d.rectangles[i];
|
||||
stream.raw(v.vertices, sizeof(v.vertices));
|
||||
v.vertices[0] >>= 2;
|
||||
v.vertices[1] >>= 2;
|
||||
v.vertices[2] >>= 2;
|
||||
v.vertices[3] >>= 2;
|
||||
v.colored = false;
|
||||
Face &f = d.faces[i];
|
||||
f.vCount = 4;
|
||||
stream.raw(f.vertices, sizeof(uint16) * 4);
|
||||
f.vertices[0] >>= 2;
|
||||
f.vertices[1] >>= 2;
|
||||
f.vertices[2] >>= 2;
|
||||
f.vertices[3] >>= 2;
|
||||
f.colored = false;
|
||||
}
|
||||
*/
|
||||
} else
|
||||
for (int i = 0; i < d.rCount; i++) readFace(stream, d.faces[idx++], false, false);
|
||||
|
||||
@ -3034,21 +3045,21 @@ namespace TR {
|
||||
swap(d.faces[j].vertices[2], d.faces[j].vertices[3]);
|
||||
}
|
||||
|
||||
stream.seek(sizeof(d.tCount));
|
||||
stream.read(tmpCount);
|
||||
ASSERT(tmpCount == d.tCount);
|
||||
if (version == VER_TR2_PSX) {
|
||||
ASSERT(false); // TODO
|
||||
/*
|
||||
stream.seek(2);
|
||||
for (int i = 0; i < d.tCount; i++) {
|
||||
Triangle &v = d.triangles[i];
|
||||
stream.raw(&v.flags.value, sizeof(uint16));
|
||||
stream.raw(v.vertices, sizeof(v.vertices));
|
||||
v.vertices[0] >>= 2;
|
||||
v.vertices[1] >>= 2;
|
||||
v.vertices[2] >>= 2;
|
||||
v.colored = false;
|
||||
Face &f = d.faces[d.rCount + i];
|
||||
f.vCount = 3;
|
||||
stream.raw(&f.flags.value, sizeof(uint16));
|
||||
stream.raw(f.vertices, sizeof(uint16) * 3);
|
||||
f.vertices[0] >>= 2;
|
||||
f.vertices[1] >>= 2;
|
||||
f.vertices[2] >>= 2;
|
||||
f.vertices[3] = 0;
|
||||
f.colored = false;
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
for (int i = 0; i < d.tCount; i++)
|
||||
readFace(stream, d.faces[idx++], false, true);
|
||||
|
@ -41,7 +41,7 @@ struct OptionItem {
|
||||
}
|
||||
|
||||
bool checkValue(uint8 value) const {
|
||||
if (value >= maxValue) return false;
|
||||
if (value > maxValue) return false;
|
||||
Core::Settings stg;
|
||||
switch (title) {
|
||||
case STR_OPT_DETAIL_FILTER : stg.detail.setFilter((Core::Settings::Quality)value); return stg.detail.filter == value;
|
||||
|
132
src/lara.h
132
src/lara.h
@ -52,8 +52,6 @@
|
||||
#define MAX_TRIGGER_ACTIONS 64
|
||||
|
||||
#define DESCENT_SPEED 2048.0f
|
||||
#define MUZZLE_FLASH_TIME 0.1f
|
||||
#define FLASH_LIGHT_COLOR vec4(0.6f, 0.5f, 0.1f, 1.0f / 3072.0f)
|
||||
#define TARGET_MAX_DIST (8.0f * 1024.0f)
|
||||
|
||||
struct Lara : Character {
|
||||
@ -183,6 +181,11 @@ struct Lara : Character {
|
||||
STATE_WATER_OUT,
|
||||
STATE_MAX };
|
||||
|
||||
#define LARA_RGUN_JOINT 10
|
||||
#define LARA_LGUN_JOINT 13
|
||||
#define LARA_RGUN_OFFSET vec3( 10, -50, 0)
|
||||
#define LARA_LGUN_OFFSET vec3(-10, -50, 0)
|
||||
|
||||
enum {
|
||||
BODY_HIP = 0x0001,
|
||||
BODY_LEG_L1 = 0x0002,
|
||||
@ -226,7 +229,6 @@ struct Lara : Character {
|
||||
struct Arm {
|
||||
Controller *tracking; // tracking target (main target)
|
||||
Controller *target; // target for shooting
|
||||
float shotTimer;
|
||||
quat rot, rotAbs;
|
||||
|
||||
Weapon::Anim::Type anim;
|
||||
@ -466,7 +468,6 @@ struct Lara : Character {
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
arms[i].shotTimer = MUZZLE_FLASH_TIME + 1.0f;
|
||||
arms[i].rot = quat(0, 0, 0, 1);
|
||||
arms[i].rotAbs = quat(0, 0, 0, 1);
|
||||
}
|
||||
@ -908,6 +909,11 @@ struct Lara : Character {
|
||||
}
|
||||
|
||||
void doShot(bool rightHand, bool leftHand) {
|
||||
if (wpnAmmo && *wpnAmmo != UNLIMITED_AMMO && *wpnAmmo <= 0) { // check for no ammo
|
||||
game->playSound(TR::SND_EMPTY, pos, Sound::PAN);
|
||||
wpnChange(Weapon::PISTOLS);
|
||||
}
|
||||
|
||||
int count = wpnCurrent == Weapon::SHOTGUN ? 6 : 2;
|
||||
float nearDist = 32.0f * 1024.0f;
|
||||
vec3 nearPos;
|
||||
@ -931,11 +937,12 @@ struct Lara : Character {
|
||||
*wpnAmmo -= 1;
|
||||
}
|
||||
|
||||
arm->shotTimer = 0.0f;
|
||||
shots++;
|
||||
|
||||
int joint = wpnCurrent == Weapon::SHOTGUN ? 8 : (i ? 11 : 8);
|
||||
game->addMuzzleFlash(this, i ? LARA_LGUN_JOINT : LARA_RGUN_JOINT, i ? LARA_LGUN_OFFSET : LARA_RGUN_OFFSET, 1 + camera->cameraIndex);
|
||||
|
||||
// TODO: use new trace code
|
||||
int joint = wpnCurrent == Weapon::SHOTGUN ? 8 : (i ? 11 : 8);
|
||||
vec3 p = getJoint(joint).pos;
|
||||
vec3 d = arm->rotAbs * vec3(0, 0, 1);
|
||||
vec3 t = p + d * (24.0f * 1024.0f) + ((vec3(randf(), randf(), randf()) * 2.0f) - vec3(1.0f)) * 1024.0f;
|
||||
@ -961,19 +968,12 @@ struct Lara : Character {
|
||||
}
|
||||
|
||||
if (shots) {
|
||||
Core::lightPos[1 + camera->cameraIndex] = (getJoint(10).pos + getJoint(13).pos) * 0.5f;
|
||||
Core::lightColor[1 + camera->cameraIndex] = FLASH_LIGHT_COLOR;
|
||||
|
||||
game->playSound(wpnGetSound(), pos, Sound::PAN);
|
||||
game->playSound(TR::SND_RICOCHET, nearPos, Sound::PAN);
|
||||
|
||||
if (wpnAmmo && *wpnAmmo != UNLIMITED_AMMO && wpnCurrent == Weapon::SHOTGUN)
|
||||
*wpnAmmo -= 1;
|
||||
}
|
||||
|
||||
if (wpnAmmo && *wpnAmmo != UNLIMITED_AMMO && *wpnAmmo <= 0) {
|
||||
wpnChange(Weapon::PISTOLS);
|
||||
}
|
||||
}
|
||||
|
||||
void updateWeapon() {
|
||||
@ -1422,8 +1422,8 @@ struct Lara : Character {
|
||||
case TR::Effect::LARA_HANDSFREE : break;//meshSwap(1, level->extra.weapons[wpnCurrent], BODY_LEG_L1 | BODY_LEG_R1); break;
|
||||
case TR::Effect::DRAW_RIGHTGUN : drawGun(true); break;
|
||||
case TR::Effect::DRAW_LEFTGUN : drawGun(false); break;
|
||||
case TR::Effect::SHOT_RIGHTGUN : arms[0].shotTimer = 0; break;
|
||||
case TR::Effect::SHOT_LEFTGUN : arms[1].shotTimer = 0; break;
|
||||
case TR::Effect::SHOT_RIGHTGUN : game->addMuzzleFlash(this, LARA_RGUN_JOINT, LARA_RGUN_OFFSET, 1 + camera->cameraIndex); break;
|
||||
case TR::Effect::SHOT_LEFTGUN : game->addMuzzleFlash(this, LARA_LGUN_JOINT, LARA_LGUN_OFFSET, 1 + camera->cameraIndex); break;
|
||||
case TR::Effect::MESH_SWAP_1 :
|
||||
case TR::Effect::MESH_SWAP_2 :
|
||||
case TR::Effect::MESH_SWAP_3 : Character::cmdEffect(fx);
|
||||
@ -1433,43 +1433,6 @@ struct Lara : Character {
|
||||
}
|
||||
}
|
||||
|
||||
void addSparks(uint32 mask) {
|
||||
Sphere spheres[MAX_SPHERES];
|
||||
int count;
|
||||
getSpheres(spheres, count);
|
||||
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;
|
||||
game->addEntity(TR::Entity::SPARKLES, getRoomIndex(), sprPos);
|
||||
}
|
||||
}
|
||||
|
||||
void addBlood(const vec3 &sprPos, const vec3 &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) {
|
||||
vec3 p = pos + vec3((randf() * 2.0f - 1.0f) * radius, -randf() * height, (randf() * 2.0f - 1.0f) * radius);
|
||||
addBlood(p, sprVel);
|
||||
}
|
||||
|
||||
void addBloodSpikes() {
|
||||
float ang = randf() * PI * 2.0f;
|
||||
addBlood(64.0f, 512.0f, vec3(sinf(ang), 0.0f, cosf(ang)) * 20.0f);
|
||||
}
|
||||
|
||||
void addBloodBlade() {
|
||||
float ang = angle.y + (randf() - 0.5f) * 30.0f * DEG2RAD;
|
||||
addBlood(64.0f, 744.0f, vec3(sinf(ang), 0.0f, cosf(ang)) * speed);
|
||||
}
|
||||
|
||||
void addBloodSlam(Controller *trapSlam) {
|
||||
for (int i = 0; i < 6; i++)
|
||||
addBloodSpikes();
|
||||
}
|
||||
|
||||
void bakeEnvironment() {
|
||||
flags.invisible = true;
|
||||
if (!environment)
|
||||
@ -1508,10 +1471,10 @@ struct Lara : Character {
|
||||
game->stopTrack();
|
||||
|
||||
Core::lightColor[1 + 0] = Core::lightColor[1 + 1] = vec4(0, 0, 0, 1);
|
||||
arms[0].shotTimer = arms[1].shotTimer = MUZZLE_FLASH_TIME + 1.0f;
|
||||
arms[0].tracking = arms[1].tracking = NULL;
|
||||
arms[0].target = arms[1].target = NULL;
|
||||
viewTarget = NULL;
|
||||
velocity = vec3(0.0f);
|
||||
animation.overrideMask = 0;
|
||||
|
||||
switch (hitType) {
|
||||
@ -2793,31 +2756,13 @@ struct Lara : Character {
|
||||
usedKey = TR::Entity::LARA;
|
||||
|
||||
if (camera->mode != Camera::MODE_CUTSCENE && camera->mode != Camera::MODE_STATIC)
|
||||
camera->mode = emptyHands() ? Camera::MODE_FOLLOW : Camera::MODE_COMBAT;
|
||||
}
|
||||
|
||||
void updateFlash() {
|
||||
float minTime = MUZZLE_FLASH_TIME;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
if (arms[i].shotTimer < MUZZLE_FLASH_TIME) {
|
||||
arms[i].shotTimer += Core::deltaTime;
|
||||
minTime = min(minTime, arms[i].shotTimer);
|
||||
}
|
||||
|
||||
if (minTime < MUZZLE_FLASH_TIME) {
|
||||
float intensity = clamp((0.1f - minTime) * 20.0f, EPS, 1.0f);
|
||||
|
||||
Core::lightColor[1 + camera->cameraIndex] = FLASH_LIGHT_COLOR * vec4(intensity, intensity, intensity, 1.0f / sqrtf(intensity));
|
||||
Core::lightPos[1 + camera->cameraIndex] = (getJoint(10).pos + getJoint(13).pos) * 0.5f;
|
||||
} else
|
||||
Core::lightColor[1 + camera->cameraIndex] = vec4(0, 0, 0, 1);
|
||||
camera->mode = (emptyHands() || health <= 0.0f) ? Camera::MODE_FOLLOW : Camera::MODE_COMBAT;
|
||||
}
|
||||
|
||||
virtual void updateAnimation(bool commands) {
|
||||
Controller::updateAnimation(commands);
|
||||
updateWeapon();
|
||||
updateFlash();
|
||||
|
||||
if (stand == STAND_UNDERWATER)
|
||||
specular = 0.0f;
|
||||
else
|
||||
@ -3266,22 +3211,6 @@ struct Lara : Character {
|
||||
return mask;
|
||||
}
|
||||
|
||||
void renderMuzzleFlash(MeshBuilder *mesh, const Basis &basis, const vec3 &offset, float time) {
|
||||
ASSERT(level->extra.muzzleFlash);
|
||||
if (time > MUZZLE_FLASH_TIME) return;
|
||||
float alpha = min(1.0f, (0.1f - time) * 20.0f);
|
||||
float lum = 3.0f;
|
||||
Basis b(basis);
|
||||
b.w = 1.0f;
|
||||
b.rotate(quat(vec3(1, 0, 0), -PI * 0.5f));
|
||||
b.translate(offset);
|
||||
if (level->version & (TR::VER_TR2 | TR::VER_TR3))
|
||||
lum = alpha;
|
||||
Core::active.shader->setParam(uMaterial, vec4(lum, 0.0f, 0.0f, alpha));
|
||||
Core::setBasis(&b, 1);
|
||||
mesh->renderModel(level->extra.muzzleFlash);
|
||||
}
|
||||
|
||||
virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) {
|
||||
uint32 visMask = visibleMask;
|
||||
if (Core::pass != Core::passShadow && camera->firstPerson && camera->viewIndex == -1 && game->getCamera() == camera) // hide head in first person view // TODO: fix for firstPerson with viewIndex always == -1
|
||||
@ -3292,31 +3221,6 @@ struct Lara : Character {
|
||||
if (braid)
|
||||
braid->render(mesh);
|
||||
|
||||
if (wpnCurrent != Weapon::SHOTGUN && Core::pass != Core::passShadow && (arms[0].shotTimer < MUZZLE_FLASH_TIME || arms[1].shotTimer < MUZZLE_FLASH_TIME)) {
|
||||
game->setShader(Core::pass, Shader::FLASH, false, true);
|
||||
|
||||
int meshTransp = mesh->transparent;
|
||||
float zOffset;
|
||||
if (level->version & (TR::VER_TR2 | TR::VER_TR3)) {
|
||||
mesh->transparent = 2;
|
||||
Core::setBlending(bmAdd);
|
||||
zOffset = 180;
|
||||
} else {
|
||||
Core::setBlending(bmAlpha);
|
||||
zOffset = 150;
|
||||
}
|
||||
|
||||
renderMuzzleFlash(mesh, joints[10], vec3(-10, -50, zOffset), arms[0].shotTimer);
|
||||
renderMuzzleFlash(mesh, joints[13], vec3( 10, -50, zOffset), arms[1].shotTimer);
|
||||
|
||||
mesh->transparent = meshTransp;
|
||||
switch (mesh->transparent) {
|
||||
case 0 : Core::setBlending(bmNone); break;
|
||||
case 1 : Core::setBlending(bmAlpha); break;
|
||||
case 2 : Core::setBlending(bmAdd); break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == STATE_MIDAS_DEATH /* && Core::pass == Core::passCompose */) {
|
||||
game->setRoomParams(getRoomIndex(), Shader::MIRROR, 1.2f, 1.0f, 0.2f, 1.0f, false);
|
||||
/* catsuit test
|
||||
|
33
src/level.h
33
src/level.h
@ -45,6 +45,7 @@ struct Level : IGame {
|
||||
Sound::Sample *sndSoundtrack;
|
||||
Sound::Sample *sndUnderwater;
|
||||
Sound::Sample *sndCurrent;
|
||||
bool waitSoundtrack;
|
||||
bool playNextTrack;
|
||||
|
||||
bool lastTitle;
|
||||
@ -486,6 +487,15 @@ struct Level : IGame {
|
||||
delete controller;
|
||||
}
|
||||
|
||||
virtual void addMuzzleFlash(Controller *owner, int joint, const vec3 &offset, int lightIndex) {
|
||||
MuzzleFlash *mf = (MuzzleFlash*)addEntity(TR::Entity::MUZZLE_FLASH, owner->getRoomIndex(), offset, 0);
|
||||
if (mf) {
|
||||
mf->owner = owner;
|
||||
mf->joint = joint;
|
||||
mf->lightIndex = lightIndex;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool invUse(int playerIndex, TR::Entity::Type type) {
|
||||
if (!players[playerIndex]->useItem(type))
|
||||
return inventory.use(type);
|
||||
@ -525,8 +535,8 @@ struct Level : IGame {
|
||||
switch (b.flags.mode) {
|
||||
case 0 : if (level.version & TR::VER_TR1) flags |= Sound::UNIQUE; break; // TODO check this
|
||||
case 1 : flags |= Sound::REPLAY; break;
|
||||
case 2 : if (level.version & TR::VER_TR1) flags |= Sound::FLIPPED | Sound::UNFLIPPED | Sound::LOOP; break;
|
||||
case 3 : if (!(level.version & TR::VER_TR1)) flags |= Sound::FLIPPED | Sound::UNFLIPPED | Sound::LOOP | Sound::UNIQUE; break;
|
||||
case 2 : if (level.version & TR::VER_TR1) flags |= Sound::LOOP; break;
|
||||
case 3 : if (!(level.version & TR::VER_TR1)) flags |= Sound::LOOP | Sound::UNIQUE; break;
|
||||
}
|
||||
}
|
||||
if (b.flags.gain) volume = max(0.0f, volume - randf() * 0.25f);
|
||||
@ -546,8 +556,9 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
static void playAsync(Stream *stream, void *userData) {
|
||||
if (!stream) return;
|
||||
Level *level = (Level*)userData;
|
||||
level->waitSoundtrack = false;
|
||||
if (!stream) return;
|
||||
|
||||
level->sndSoundtrack = Sound::play(stream, vec3(0.0f), 0.01f, 1.0f, Sound::MUSIC);
|
||||
if (level->sndSoundtrack) {
|
||||
@ -582,6 +593,7 @@ struct Level : IGame {
|
||||
|
||||
if (track == 0xFF) return;
|
||||
|
||||
waitSoundtrack = true;
|
||||
getGameTrack(level.version, track, playAsync, this);
|
||||
}
|
||||
|
||||
@ -633,7 +645,10 @@ struct Level : IGame {
|
||||
|
||||
for (int i = 0; i < level.soundSourcesCount; i++) {
|
||||
TR::SoundSource &src = level.soundSources[i];
|
||||
playSound(src.id, vec3(float(src.x), float(src.y), float(src.z)), Sound::PAN | src.flags);
|
||||
int flags = Sound::PAN;
|
||||
if (src.flags & 64) flags |= Sound::FLIPPED;
|
||||
if (src.flags & 128) flags |= Sound::UNFLIPPED;
|
||||
playSound(src.id, vec3(float(src.x), float(src.y), float(src.z)), flags);
|
||||
}
|
||||
|
||||
lastTitle = false;
|
||||
@ -684,6 +699,8 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
void addPlayer(int index) {
|
||||
if (level.isCutsceneLevel()) return;
|
||||
|
||||
if (!players[index]) {
|
||||
players[index] = (Lara*)addEntity(TR::Entity::LARA, 0, vec3(0.0f), 0.0f);
|
||||
players[index]->camera->cameraIndex = index;
|
||||
@ -790,6 +807,7 @@ struct Level : IGame {
|
||||
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::MUZZLE_FLASH : return new MuzzleFlash(this, index);
|
||||
case TR::Entity::LAVA_PARTICLE : return new LavaParticle(this, index);
|
||||
case TR::Entity::TRAP_LAVA_EMITTER : return new TrapLavaEmitter(this, index);
|
||||
case TR::Entity::FLAME : return new Flame(this, index);
|
||||
@ -1402,7 +1420,7 @@ struct Level : IGame {
|
||||
playNextTrack = false;
|
||||
}
|
||||
|
||||
if (level.isCutsceneLevel()) {
|
||||
if (level.isCutsceneLevel() && waitSoundtrack) {
|
||||
if (!sndSoundtrack && TR::LEVEL_INFO[level.id].ambientTrack != TR::NO_TRACK) {
|
||||
if (camera->timer > 0.0f) // for the case that audio stops before animation ends
|
||||
loadNextLevel();
|
||||
@ -1425,6 +1443,11 @@ struct Level : IGame {
|
||||
if ((Input::state[0][cInventory] || Input::state[1][cInventory]) && !level.isTitle() && inventory.titleTimer < 1.0f && !inventory.active && inventory.lastKey == cMAX) {
|
||||
int playerIndex = Input::state[0][cInventory] ? 0 : 1;
|
||||
|
||||
if (level.isCutsceneLevel()) {
|
||||
loadNextLevel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->health <= 0.0f)
|
||||
inventory.toggle(playerIndex, Inventory::PAGE_OPTION, TR::Entity::INV_PASSPORT);
|
||||
else
|
||||
|
@ -204,6 +204,68 @@ struct TrapFlameEmitter : Controller {
|
||||
};
|
||||
|
||||
|
||||
#define MUZZLE_FLASH_TIME 0.1f
|
||||
#define FLASH_LIGHT_COLOR vec4(0.6f, 0.5f, 0.1f, 1.0f / 3072.0f)
|
||||
|
||||
struct MuzzleFlash : Controller {
|
||||
Controller *owner;
|
||||
int joint;
|
||||
int lightIndex;
|
||||
|
||||
MuzzleFlash(IGame *game, int entity) : Controller(game, entity), owner(NULL), joint(0), lightIndex(-1) {
|
||||
pos.z += (level->version & (TR::VER_TR2 | TR::VER_TR3)) ? 180.0f : 150.0f;
|
||||
activate();
|
||||
}
|
||||
|
||||
virtual void update() {
|
||||
if (timer < MUZZLE_FLASH_TIME) {
|
||||
timer += Core::deltaTime;
|
||||
|
||||
if (timer < MUZZLE_FLASH_TIME) {
|
||||
float intensity = clamp((MUZZLE_FLASH_TIME - timer) * 20.0f, EPS, 1.0f);
|
||||
|
||||
vec4 lightPos = vec4(owner->getJoint(joint).pos, 0);
|
||||
vec4 lightColor = FLASH_LIGHT_COLOR * vec4(intensity, intensity, intensity, 1.0f / sqrtf(intensity));
|
||||
if (lightIndex > -1) {
|
||||
ASSERT(lightIndex + 1 < MAX_LIGHTS);
|
||||
Core::lightPos[lightIndex] = lightPos;
|
||||
Core::lightColor[lightIndex] = lightColor;
|
||||
} else
|
||||
getRoom().addDynLight(owner->entity, lightPos, lightColor);
|
||||
} else {
|
||||
if (lightIndex > -1) {
|
||||
ASSERT(lightIndex < MAX_LIGHTS);
|
||||
Core::lightPos[lightIndex] = vec4(0);
|
||||
Core::lightColor[lightIndex] = vec4(0, 0, 0, 1);
|
||||
} else
|
||||
getRoom().removeDynLight(owner->entity);
|
||||
game->removeEntity(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) {
|
||||
ASSERT(level->extra.muzzleFlash);
|
||||
ASSERT(owner);
|
||||
|
||||
float alpha = min(1.0f, (0.1f - timer) * 20.0f);
|
||||
float lum = 3.0f;
|
||||
Basis b = owner->getJoint(joint);
|
||||
b.w = 1.0f;
|
||||
b.rotate(quat(vec3(1, 0, 0), -PI * 0.5f));
|
||||
b.translate(pos);
|
||||
if (level->version & (TR::VER_TR2 | TR::VER_TR3))
|
||||
lum = alpha;
|
||||
|
||||
game->setShader(Core::pass, Shader::FLASH, false, true);
|
||||
Core::active.shader->setParam(uMaterial, vec4(lum, 0.0f, 0.0f, alpha));
|
||||
Core::setBasis(&b, 1);
|
||||
|
||||
mesh->renderModel(level->extra.muzzleFlash);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#define LAVA_PARTICLE_DAMAGE 10
|
||||
#define LAVA_V_SPEED -165
|
||||
#define LAVA_H_SPEED 32
|
||||
@ -1380,6 +1442,9 @@ struct Waterfall : Controller {
|
||||
Waterfall(IGame *game, int entity) : Controller(game, entity), timer(0.0f) {}
|
||||
|
||||
virtual void update() {
|
||||
if (getEntity().room != getRoomIndex()) // room is flipped
|
||||
return;
|
||||
|
||||
vec3 delta = (game->getLara(pos)->pos - pos) * (1.0f / 1024.0f);
|
||||
if (delta.length2() > 100.0f)
|
||||
return;
|
||||
|
Loading…
x
Reference in New Issue
Block a user