1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-01-17 21:09:00 +01:00

closed #13 add doors implementation & puzzle hole mesh swap; #9 loop ambient sound;

This commit is contained in:
XProger 2016-11-26 01:39:40 +03:00
parent 36fce871cb
commit ba5ffaf977
8 changed files with 118 additions and 31 deletions

Binary file not shown.

View File

@ -57,8 +57,10 @@ struct Camera : Controller {
virtual void update() {
int lookAt = -1;
if (owner->target > -1) lookAt = owner->target;
if (actTargetEntity > -1) lookAt = actTargetEntity;
if (owner->target > -1) lookAt = owner->target;
owner->viewTarget = lookAt;
if (timer > 0.0f) {
timer -= Core::deltaTime;

View File

@ -52,7 +52,9 @@ struct Controller {
meshes[i] = model->mStart + i;
}
void meshSwap(TR::Model *model, int mask) {
void meshSwap(TR::Model *model, int mask = 0xFFFFFFFF) {
if (!meshes) initMeshOverrides();
for (int i = 0; i < model->mCount; i++) {
int index = model->mStart + i;
if (((1 << i) & mask) && level->meshOffsets[index])

View File

@ -9,6 +9,10 @@
namespace TR {
enum {
FLOOR_BLOCK = -127,
};
enum {
ANIM_CMD_NONE ,
ANIM_CMD_OFFSET ,
@ -387,6 +391,7 @@ namespace TR {
PUZZLE_4 = 113, // sprite
HOLE_PUZZLE = 118,
HOLE_PUZZLE_SET = 122,
PICKUP = 126, // sprite
@ -766,6 +771,7 @@ namespace TR {
struct {
Model *muzzleFlash;
Model *puzzleSet;
} extra;
Level(const char *name, bool demo) {
@ -899,7 +905,8 @@ namespace TR {
memset(&extra, 0, sizeof(extra));
for (int i = 0; i < modelsCount; i++)
switch (models[i].type) {
case Entity::MUZZLE_FLASH : extra.muzzleFlash = &models[i]; break;
case Entity::MUZZLE_FLASH : extra.muzzleFlash = &models[i]; break;
case Entity::HOLE_PUZZLE_SET : extra.puzzleSet = &models[i]; break;
default : ;
}
}
@ -1032,6 +1039,9 @@ namespace TR {
info.trigger = Trigger::ACTIVATE;
info.trigCmdCount = 0;
if (s.floor == -127)
return;
Room::Sector *sBelow = &s;
while (sBelow->roomBelow != 0xFF) sBelow = &getSector(sBelow->roomBelow, x, z, dx, dz);
info.floor = 256 * sBelow->floor;
@ -1072,7 +1082,7 @@ namespace TR {
if (ey >= y - 128 && ey < info.floor)
info.floor = ey;
if (ey < y - 128 && ey > info.ceiling)
info.ceiling = ey + (e.type == Entity::TRAP_FLOOR ? 256 : 0);
info.ceiling = ey + (e.type == Entity::TRAP_FLOOR ? 0 : 256);
break;
}

View File

@ -24,6 +24,7 @@
#define PICKUP_FRAME_GROUND 40
#define PICKUP_FRAME_UNDERWATER 16
#define PUZZLE_FRAME 80
#define MAX_TRIGGER_ACTIONS 64
@ -193,8 +194,9 @@ struct Lara : Character {
Inventory inventory;
int lastPickUp;
int viewTarget;
Lara(TR::Level *level, int entity, bool home) : Character(level, entity, 1000), home(home), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos) {
Lara(TR::Level *level, int entity, bool home) : Character(level, entity, 1000), home(home), wpnCurrent(Weapon::EMPTY), wpnNext(Weapon::EMPTY), chestOffset(pos), viewTarget(-1) {
animation.setAnim(ANIM_STAND);
getEntity().flags.active = 1;
initMeshOverrides();
@ -264,12 +266,12 @@ struct Lara : Character {
pos = vec3(31390, -2048, 33472);
angle = vec3(0.0f, 0.0f, 0.0f);
getEntity().room = 63;
*/
// level 2 (trap door)
pos = vec3(21987, -1024, 29144);
angle = vec3(0.0f, PI * 3.0f * 0.5f, 0.0f);
getEntity().room = 61;
/*
// level 3a
pos = vec3(41015, 3584, 34494);
angle = vec3(0.0f, -PI, 0.0f);
@ -376,6 +378,15 @@ struct Lara : Character {
return wpnCurrent == Weapon::EMPTY || arms[0].anim == Weapon::Anim::NONE;
}
bool canLookAt() {
return (stand == STAND_GROUND || stand == STAND_SLIDE)
&& state != STATE_REACH
&& state != STATE_PUSH_BLOCK
&& state != STATE_PULL_BLOCK
&& state != STATE_PUSH_PULL_READY
&& state != STATE_PICK_UP;
}
bool canDrawWeapon() {
return wpnCurrent != Weapon::EMPTY
&& emptyHands()
@ -713,7 +724,8 @@ struct Lara : Character {
} else
animation.overrideMask &= ~(BODY_ARM_R | BODY_ARM_L);
lookAt(target);
lookAt(viewTarget);
if (wpnCurrent == Weapon::SHOTGUN)
aimShotgun();
@ -725,15 +737,16 @@ struct Lara : Character {
float speed = 8.0f * Core::deltaTime;
quat rot;
bool can = canLookAt();
// chest
if ((stand == STAND_GROUND || stand == STAND_SLIDE) && aim(target, 7, vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.9f, PI * 0.9f), rot))
if (can && aim(target, 7, vec4(-PI * 0.4f, PI * 0.4f, -PI * 0.9f, PI * 0.9f), rot))
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[7] = rotChest * animation.overrides[7];
// head
if (aim(target, 14, vec4(-PI * 0.25f, PI * 0.25f, -PI * 0.5f, PI * 0.5f), rot))
if (can && aim(target, 14, vec4(-PI * 0.25f, PI * 0.25f, -PI * 0.5f, PI * 0.5f), rot))
rotHead = rotHead.slerp(rot, speed);
else
rotHead = rotHead.slerp(quat(0, 0, 0, 1), speed);
@ -941,15 +954,21 @@ struct Lara : Character {
return;
break;
case TR::Level::Trigger::KEY :
actionState = STATE_USE_KEY;
if (level->entities[info.trigCmd[0].args].flags.active)
return;
actionState = level->entities[info.trigCmd[0].args].type == TR::Entity::HOLE_KEY ? STATE_USE_KEY : STATE_USE_PUZZLE;
if (isActive || !isPressed(ACTION) || state == actionState || !emptyHands()) // TODO: STATE_USE_PUZZLE
return;
if (!checkAngle(level->entities[info.trigCmd[0].args].rotation))
return;
if (animation.canSetState(actionState) && !useItem(TR::Entity::NONE, level->entities[info.trigCmd[0].args].type)) {
playSound(TR::SND_NO, pos, Sound::PAN);
if (animation.canSetState(actionState)) {
if (false) { //!useItem(TR::Entity::NONE, level->entities[info.trigCmd[0].args].type)) {
playSound(TR::SND_NO, pos, Sound::PAN);
return;
}
} else
return;
}
lastPickUp = info.trigCmd[0].args; // TODO: it's not pickup, it's key/puzzle hole
break;
case TR::Level::Trigger::PICKUP :
if (!isActive) // check if item is not picked up
@ -964,6 +983,9 @@ struct Lara : Character {
if (!animation.setState(actionState)) return;
if (info.trigger == TR::Level::Trigger::SWITCH || info.trigger == TR::Level::Trigger::KEY) {
if (info.trigger == TR::Level::Trigger::KEY)
level->entities[info.trigCmd[0].args].flags.active = true;
TR::Entity &p = level->entities[info.trigCmd[0].args];
angle.y = p.rotation;
angle.x = 0;
@ -1370,14 +1392,23 @@ struct Lara : Character {
}
virtual void doCustomCommand(int curFrame, int prevFrame) {
if (state == STATE_PICK_UP) {
TR::Entity &item = level->entities[lastPickUp];
if (!item.flags.invisible) {
int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER;
if (animation.isFrameActive(pickupFrame)) {
item.flags.invisible = true;
inventory.add(item.type, 1);
switch (state) {
case STATE_PICK_UP : {
TR::Entity &item = level->entities[lastPickUp];
if (!item.flags.invisible) {
int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER;
if (animation.isFrameActive(pickupFrame)) {
item.flags.invisible = true;
inventory.add(item.type, 1);
}
}
break;
}
case STATE_USE_PUZZLE : {
TR::Entity &item = level->entities[lastPickUp];
if (animation.isFrameActive(PUZZLE_FRAME))
((Controller*)item.controller)->meshSwap(level->extra.puzzleSet);
break;
}
}
}

View File

@ -385,7 +385,7 @@ struct Level {
}
void renderEntity(const TR::Entity &entity) {
// if (entity.room != lara->getRoomIndex()) return;
//if (entity.room != lara->getRoomIndex()) return;
if (entity.type == TR::Entity::NONE) return;
ASSERT(entity.controller);
@ -495,7 +495,7 @@ struct Level {
// Debug::Level::portals(level);
// Debug::Level::meshes(level);
Debug::Level::entities(level);
// Debug::Level::info(level, lara->getEntity(), lara->animation);
Debug::Level::info(level, lara->getEntity(), lara->animation);
Debug::end();
#endif
}

View File

@ -196,10 +196,9 @@ namespace Sound {
OGG(Stream *stream, int channels) : Decoder(stream, channels), size(stream->size), pos(0) {
buffer = new char[size]; // TODO: file streaming
stream->raw(buffer, size);
int error;
alloc.alloc_buffer_length_in_bytes = 256 * 1024;
alloc.alloc_buffer = new char[alloc.alloc_buffer_length_in_bytes];
ogg = stb_vorbis_open_memory((unsigned char*)buffer, size, &error, &alloc);
ogg = stb_vorbis_open_memory((unsigned char*)buffer, size, NULL, &alloc);
stb_vorbis_info info = stb_vorbis_get_info(ogg);
channels = info.channels;
}
@ -220,7 +219,10 @@ namespace Sound {
return i;
}
// TODO: replay
virtual void replay() {
stb_vorbis_close(ogg);
ogg = stb_vorbis_open_memory((unsigned char*)buffer, size, NULL, &alloc);
}
};
#endif
@ -321,8 +323,11 @@ namespace Sound {
while (i < count) {
int res = decoder->decode(&frames[i], count - i);
if (res == 0) {
if (i == 0) isPlaying = false;
break;
if (!(flags & Flags::LOOP)) {
if (i == 0) isPlaying = false;
break;
} else
decoder->replay();
}
i += res;
}

View File

@ -51,7 +51,7 @@ struct Trigger : Controller {
}
}
if (!inState())
if (!inState() && entity.type != TR::Entity::HOLE_KEY && entity.type != TR::Entity::HOLE_PUZZLE)
animation.setState(state != baseState ? baseState : (entity.type == TR::Entity::TRAP_BLADE ? 2 : (baseState ^ 1)));
updateAnimation(true);
@ -179,8 +179,45 @@ struct Block : Controller {
struct Door : Trigger {
int8 *floor[2], orig[2];
Door(TR::Level *level, int entity) : Trigger(level, entity, true) {}
Door(TR::Level *level, int entity) : Trigger(level, entity, true) {
TR::Entity &e = getEntity();
TR::Level::FloorInfo info;
vec3 p = pos - getDir() * 1024.0f;
level->getFloorInfo(e.room, (int)p.x, (int)p.y, (int)p.z, info);
int dx, dz;
TR::Room::Sector *s = &level->getSector(e.room, (int)p.x, (int)p.z, dx, dz);
orig[0] = *(floor[0] = &s->floor);
if (info.roomNext != 0xFF) {
s = &level->getSector(info.roomNext, e.x, e.z, dx, dz);
orig[1] = *(floor[1] = &s->floor);
} else
floor[1] = NULL;
updateBlock();
}
void updateBlock() {
int8 v[2];
if (getEntity().flags.active) {
v[0] = orig[0];
v[1] = orig[1];
} else
v[0] = v[1] = TR::FLOOR_BLOCK;
if (floor[0]) *floor[0] = v[0];
if (floor[1]) *floor[1] = v[1];
}
virtual bool activate(ActionCommand *cmd) {
bool res = Trigger::activate(cmd);
updateBlock();
return res;
}
};
struct DoorFloor : Trigger {
@ -235,7 +272,7 @@ struct TrapFloor : Trigger {
e.room = info.roomBelow;
if (pos.y > info.floor) {
pos.y = info.floor;
pos.y = (float)info.floor;
animation.setState(STATE_DOWN);
}
updateEntity();