1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-02-25 16:03:31 +01:00

#3 burning Lara; #22 add fire traps, lava death; #23 filter sounds by distance;

This commit is contained in:
XProger 2017-09-30 06:05:24 +03:00
parent 9ac30862c7
commit fab5d8b301
8 changed files with 230 additions and 84 deletions

View File

@ -402,6 +402,10 @@ struct Controller {
return mask;
}
bool collide(const Sphere &sphere) {
return getBoundingBoxLocal().intersect(Sphere(getMatrix().inverse() * sphere.center, sphere.radius));
}
vec3 trace(int fromRoom, const vec3 &from, const vec3 &to, int &room, bool isCamera) { // TODO: use Bresenham
room = fromRoom;

View File

@ -668,9 +668,12 @@ namespace Debug {
level.getFloorInfo(((Controller*)entity.controller)->getRoomIndex(), entity.x, entity.y, entity.z, 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);
y += 16;
if (info.lava)
Debug::Draw::text(vec2(16, y += 16), vec4(1.0f, 0.5f, 0.3f, 1.0f), "LAVA");
if (info.trigCmdCount > 0) {
y += 16;
sprintf(buf, "trigger: %s%s mask: %d timer: %d", getTriggerType(level, info.trigger), info.trigInfo.once ? " (once)" : "", info.trigInfo.mask, info.trigInfo.timer);
Debug::Draw::text(vec2(16, y += 16), vec4(0.5f, 0.8f, 0.5f, 1.0f), buf);

View File

@ -51,7 +51,7 @@
E( TRAP_SPIKES ) \
E( TRAP_BOULDER ) \
E( TRAP_DART ) \
E( TRAP_DARTGUN ) \
E( TRAP_DART_EMITTER ) \
E( DRAWBRIDGE ) \
E( TRAP_SLAM ) \
E( TRAP_SWORD ) \
@ -187,10 +187,10 @@
E( MUTANT_GRENADE ) \
E( UNUSED_16 ) \
E( UNUSED_17 ) \
E( LAVA_PARTICLE ) \
E( LAVA_EMITTER ) \
E( FLAME ) \
E( FLAME_EMITTER ) \
E( TRAP_LAVA_PARTICLE ) \
E( TRAP_LAVA_EMITTER ) \
E( TRAP_FLAME ) \
E( TRAP_FLAME_EMITTER ) \
E( TRAP_LAVA ) \
E( MUTANT_EGG_BIG ) \
E( BOAT ) \
@ -268,12 +268,16 @@ namespace TR {
SND_UNDERWATER = 60,
SND_BOULDER = 70,
SND_FLOOD = 81,
SND_HIT_LION = 85,
SND_HIT_RAT = 95,
SND_ROCK = 99,
SND_EXPLOSION = 104,
SND_INV_SPIN = 108,
@ -291,7 +295,9 @@ namespace TR {
SND_HIT_SKATEBOY = 132,
SND_HIT_MUTANT = 142,
SND_STOMP = 147,
SND_FLAME = 150,
SND_DART = 151,
SND_TNT = 170,
@ -327,7 +333,7 @@ namespace TR {
HIT_BLADE,
HIT_BOULDER,
HIT_SPIKES,
HIT_FLAME,
HIT_LAVA,
HIT_SLAM,
HIT_REX,
};
@ -578,7 +584,7 @@ namespace TR {
FLOOR ,
CEILING ,
TRIGGER ,
KILL ,
LAVA ,
};
};
@ -663,7 +669,7 @@ namespace TR {
return (type >= PISTOLS && type <= AMMO_UZIS) ||
(type >= PUZZLE_1 && type <= PUZZLE_4) ||
(type >= KEY_ITEM_1 && type <= KEY_ITEM_4) ||
(type == MEDIKIT_SMALL || type == MEDIKIT_BIG || type == SCION_1); // TODO: recheck all items
(type == MEDIKIT_SMALL || type == MEDIKIT_BIG || type == SCION_1 || type == LEADBAR); // TODO: recheck all items
}
bool isActor() const {
@ -1140,7 +1146,7 @@ namespace TR {
int slantX, slantZ;
int floorIndex;
int boxIndex;
int kill;
int lava;
int trigCmdCount;
Trigger trigger;
FloorData::TriggerInfo trigInfo;
@ -2172,7 +2178,7 @@ namespace TR {
info.roomAbove = s.roomAbove;
info.floorIndex = s.floorIndex;
info.boxIndex = s.boxIndex;
info.kill = 0;
info.lava = false;
info.trigger = Trigger::ACTIVATE;
info.trigCmdCount = 0;
@ -2344,8 +2350,8 @@ namespace TR {
break;
}
case FloorData::KILL :
info.kill = 1;
case FloorData::LAVA :
info.lava = true;
break;
default : LOG("unknown func: %d\n", cmd.func);

View File

@ -400,14 +400,6 @@ struct Lara : Character {
} *braid;
Lara(IGame *game, int entity) : Character(game, entity, LARA_MAX_HEALTH), dozy(false), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), braid(NULL) {
if (getEntity().type == TR::Entity::LARA) {
if (getRoom().flags.water)
animation.setAnim(ANIM_UNDERWATER);
else
animation.setAnim(ANIM_STAND);
}
jointChest = 7;
jointHead = 14;
rangeChest = vec4(-0.40f, 0.40f, -0.90f, 0.90f) * PI;
@ -465,6 +457,9 @@ struct Lara : Character {
//reset(18, vec3(34914, 11008, 41315), 90 * DEG2RAD); // level 4 (main hall)
//reset(19, vec3(33368, 19968, 45643), 270 * DEG2RAD); // level 4 (damocles)
//reset(24, vec3(45609, 18176, 41500), 90 * DEG2RAD); // level 4 (thor)
//reset(21, vec3(24106, -4352, 52089), 0); // level 6 (flame traps)
//reset(73, vec3(73372, -122, 51687), PI * 0.5f); // level 6 (midas hand)
//reset(64, vec3(36839, -2560, 48769), 270 * DEG2RAD); // level 6 (flipmap effect)
//reset(99, vec3(45562, -3328, 63366), 225 * DEG2RAD); // level 7a (flipmap)
//reset(90, vec3(19438, 3840, 78341), 90 * DEG2RAD); // level 7a (statues)
//reset(57, vec3(54844, -3328, 53145), 0); // level 8b (bridge switch)
@ -473,12 +468,20 @@ struct Lara : Character {
//reset(30, vec3(69689, -8448, 34922), 330 * DEG2RAD); // Level 10a (cabin)
//reset(27, vec3(52631, -4352, 57893), 270 * DEG2RAD); // Level 10a (drill)
//reset(44, vec3(75803, -11008, 21097), 90 * DEG2RAD); // Level 10a (boat)
//reset(50, vec3(53703, -18688, 13769), PI); // Level 10c (scion holder)
//reset(19, vec3(35364, -512, 40199), PI * 0.5f); // Level 10c (lava flow)
//reset(50, vec3(53703, -18688, 13769), PI); // Level 10c (scion holder)
//reset(19, vec3(35364, -512, 40199), PI * 0.5f); // Level 10c (lava flow)
//reset(9, vec3(69074, -14592, 25192), 0); // Level 10c (trap slam)
//reset(10, vec3(90443, 11264 - 256, 114614), PI, STAND_ONWATER); // villa mortal 2
#endif
chestOffset = animation.getJoints(getMatrix(), 7).pos;
if (getEntity().type == TR::Entity::LARA) {
if (getRoom().flags.water)
animation.setAnim(ANIM_UNDERWATER);
else
animation.setAnim(ANIM_STAND);
}
chestOffset = animation.getJoints(getMatrix(), jointChest).pos;
}
virtual ~Lara() {
@ -983,8 +986,8 @@ struct Lara : Character {
// head & chest
overrideMask |= BODY_CHEST | BODY_HEAD;
animation.overrides[ 7] = animation.getJointRot( 7);
animation.overrides[14] = animation.getJointRot(14);
animation.overrides[jointChest] = animation.getJointRot(jointChest);
animation.overrides[jointHead] = animation.getJointRot(jointHead);
/* TODO: shotgun full body animation
if (wpnCurrent == Weapon::SHOTGUN) {
@ -1396,6 +1399,20 @@ struct Lara : Character {
}
default : ;
}
if (hitType != TR::HIT_LAVA) {
TR::Entity &e = getEntity();
TR::Level::FloorInfo info;
level->getFloorInfo(e.room, e.x, e.y, e.z, info);
if (info.lava && info.floor == e.y)
hitType = TR::HIT_LAVA;
}
if (hitType == TR::HIT_LAVA) {
for (int i = 0; i < 10; i++)
Flame::add(game, this, int(randf() * 24.0f));
}
};
bool useItem(TR::Entity::Type item) {
@ -1578,6 +1595,11 @@ struct Lara : Character {
TR::Level::FloorInfo info;
level->getFloorInfo(e.room, e.x, e.y, e.z, info);
if (info.lava && info.floor == e.y) {
hit(LARA_MAX_HEALTH + 1, NULL, TR::HIT_LAVA);
return;
}
if (!info.trigCmdCount) return; // has no trigger
TR::Limits::Limit *limit = NULL;
@ -2218,10 +2240,10 @@ struct Lara : Character {
if (state == STATE_SURF_TREAD) {
if (animation.isFrameActive(0))
game->waterDrop(animation.getJoints(getMatrix(), 14).pos, 96.0f, 0.03f);
game->waterDrop(animation.getJoints(getMatrix(), jointHead).pos, 96.0f, 0.03f);
} else {
if (animation.frameIndex % 4 == 0)
game->waterDrop(animation.getJoints(getMatrix(), 14).pos, 96.0f, 0.02f);
game->waterDrop(animation.getJoints(getMatrix(), jointHead).pos, 96.0f, 0.02f);
}
if (input & FORTH) {
@ -2779,7 +2801,7 @@ struct Lara : Character {
virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) {
Controller::render(frustum, mesh, type, caustics);
chestOffset = animation.getJoints(getMatrix(), 7).pos; // TODO: move to update func
chestOffset = animation.getJoints(getMatrix(), jointChest).pos; // TODO: move to update func
if (braid)
braid->render(mesh);

View File

@ -283,7 +283,7 @@ struct Level : IGame {
switch (b.flags.mode) {
case 0 : flags |= Sound::UNIQUE; break;
case 1 : flags |= Sound::REPLAY; break;
case 2 : flags |= Sound::STATIC | Sound::LOOP; break;
case 2 : flags |= Sound::FLIPPED | Sound::UNFLIPPED | Sound::LOOP; break;
}
}
if (b.flags.gain) volume = max(0.0f, volume - randf() * 0.25f);
@ -447,8 +447,8 @@ struct Level : IGame {
case TR::Entity::TRAP_BOULDER :
entity.controller = new TrapBoulder(this, i);
break;
case TR::Entity::TRAP_DARTGUN :
entity.controller = new TrapDartgun(this, i);
case TR::Entity::TRAP_DART_EMITTER :
entity.controller = new TrapDartEmitter(this, i);
break;
case TR::Entity::DRAWBRIDGE :
entity.controller = new Drawbridge(this, i);
@ -498,6 +498,9 @@ struct Level : IGame {
case TR::Entity::CABIN :
entity.controller = new Cabin(this, i);
break;
case TR::Entity::TRAP_FLAME_EMITTER :
entity.controller = new TrapFlameEmitter(this, i);
break;
case TR::Entity::BOAT :
entity.controller = new Boat(this, i);
break;

View File

@ -412,10 +412,9 @@ namespace Sound {
PAN = 2,
UNIQUE = 4,
REPLAY = 8,
STATIC = 16,
MUSIC = 32,
FLIPPED = 64,
UNFLIPPED = 128,
MUSIC = 16,
FLIPPED = 32,
UNFLIPPED = 64,
};
bool flipped;
@ -607,7 +606,7 @@ namespace Sound {
if (music != ((channels[i]->flags & MUSIC) != 0))
continue;
if (channels[i]->flags & STATIC) {
if (channels[i]->flags & (FLIPPED | UNFLIPPED)) {
if (!(channels[i]->flags & (flipped ? FLIPPED : UNFLIPPED)))
continue;
@ -704,7 +703,7 @@ namespace Sound {
Sample* play(Stream *stream, const vec3 &pos, float volume = 1.0f, float pitch = 0.0f, int flags = 0, int id = - 1) {
if (!stream) return NULL;
if (volume > 0.001f) {
if (!(flags & (STATIC | MUSIC)) && (flags & PAN)) {
if (!(flags & (FLIPPED | UNFLIPPED | MUSIC)) && (flags & PAN)) {
vec3 d = pos - listener.matrix.getPos();
if (fabsf(d.x) > SND_FADEOFF_DIST || fabsf(d.y) > SND_FADEOFF_DIST || fabsf(d.z) > SND_FADEOFF_DIST) {
delete stream;
@ -712,17 +711,23 @@ namespace Sound {
}
}
if (flags & (UNIQUE | REPLAY))
if (flags & (UNIQUE | REPLAY)) {
for (int i = 0; i < channelsCount; i++)
if (channels[i]->id == id) {
if (!(flags & UNIQUE)) {
channels[i]->pos = pos;
vec3 p = listener.matrix.getPos();
if ((p - channels[i]->pos).length2() > (p - pos).length2()) {
channels[i]->pos = pos;
channels[i]->pitch = pitch;
channels[i]->replay();
}
if (flags & REPLAY)
channels[i]->replay();
delete stream;
return channels[i];
}
}
if (channelsCount < SND_CHANNELS_MAX)
return channels[channelsCount++] = new Sample(stream, pos, volume, pitch, flags, id);

View File

@ -56,6 +56,8 @@ struct Gear : Controller {
}
};
#define DART_DAMAGE 50
struct Dart : Controller {
vec3 velocity;
vec3 dir;
@ -74,7 +76,7 @@ struct Dart : Controller {
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(50.0f, this);
lara->hit(DART_DAMAGE, this);
armed = false;
}
@ -95,13 +97,13 @@ struct Dart : Controller {
}
};
struct TrapDartgun : Controller {
struct TrapDartEmitter : Controller {
enum {
STATE_IDLE,
STATE_FIRE
};
TrapDartgun(IGame *game, int entity) : Controller(game, entity) {}
TrapDartEmitter(IGame *game, int entity) : Controller(game, entity) {}
virtual bool activate() {
if (!Controller::activate())
@ -114,11 +116,11 @@ struct TrapDartgun : Controller {
vec3 p = pos + vec3(0.0f, -512.0f, 256.0f).rotateY(PI - entity.rotation);
int dartIndex = level->entityAdd(TR::Entity::TRAP_DART, entity.room, (int)p.x, (int)p.y, (int)p.z, entity.rotation, entity.intensity);
if (dartIndex > -1) {
Dart *dart = new Dart(game, dartIndex);
int index = level->entityAdd(TR::Entity::TRAP_DART, getRoomIndex(), int(pos.x), int(pos.y), int(pos.z), entity.rotation, entity.intensity);
if (index > -1) {
Dart *dart = new Dart(game, index);
dart->activate();
level->entities[dartIndex].controller = dart;
level->entities[index].controller = dart;
}
Sprite::add(game, TR::Entity::SMOKE, entity.room, (int)p.x, (int)p.y, (int)p.z);
@ -137,6 +139,91 @@ struct TrapDartgun : Controller {
}
};
#define FLAME_HEAT_DAMAGE 90
#define FLAME_BURN_DAMAGE 150
struct Flame : Sprite {
static Flame* add(IGame *game, Controller *controller, int jointIndex) {
Flame *flame = NULL;
TR::Level *level = game->getLevel();
int roomIndex = controller->getRoomIndex();
vec3 pos = controller->pos;
int index = level->entityAdd(TR::Entity::TRAP_FLAME, roomIndex, int(pos.x), int(pos.y), int(pos.z), 0, 0);
if (index > -1) {
flame = new Flame(game, index, jointIndex);
level->entities[index].controller = flame;
}
return flame;
}
int jointIndex;
float sleep;
Flame(IGame *game, int entity, int jointIndex) : Sprite(game, entity, false, Sprite::FRAME_ANIMATED), jointIndex(jointIndex), sleep(0.0f) {
time = randf() * 3.0f;
activate();
}
virtual void update() {
Sprite::update();
game->playSound(TR::SND_FLAME, pos, Sound::PAN);
Character *lara = (Character*)level->laraController;
if (jointIndex > -1) {
if (lara->stand == Character::STAND_UNDERWATER) {
level->entityRemove(entity);
delete this;
return;
}
pos = lara->animation.getJoints(lara->getMatrix(), jointIndex).pos;
if (jointIndex == 0)
pos.y += 100.0f;
lara->hit(FLAME_BURN_DAMAGE * Core::deltaTime, this);
} else
if (lara->health > 0.0f) {
if (sleep > 0.0f)
sleep = max(0.0f, sleep - Core::deltaTime);
if (sleep == 0.0f && lara->collide(Sphere(pos, 600.0f))) {
lara->hit(FLAME_HEAT_DAMAGE * Core::deltaTime, this);
if (lara->collide(Sphere(pos, 300.0f))) {
Flame::add(game, lara, 0);
sleep = 3.0f; // stay inactive for 3 seconds
}
}
}
}
};
struct TrapFlameEmitter : Controller {
Flame *flame;
TrapFlameEmitter(IGame *game, int entity) : Controller(game, entity), flame(NULL) {}
void virtual update() {
if (!isActive()) {
if (flame) {
level->entityRemove(flame->entity);
delete flame;
flame = NULL;
Sound::stop(TR::SND_FLAME);
}
return;
}
if (flame) return;
flame = Flame::add(game, this, -1);
}
};
#define BOULDER_DAMAGE_GROUND 1000
#define BOULDER_DAMAGE_AIR 100
@ -694,7 +781,7 @@ struct TrapLava : Controller {
virtual void update() {
Character *lara = (Character*)level->laraController;
if (lara->health > 0.0f && collide(lara))
lara->hit(1000.0f, this, TR::HIT_FLAME);
lara->hit(1001.0f, this, TR::HIT_LAVA);
if (done) {
deactivate();

View File

@ -773,6 +773,41 @@ vec3 boxNormal(int x, int z) {
return x < z ? vec3(-1, 0, 0) : vec3(0, 0, -1);
}
struct Sphere {
vec3 center;
float radius;
Sphere() {}
Sphere(const vec3 &center, float radius) : center(center), radius(radius) {}
bool intersect(const Sphere &s) const {
float d = (center - s.center).length2();
float r = (radius + s.radius);
return d < r * r;
}
bool intersect(const vec3 &rayPos, const vec3 &rayDir, float &t) const {
vec3 v = rayPos - center;
float h = -v.dot(rayDir);
float d = h * h + radius * radius - v.length2();
if (d > 0.0f) {
d = sqrtf(d);
float tMin = h - d;
float tMax = h + d;
if (tMax > 0.0f) {
if (tMin < 0.0f)
tMin = 0.0f;
t = tMin;
return true;
}
}
return false;
}
};
struct Box {
vec3 min, max;
@ -872,6 +907,12 @@ struct Box {
max += offset;
}
vec3 closestPoint(const vec3 &p) const {
return vec3(clamp(p.x, min.x, max.x),
clamp(p.y, min.y, max.y),
clamp(p.z, min.z, max.z));
}
bool contains(const vec3 &v) const {
return v.x >= min.x && v.x <= max.x && v.y >= min.y && v.y <= max.y && v.z >= min.z && v.z <= max.z;
}
@ -918,6 +959,13 @@ struct Box {
return !((max.x < box.min.x || min.x > box.max.x) || (max.y < box.min.y || min.y > box.max.y) || (max.z < box.min.z || min.z > box.max.z));
}
bool intersect(const Sphere &sphere) const {
if (contains(sphere.center))
return true;
vec3 dir = sphere.center - closestPoint(sphere.center);
return (dir.length2() < sphere.radius * sphere.radius);
}
bool intersect(const vec3 &rayPos, const vec3 &rayDir, float &t) const {
float tMax = INF, tMin = -tMax;
@ -940,38 +988,6 @@ struct Box {
}
};
struct Sphere {
vec3 center;
float radius;
Sphere() {}
Sphere(const vec3 &center, float radius) : center(center), radius(radius) {}
bool intersect(const Sphere &s) const {
float d = (center - s.center).length2();
float r = (radius + s.radius);
return d < r * r;
}
bool intersect(const vec3 &rayPos, const vec3 &rayDir, float &t) const {
vec3 v = rayPos - center;
float h = -v.dot(rayDir);
float d = h * h + radius * radius - v.length2();
if (d > 0.0f) {
d = sqrtf(d);
float tMin = h - d;
float tMax = h + d;
if (tMax > 0.0f) {
if (tMin < 0.0f)
tMin = 0.0f;
t = tMin;
return true;
}
}
return false;
}
};
struct Stream {
static char cacheDir[255];