mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-15 01:24:35 +02:00
#22 add midas hand logic and death; #3 multiple pickups, fix surfacing for flipmaps; #11 fix inventory after death; #23 mipmap generation for cubemap textures
This commit is contained in:
@@ -166,8 +166,11 @@ struct Camera : ICamera {
|
||||
if (indexA == level->cameraFramesCount - 1) {
|
||||
if (level->cutEntity != -1)
|
||||
game->loadLevel(TR::LevelID(level->id + 1));
|
||||
else
|
||||
state = STATE_FOLLOW;
|
||||
else {
|
||||
Character *lara = (Character*)game->getLara();
|
||||
if (lara->health > 0.0f)
|
||||
state = STATE_FOLLOW;
|
||||
}
|
||||
}
|
||||
|
||||
TR::CameraFrame *frameA = &level->cameraFrames[indexA];
|
||||
|
@@ -97,7 +97,11 @@ struct Character : Controller {
|
||||
e.room = info.roomBelow;
|
||||
|
||||
if (info.roomAbove != TR::NO_ROOM && e.y <= info.roomCeiling) {
|
||||
if (stand == STAND_UNDERWATER && !level->rooms[info.roomAbove].flags.water) {
|
||||
TR::Room *room = &level->rooms[info.roomAbove];
|
||||
if (level->isFlipped && room->alternateRoom > -1)
|
||||
room = &level->rooms[room->alternateRoom];
|
||||
|
||||
if (stand == STAND_UNDERWATER && !room->flags.water) {
|
||||
stand = STAND_ONWATER;
|
||||
velocity.y = 0;
|
||||
pos.y = float(info.roomCeiling);
|
||||
|
@@ -25,6 +25,7 @@ struct ICamera {
|
||||
|
||||
virtual void setup(bool calcMatrices) {}
|
||||
virtual int getRoomIndex() const { return TR::NO_ROOM; }
|
||||
virtual void doCutscene(const vec3 &pos, float rotation) {}
|
||||
};
|
||||
|
||||
struct IGame {
|
||||
@@ -45,6 +46,7 @@ struct IGame {
|
||||
virtual void setWaterParams(float height) {}
|
||||
virtual void waterDrop(const vec3 &pos, float radius, float strength) {}
|
||||
virtual void setShader(Core::Pass pass, Shader::Type type, bool underwater = false, bool alphaTest = false) {}
|
||||
virtual void setRoomParams(int roomIndex, Shader::Type type, float diffuse, float ambient, float specular, float alpha, bool alphaTest = false) {}
|
||||
virtual void setupBinding() {}
|
||||
virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0) {}
|
||||
virtual void renderCompose(int roomIndex) {}
|
||||
@@ -98,6 +100,7 @@ struct Controller {
|
||||
uint32 mask;
|
||||
} *layers;
|
||||
|
||||
uint32 visibleMask;
|
||||
uint32 explodeMask;
|
||||
struct ExplodePart {
|
||||
Basis basis;
|
||||
@@ -120,6 +123,7 @@ struct Controller {
|
||||
ambient[0] = ambient[1] = ambient[2] = ambient[3] = ambient[4] = ambient[5] = vec3(intensityf(getRoom().ambient));
|
||||
targetLight = NULL;
|
||||
updateLights(false);
|
||||
visibleMask = 0xFFFFFFFF;
|
||||
|
||||
if (e.flags.once) {
|
||||
e.flags.invisible = true;
|
||||
@@ -762,6 +766,7 @@ struct Controller {
|
||||
|
||||
for (int i = MAX_LAYERS - 1; i >= 0; i--) {
|
||||
uint32 vmask = (layers[i].mask & ~mask) | (!i ? explodeMask : 0);
|
||||
vmask &= visibleMask;
|
||||
if (!vmask) continue;
|
||||
mask |= layers[i].mask;
|
||||
// set meshes visibility
|
||||
@@ -769,6 +774,7 @@ struct Controller {
|
||||
joints[j].w = (vmask & (1 << j)) ? 1.0f : -1.0f; // AHAHA
|
||||
|
||||
if (explodeMask) {
|
||||
ASSERT(explodeParts);
|
||||
TR::Model *model = getModel();
|
||||
for (int i = 0; i < model->mCount; i++)
|
||||
if (explodeMask & (1 << i))
|
||||
|
@@ -668,6 +668,7 @@ namespace Core {
|
||||
|
||||
memset(rtCache, 0, sizeof(rtCache));
|
||||
defaultTarget = NULL;
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
|
||||
Sound::init();
|
||||
|
||||
|
22
src/format.h
22
src/format.h
@@ -139,7 +139,7 @@
|
||||
E( PUZZLE_DONE_4 ) \
|
||||
E( LEADBAR ) \
|
||||
E( INV_LEADBAR ) \
|
||||
E( MIDAS_TOUCH ) \
|
||||
E( MIDAS_HAND ) \
|
||||
E( KEY_ITEM_1 ) \
|
||||
E( KEY_ITEM_2 ) \
|
||||
E( KEY_ITEM_3 ) \
|
||||
@@ -340,6 +340,7 @@ namespace TR {
|
||||
HIT_SLAM,
|
||||
HIT_REX,
|
||||
HIT_LIGHTNING,
|
||||
HIT_MIDAS,
|
||||
};
|
||||
|
||||
enum Action : uint16 {
|
||||
@@ -393,8 +394,12 @@ namespace TR {
|
||||
0, -612, 30, {{-300, 0, -692}, {300, 0, -512}}, true, false
|
||||
};
|
||||
|
||||
Limit MIDAS = {
|
||||
512, -612, 30, {{-700, 284, -700}, {700, 996, 700}}, true, false
|
||||
};
|
||||
|
||||
Limit SCION = {
|
||||
640, -202, 30, {{-256, 540, -350}, {256, 740, -200}}, false, false
|
||||
640, -202, 30, {{-256, 540, -350}, {256, 740, -200}}, false, false
|
||||
};
|
||||
}
|
||||
|
||||
@@ -667,14 +672,16 @@ namespace TR {
|
||||
isDoor() ||
|
||||
(type == DRAWBRIDGE && flags.active != ACTIVE) ||
|
||||
(type == SCION_HOLDER) ||
|
||||
((type == HAMMER_HANDLE || type == HAMMER_BLOCK) && flags.collision);
|
||||
((type == HAMMER_HANDLE || type == HAMMER_BLOCK) && flags.collision) ||
|
||||
(type == CRYSTAL);
|
||||
}
|
||||
|
||||
bool isItem() const {
|
||||
bool isPickup() const {
|
||||
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_QUALOPEC || type == SCION_DROP || type == SCION_NATLA || type == LEADBAR); // TODO: recheck all items
|
||||
(type == MEDIKIT_SMALL || type == MEDIKIT_BIG) ||
|
||||
(type == SCION_QUALOPEC || type == SCION_DROP || type == SCION_NATLA || type == LEADBAR); // TODO: recheck all items
|
||||
}
|
||||
|
||||
bool isActor() const {
|
||||
@@ -748,6 +755,7 @@ namespace TR {
|
||||
case KEY_HOLE_2 : return KEY_ITEM_2; break;
|
||||
case KEY_HOLE_3 : return KEY_ITEM_3; break;
|
||||
case KEY_HOLE_4 : return KEY_ITEM_4; break;
|
||||
case MIDAS_HAND : return LEADBAR; break;
|
||||
default : return NONE;
|
||||
}
|
||||
}
|
||||
@@ -1668,6 +1676,10 @@ namespace TR {
|
||||
}
|
||||
}
|
||||
|
||||
bool isCutsceneLevel() {
|
||||
return cutEntity > -1;
|
||||
}
|
||||
|
||||
void readMeshes(Stream &stream) {
|
||||
uint32 meshDataSize;
|
||||
stream.read(meshDataSize);
|
||||
|
@@ -190,6 +190,8 @@ struct Inventory {
|
||||
add(TR::Entity::INV_PUZZLE_2, 3);
|
||||
add(TR::Entity::INV_PUZZLE_3, 3);
|
||||
add(TR::Entity::INV_PUZZLE_4, 3);
|
||||
|
||||
add(TR::Entity::INV_LEADBAR, 3);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -351,6 +353,9 @@ struct Inventory {
|
||||
}
|
||||
|
||||
index = targetIndex = pageItemIndex[page];
|
||||
|
||||
//if (type == TR::Entity::INV_PASSPORT) // toggle after death
|
||||
// chooseItem();
|
||||
}
|
||||
}
|
||||
return active;
|
||||
@@ -563,8 +568,8 @@ struct Inventory {
|
||||
|
||||
if (index == targetIndex && targetPage == page && ready) {
|
||||
if (!chosen) {
|
||||
if (key == cUp && !(page < PAGE_ITEMS && getItemsCount(page + 1))) key = cMAX;
|
||||
if (key == cDown && !(page > PAGE_OPTION && getItemsCount(page - 1))) key = cMAX;
|
||||
if ((key == cUp && !canFlipPage(-1)) || (key == cDown && !canFlipPage( 1)))
|
||||
key = cMAX;
|
||||
|
||||
switch (key) {
|
||||
case cLeft : { phaseSelect = 0.0f; targetIndex = (targetIndex - 1 + count) % count; } break;
|
||||
@@ -577,21 +582,8 @@ struct Inventory {
|
||||
if (index != targetIndex)
|
||||
game->playSound(TR::SND_INV_SPIN);
|
||||
|
||||
if (lastKey != key && key == cAction && phaseChoose == 0.0f) {
|
||||
vec3 p;
|
||||
chosen = true;
|
||||
switch (item->type) {
|
||||
case TR::Entity::INV_COMPASS : game->playSound(TR::SND_INV_COMPASS); break;
|
||||
case TR::Entity::INV_HOME : game->playSound(TR::SND_INV_HOME); break;
|
||||
case TR::Entity::INV_CONTROLS : game->playSound(TR::SND_INV_CONTROLS); break;
|
||||
case TR::Entity::INV_PISTOLS :
|
||||
case TR::Entity::INV_SHOTGUN :
|
||||
case TR::Entity::INV_MAGNUMS :
|
||||
case TR::Entity::INV_UZIS : game->playSound(TR::SND_INV_WEAPON); break;
|
||||
default : game->playSound(TR::SND_INV_SHOW); break;
|
||||
}
|
||||
item->choose();
|
||||
}
|
||||
if (lastKey != key && key == cAction && phaseChoose == 0.0f)
|
||||
chooseItem();
|
||||
} else {
|
||||
if (changeTimer > 0.0f) {
|
||||
changeTimer -= Core::deltaTime;
|
||||
@@ -662,6 +654,32 @@ struct Inventory {
|
||||
game->loadLevel(nextLevel);
|
||||
}
|
||||
|
||||
void chooseItem() {
|
||||
Item *item = items[getGlobalIndex(page, index)];
|
||||
|
||||
vec3 p;
|
||||
chosen = true;
|
||||
switch (item->type) {
|
||||
case TR::Entity::INV_COMPASS : game->playSound(TR::SND_INV_COMPASS); break;
|
||||
case TR::Entity::INV_HOME : game->playSound(TR::SND_INV_HOME); break;
|
||||
case TR::Entity::INV_CONTROLS : game->playSound(TR::SND_INV_CONTROLS); break;
|
||||
case TR::Entity::INV_PISTOLS :
|
||||
case TR::Entity::INV_SHOTGUN :
|
||||
case TR::Entity::INV_MAGNUMS :
|
||||
case TR::Entity::INV_UZIS : game->playSound(TR::SND_INV_WEAPON); break;
|
||||
default : game->playSound(TR::SND_INV_SHOW); break;
|
||||
}
|
||||
item->choose();
|
||||
}
|
||||
|
||||
bool canFlipPage(int dir) {
|
||||
if (((Character*)game->getLara())->health <= 0.0f)
|
||||
return false;
|
||||
if (dir == -1) return page < PAGE_ITEMS && getItemsCount(page + 1);
|
||||
if (dir == 1) return page > PAGE_OPTION && getItemsCount(page - 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
void prepareBackground() {
|
||||
Core::setDepthTest(false);
|
||||
|
||||
@@ -952,7 +970,7 @@ struct Inventory {
|
||||
Core::setDepthTest(true);
|
||||
Core::setBlending(bmAlpha);
|
||||
|
||||
if (game->isCutscene())
|
||||
if (game->getLevel()->isCutsceneLevel())
|
||||
return;
|
||||
|
||||
// items
|
||||
@@ -1002,12 +1020,12 @@ struct Inventory {
|
||||
if (game->getLevel()->id != TR::TITLE)
|
||||
UI::textOut(vec2( 0, 32), pageTitle[page], UI::aCenter, UI::width);
|
||||
|
||||
if (page < PAGE_ITEMS && getItemsCount(page + 1)) {
|
||||
if (canFlipPage(-1)) {
|
||||
UI::textOut(vec2(16, 32), "[", UI::aLeft, UI::width);
|
||||
UI::textOut(vec2( 0, 32), "[", UI::aRight, UI::width - 20);
|
||||
}
|
||||
|
||||
if (page > PAGE_OPTION && getItemsCount(page - 1)) {
|
||||
if (canFlipPage(1)) {
|
||||
UI::textOut(vec2(16, 480 - 16), "]", UI::aLeft, UI::width);
|
||||
UI::textOut(vec2(0, 480 - 16), "]", UI::aRight, UI::width - 20);
|
||||
}
|
||||
|
186
src/lara.h
186
src/lara.h
@@ -226,9 +226,11 @@ struct Lara : Character {
|
||||
} arms[2];
|
||||
|
||||
TR::Entity::Type usedKey;
|
||||
TR::Entity *pickupEntity;
|
||||
int pickupListCount;
|
||||
TR::Entity *pickupList[32];
|
||||
KeyHole *keyHole;
|
||||
Lightning *lightning;
|
||||
Texture *environment;
|
||||
|
||||
int roomPrev; // water out from room
|
||||
vec2 rotFactor;
|
||||
@@ -411,8 +413,9 @@ struct Lara : Character {
|
||||
damageTime = LARA_DAMAGE_TIME;
|
||||
hitTime = 0.0f;
|
||||
|
||||
keyHole = NULL;
|
||||
lightning = NULL;
|
||||
keyHole = NULL;
|
||||
lightning = NULL;
|
||||
environment = NULL;
|
||||
|
||||
getEntity().flags.active = 1;
|
||||
initMeshOverrides();
|
||||
@@ -460,11 +463,12 @@ struct Lara : Character {
|
||||
//reset(24, vec3(45609, 18176, 41500), 90 * DEG2RAD); // level 4 (thor)
|
||||
//reset(19, vec3(41418, -3707, 58863), 270 * DEG2RAD); // level 5 (triangle)
|
||||
//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(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)
|
||||
//reset(77, vec3(36943, -4096, 62821), 270 * DEG2RAD); // level 7b (heavy trigger)
|
||||
//reset(57, vec3(54844, -3328, 53145), 0); // level 8b (bridge switch
|
||||
//reset(12, vec3(34236, -2415, 14974), 0); // level 8b (sphinx)
|
||||
//reset(0, vec3(40913, -1012, 42252), PI); // level 8c
|
||||
//reset(30, vec3(69689, -8448, 34922), 330 * DEG2RAD); // Level 10a (cabin)
|
||||
@@ -488,6 +492,7 @@ struct Lara : Character {
|
||||
|
||||
virtual ~Lara() {
|
||||
delete braid;
|
||||
delete environment;
|
||||
}
|
||||
|
||||
int getRoomByPos(const vec3 &pos) {
|
||||
@@ -517,6 +522,9 @@ struct Lara : Character {
|
||||
if (level->rooms[room].flags.water) {
|
||||
stand = STAND_UNDERWATER;
|
||||
animation.setAnim(ANIM_UNDERWATER);
|
||||
} else {
|
||||
stand = STAND_GROUND;
|
||||
animation.setAnim(ANIM_STAND);
|
||||
}
|
||||
|
||||
velocity = vec3(0.0f);
|
||||
@@ -1408,6 +1416,19 @@ struct Lara : Character {
|
||||
animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation + 1);
|
||||
break;
|
||||
}
|
||||
case TR::HIT_MIDAS : {
|
||||
// generate environment map for reflections
|
||||
if (!environment)
|
||||
environment = new Texture(256, 256, Texture::RGBA, true, NULL, true, true);
|
||||
Core::beginFrame();
|
||||
game->renderEnvironment(73, pos - vec3(0.0f, 384.0f, 0.0f), &environment);
|
||||
environment->generateMipMap();
|
||||
Core::endFrame();
|
||||
// set death animation
|
||||
animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation + 1);
|
||||
game->getCamera()->doCutscene(pos, angle.y);
|
||||
break;
|
||||
}
|
||||
default : ;
|
||||
}
|
||||
|
||||
@@ -1427,6 +1448,8 @@ struct Lara : Character {
|
||||
};
|
||||
|
||||
bool useItem(TR::Entity::Type item) {
|
||||
if (game->isCutscene()) return false;
|
||||
|
||||
switch (item) {
|
||||
case TR::Entity::INV_PISTOLS : wpnChange(Lara::Weapon::PISTOLS); break;
|
||||
case TR::Entity::INV_SHOTGUN : wpnChange(Lara::Weapon::SHOTGUN); break;
|
||||
@@ -1451,6 +1474,17 @@ struct Lara : Character {
|
||||
return false;
|
||||
usedKey = item;
|
||||
break;
|
||||
case TR::Entity::INV_LEADBAR :
|
||||
for (int i = 0; i < level->entitiesCount; i++)
|
||||
if (level->entities[i].type == TR::Entity::MIDAS_HAND) {
|
||||
MidasHand *controller = (MidasHand*)level->entities[i].controller;
|
||||
if (controller->interaction) {
|
||||
controller->invItem = item;
|
||||
return false; // remove item from inventory
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
default : return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1488,46 +1522,68 @@ struct Lara : Character {
|
||||
}
|
||||
|
||||
bool doPickUp() {
|
||||
if ((state != STATE_STOP && state != STATE_TREAD) || !animation.canSetState(STATE_PICK_UP))
|
||||
if (!animation.canSetState(STATE_PICK_UP))
|
||||
return false;
|
||||
|
||||
int room = getRoomIndex();
|
||||
TR::Limits::Limit limit = state == STATE_STOP ? TR::Limits::PICKUP : TR::Limits::PICKUP_UNDERWATER;
|
||||
|
||||
pickupListCount = 0;
|
||||
|
||||
for (int i = 0; i < level->entitiesCount; i++) {
|
||||
TR::Entity &item = level->entities[i];
|
||||
if (!item.isItem())
|
||||
TR::Entity &entity = level->entities[i];
|
||||
if (!entity.isPickup())
|
||||
continue;
|
||||
|
||||
Controller *controller = (Controller*)item.controller;
|
||||
if (controller->getRoomIndex() == room && !item.flags.invisible) {
|
||||
if (stand == STAND_UNDERWATER)
|
||||
controller->angle.x = -25 * DEG2RAD;
|
||||
controller->angle.y = angle.y;
|
||||
Controller *controller = (Controller*)entity.controller;
|
||||
|
||||
if (item.type == TR::Entity::SCION_QUALOPEC)
|
||||
limit = TR::Limits::SCION;
|
||||
// set item orientation to hack limits check
|
||||
if (stand == STAND_UNDERWATER)
|
||||
controller->angle.x = -25 * DEG2RAD;
|
||||
controller->angle.y = angle.y;
|
||||
|
||||
if (!checkInteraction(controller, limit, (input & ACTION) != 0))
|
||||
continue;
|
||||
if (controller->getRoomIndex() != room || entity.flags.invisible || !canPickup(controller))
|
||||
continue;
|
||||
|
||||
if (stand == STAND_UNDERWATER)
|
||||
angle.x = -25 * DEG2RAD;
|
||||
|
||||
pickupEntity = &item;
|
||||
|
||||
if (item.type == TR::Entity::SCION_QUALOPEC) {
|
||||
animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation);
|
||||
((Camera*)level->cameraController)->doCutscene(pos, angle.y);
|
||||
} else
|
||||
state = STATE_PICK_UP;
|
||||
|
||||
return true;
|
||||
}
|
||||
ASSERT(pickupListCount < COUNT(pickupList));
|
||||
pickupList[pickupListCount++] = &entity;
|
||||
}
|
||||
|
||||
if (pickupListCount > 0) {
|
||||
state = STATE_PICK_UP;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool canPickup(Controller *controller) {
|
||||
TR::Entity &entity = controller->getEntity();
|
||||
|
||||
// get limits
|
||||
TR::Limits::Limit *limit;
|
||||
switch (entity.type) {
|
||||
case TR::Entity::SCION_QUALOPEC : limit = &TR::Limits::SCION; break;
|
||||
default : limit = level->rooms[getRoomIndex()].flags.water ? &TR::Limits::PICKUP_UNDERWATER : &TR::Limits::PICKUP;
|
||||
}
|
||||
|
||||
if (!checkInteraction(controller, limit, true))
|
||||
return false;
|
||||
|
||||
if (stand == Character::STAND_UNDERWATER)
|
||||
angle.x = -25 * DEG2RAD;
|
||||
|
||||
// set new state
|
||||
switch (entity.type) {
|
||||
case TR::Entity::SCION_QUALOPEC :
|
||||
animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation);
|
||||
game->getCamera()->doCutscene(pos, angle.y);
|
||||
break;
|
||||
default : ;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int doTutorial(int track) {
|
||||
switch (track) { // GYM tutorial routine
|
||||
case 28 : if (level->tracks[track].once && state == STATE_UP_JUMP) track = 29; break;
|
||||
@@ -1551,22 +1607,22 @@ struct Lara : Character {
|
||||
}
|
||||
|
||||
|
||||
bool checkInteraction(Controller *controller, const TR::Limits::Limit &limit, bool action) {
|
||||
bool checkInteraction(Controller *controller, const TR::Limits::Limit *limit, bool action) {
|
||||
if ((state != STATE_STOP && state != STATE_TREAD && state != STATE_PUSH_PULL_READY) || !action || !emptyHands())
|
||||
return false;
|
||||
|
||||
mat4 m = controller->getMatrix();
|
||||
|
||||
float fx = 0.0f;
|
||||
if (!limit.alignHoriz)
|
||||
if (!limit->alignHoriz)
|
||||
fx = (m.transpose() * vec4(pos - controller->pos, 0.0f)).x;
|
||||
|
||||
vec3 targetPos = controller->pos + (m * vec4(fx, limit.dy, limit.dz, 0.0f)).xyz;
|
||||
vec3 targetPos = controller->pos + (m * vec4(fx, limit->dy, limit->dz, 0.0f)).xyz;
|
||||
|
||||
vec3 deltaAbs = pos - targetPos;
|
||||
vec3 deltaRel = (controller->getMatrix().transpose() * vec4(pos - controller->pos, 0.0f)).xyz; // inverse transform
|
||||
|
||||
if (limit.box.contains(deltaRel)) {
|
||||
if (limit->box.contains(deltaRel)) {
|
||||
float deltaAngY = shortAngle(angle.y, controller->angle.y);
|
||||
|
||||
if (stand == STAND_UNDERWATER) {
|
||||
@@ -1580,9 +1636,9 @@ struct Lara : Character {
|
||||
}
|
||||
}
|
||||
|
||||
if (fabsf(deltaAngY) <= limit.ay * DEG2RAD) {
|
||||
if (fabsf(deltaAngY) <= limit->ay * DEG2RAD) {
|
||||
// align
|
||||
if (limit.alignAngle)
|
||||
if (limit->alignAngle)
|
||||
angle = controller->angle;
|
||||
else
|
||||
angle.x = angle.z = 0.0f;
|
||||
@@ -1624,7 +1680,7 @@ struct Lara : Character {
|
||||
|
||||
if (controller->activeState == asNone) {
|
||||
limit = state == STATE_STOP ? &TR::Limits::SWITCH : &TR::Limits::SWITCH_UNDERWATER;
|
||||
if (checkInteraction(controller, *limit, Input::state[cAction])) {
|
||||
if (checkInteraction(controller, limit, Input::state[cAction])) {
|
||||
actionState = (controller->state == Switch::STATE_DOWN && stand == STAND_GROUND) ? STATE_SWITCH_UP : STATE_SWITCH_DOWN;
|
||||
if (animation.setState(actionState))
|
||||
controller->activate();
|
||||
@@ -1651,7 +1707,7 @@ struct Lara : Character {
|
||||
return;
|
||||
|
||||
limit = actionState == STATE_USE_PUZZLE ? &TR::Limits::PUZZLE_HOLE : &TR::Limits::KEY_HOLE;
|
||||
if (!checkInteraction(controller, *limit, isPressed(ACTION) || usedKey != TR::Entity::NONE))
|
||||
if (!checkInteraction(controller, limit, isPressed(ACTION) || usedKey != TR::Entity::NONE))
|
||||
return;
|
||||
|
||||
if (usedKey == TR::Entity::NONE) {
|
||||
@@ -2010,7 +2066,7 @@ struct Lara : Character {
|
||||
float oldAngle = block->angle.y;
|
||||
block->angle.y = angleQuadrant(angle.y) * (PI * 0.5f);
|
||||
|
||||
if (!checkInteraction(block, TR::Limits::BLOCK, (input & ACTION) != 0)) {
|
||||
if (!checkInteraction(block, &TR::Limits::BLOCK, (input & ACTION) != 0)) {
|
||||
block->angle.y = oldAngle;
|
||||
continue;
|
||||
}
|
||||
@@ -2024,7 +2080,7 @@ struct Lara : Character {
|
||||
int res = STATE_STOP;
|
||||
angle.x = 0.0f;
|
||||
|
||||
if ((state == STATE_STOP || state == STATE_TREAD) && (input & ACTION) && emptyHands() && doPickUp())
|
||||
if ((input == ACTION) && (state == STATE_STOP) && emptyHands() && doPickUp())
|
||||
return state;
|
||||
|
||||
if ((input & (FORTH | ACTION)) == (FORTH | ACTION) && (animation.index == ANIM_STAND || animation.index == ANIM_STAND_NORMAL) && emptyHands() && collision.side == Collision::FRONT) { // TODO: get rid of animation.index
|
||||
@@ -2209,7 +2265,7 @@ struct Lara : Character {
|
||||
}
|
||||
|
||||
virtual int getStateUnderwater() {
|
||||
if (input == ACTION && doPickUp())
|
||||
if ((input == ACTION) && (state == STATE_TREAD) && emptyHands() && doPickUp())
|
||||
return state;
|
||||
|
||||
if (state == STATE_FORWARD_JUMP || state == STATE_UP_JUMP || state == STATE_BACK_JUMP || state == STATE_LEFT_JUMP || state == STATE_RIGHT_JUMP || state == STATE_FALL || state == STATE_REACH || state == STATE_SLIDE || state == STATE_SLIDE_BACK) {
|
||||
@@ -2335,6 +2391,12 @@ struct Lara : Character {
|
||||
case TR::LEVEL_4 :
|
||||
reset(18, vec3(34914, 11008, 41315), 90 * DEG2RAD); // main hall
|
||||
break;
|
||||
case TR::LEVEL_6 :
|
||||
reset(73, vec3(73372, 122, 51687), PI * 0.5f); // level 6 (midas hand)
|
||||
break;
|
||||
case TR::LEVEL_7B :
|
||||
reset(77, vec3(36943, -4096, 62821), 270 * DEG2RAD); // level 7b (heavy trigger)
|
||||
break;
|
||||
default : game->playSound(TR::SND_NO, pos, Sound::PAN);
|
||||
}
|
||||
}
|
||||
@@ -2372,13 +2434,13 @@ struct Lara : Character {
|
||||
virtual void doCustomCommand(int curFrame, int prevFrame) {
|
||||
switch (state) {
|
||||
case STATE_PICK_UP : {
|
||||
if (pickupEntity && !pickupEntity->flags.invisible) {
|
||||
int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER;
|
||||
if (animation.isFrameActive(pickupFrame)) {
|
||||
pickupEntity->flags.invisible = true;
|
||||
game->invAdd(pickupEntity->type, 1);
|
||||
pickupEntity = NULL;
|
||||
int pickupFrame = stand == STAND_GROUND ? PICKUP_FRAME_GROUND : PICKUP_FRAME_UNDERWATER;
|
||||
if (animation.isFrameActive(pickupFrame)) {
|
||||
for (int i = 0; i < pickupListCount; i++) {
|
||||
pickupList[i]->flags.invisible = true;
|
||||
game->invAdd(pickupList[i]->type, 1);
|
||||
}
|
||||
pickupListCount = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2814,6 +2876,24 @@ struct Lara : Character {
|
||||
velocity = v * (sink.speed * 8.0f);
|
||||
}
|
||||
|
||||
uint32 getMidasDeathMask() {
|
||||
uint32 mask = 0;
|
||||
int frame = animation.frameIndex;
|
||||
if (frame > 4 ) mask |= BODY_LEG_L3 | BODY_LEG_R3;
|
||||
if (frame > 69 ) mask |= BODY_LEG_L2;
|
||||
if (frame > 79 ) mask |= BODY_LEG_L1;
|
||||
if (frame > 99 ) mask |= BODY_LEG_R2;
|
||||
if (frame > 119) mask |= BODY_LEG_R1 | BODY_HIP;
|
||||
if (frame > 134) mask |= BODY_CHEST;
|
||||
if (frame > 149) mask |= BODY_ARM_L1;
|
||||
if (frame > 162) mask |= BODY_ARM_L2;
|
||||
if (frame > 173) mask |= BODY_ARM_L3;
|
||||
if (frame > 185) mask |= BODY_ARM_R1;
|
||||
if (frame > 194) mask |= BODY_ARM_R2;
|
||||
if (frame > 217) mask |= BODY_ARM_R3;
|
||||
if (frame > 224) mask |= BODY_HEAD;
|
||||
return mask;
|
||||
}
|
||||
|
||||
void renderMuzzleFlash(MeshBuilder *mesh, const Basis &basis, const vec3 &offset, float time) {
|
||||
ASSERT(level->extra.muzzleFlash);
|
||||
@@ -2843,6 +2923,16 @@ struct Lara : Character {
|
||||
renderMuzzleFlash(mesh, animation.getJoints(matrix, 13, true), vec3( 10, -50, 150), arms[1].shotTimer);
|
||||
Core::setBlending(bmNone);
|
||||
}
|
||||
|
||||
if (state == STATE_DIE_MIDAS) {
|
||||
game->setRoomParams(getRoomIndex(), Shader::MIRROR, 1.2f, 1.0f, 0.2f, 1.0f, false);
|
||||
environment->bind(sEnvironment);
|
||||
Core::setBlending(bmAlpha);
|
||||
visibleMask = getMidasDeathMask();
|
||||
Controller::render(frustum, mesh, type, caustics);
|
||||
visibleMask = 0xFFFFFFFF;
|
||||
Core::setBlending(bmNone);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
71
src/level.h
71
src/level.h
@@ -163,6 +163,34 @@ struct Level : IGame {
|
||||
shaderCache->bind(pass, type, (underwater ? ShaderCache::FX_UNDERWATER : 0) | (alphaTest ? ShaderCache::FX_ALPHA_TEST : 0) | ((params->clipHeight != NO_CLIP_PLANE && pass == Core::passCompose) ? ShaderCache::FX_CLIP_PLANE : 0), this);
|
||||
}
|
||||
|
||||
virtual void setRoomParams(int roomIndex, Shader::Type type, float diffuse, float ambient, float specular, float alpha, bool alphaTest = false) {
|
||||
if (Core::pass == Core::passShadow) {
|
||||
setShader(Core::pass, type);
|
||||
return;
|
||||
}
|
||||
|
||||
TR::Room &room = level.rooms[roomIndex];
|
||||
|
||||
if (room.flags.water)
|
||||
setWaterParams(float(room.info.yTop));
|
||||
else
|
||||
setWaterParams(NO_CLIP_PLANE);
|
||||
|
||||
setShader(Core::pass, type, room.flags.water, alphaTest);
|
||||
|
||||
if (room.flags.water) {
|
||||
if (waterCache)
|
||||
waterCache->bindCaustics(roomIndex);
|
||||
setWaterParams(float(room.info.yTop));
|
||||
}
|
||||
|
||||
Core::active.shader->setParam(uParam, Core::params);
|
||||
Core::active.shader->setParam(uMaterial, vec4(diffuse, ambient, specular, alpha));
|
||||
|
||||
if (Core::settings.detail.shadows > Core::Settings::MEDIUM)
|
||||
Core::active.shader->setParam(uContacts, Core::contacts[0], MAX_CONTACTS);
|
||||
}
|
||||
|
||||
virtual void setupBinding() {
|
||||
atlas->bind(sDiffuse);
|
||||
Core::whiteTex->bind(sNormal);
|
||||
@@ -489,6 +517,7 @@ struct Level : IGame {
|
||||
case TR::Entity::KEY_HOLE_2 :
|
||||
case TR::Entity::KEY_HOLE_3 :
|
||||
case TR::Entity::KEY_HOLE_4 : return new KeyHole(this, index);
|
||||
case TR::Entity::MIDAS_HAND : return new MidasHand(this, index);
|
||||
case TR::Entity::SCION_TARGET : return new ScionTarget(this, index);
|
||||
case TR::Entity::WATERFALL : return new Waterfall(this, index);
|
||||
case TR::Entity::TRAP_LAVA : return new TrapLava(this, index);
|
||||
@@ -729,38 +758,12 @@ struct Level : IGame {
|
||||
if (e.type == TR::Entity::CRYSTAL) {
|
||||
Crystal *c = (Crystal*)e.controller;
|
||||
renderEnvironment(c->getRoomIndex(), c->pos - vec3(0, 512, 0), &c->environment);
|
||||
c->environment->generateMipMap();
|
||||
}
|
||||
}
|
||||
Core::endFrame();
|
||||
}
|
||||
|
||||
void setRoomParams(int roomIndex, Shader::Type type, float diffuse, float ambient, float specular, float alpha, bool alphaTest = false) {
|
||||
if (Core::pass == Core::passShadow) {
|
||||
setShader(Core::pass, type);
|
||||
return;
|
||||
}
|
||||
|
||||
TR::Room &room = level.rooms[roomIndex];
|
||||
|
||||
if (room.flags.water)
|
||||
setWaterParams(float(room.info.yTop));
|
||||
else
|
||||
setWaterParams(NO_CLIP_PLANE);
|
||||
|
||||
setShader(Core::pass, type, room.flags.water, alphaTest);
|
||||
|
||||
if (room.flags.water) {
|
||||
if (waterCache)
|
||||
waterCache->bindCaustics(roomIndex);
|
||||
setWaterParams(float(room.info.yTop));
|
||||
}
|
||||
|
||||
Core::active.shader->setParam(uParam, Core::params);
|
||||
Core::active.shader->setParam(uMaterial, vec4(diffuse, ambient, specular, alpha));
|
||||
if (Core::settings.detail.shadows > Core::Settings::MEDIUM)
|
||||
Core::active.shader->setParam(uContacts, Core::contacts[0], MAX_CONTACTS);
|
||||
}
|
||||
|
||||
void setMainLight(Controller *controller) {
|
||||
Core::lightPos[0] = controller->mainLightPos;
|
||||
Core::lightColor[0] = vec4(controller->mainLightColor.xyz, 1.0f / controller->mainLightColor.w);
|
||||
@@ -874,7 +877,7 @@ struct Level : IGame {
|
||||
|
||||
if (type == Shader::SPRITE) {
|
||||
float alpha = (entity.type == TR::Entity::SMOKE || entity.type == TR::Entity::WATER_SPLASH) ? 0.75f : 1.0f;
|
||||
float diffuse = entity.isItem() ? 1.0f : 0.5f;
|
||||
float diffuse = entity.isPickup() ? 1.0f : 0.5f;
|
||||
setRoomParams(roomIndex, type, diffuse, intensityf(lum), controller->specular, alpha, isModel ? !mesh->models[entity.modelIndex - 1].opaque : true);
|
||||
} else
|
||||
setRoomParams(roomIndex, type, 1.0f, intensityf(lum), controller->specular, 1.0f, isModel ? !mesh->models[entity.modelIndex - 1].opaque : true);
|
||||
@@ -904,11 +907,15 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
void update() {
|
||||
if (isCutscene() && !sndSoundtrack)
|
||||
if (isCutscene() && (lara->health > 0.0f && !sndSoundtrack))
|
||||
return;
|
||||
|
||||
if (Input::state[cInventory] && level.id != TR::TITLE)
|
||||
inventory.toggle();
|
||||
if (Input::state[cInventory] && level.id != TR::TITLE) {
|
||||
if (lara->health <= 0.0f)
|
||||
inventory.toggle(Inventory::PAGE_OPTION, TR::Entity::INV_PASSPORT);
|
||||
else
|
||||
inventory.toggle();
|
||||
}
|
||||
|
||||
Sound::Sample *sndChanged = sndCurrent;
|
||||
|
||||
@@ -1477,7 +1484,7 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
void renderUI() {
|
||||
if (isCutscene()) return;
|
||||
if (level.isCutsceneLevel()) return;
|
||||
|
||||
UI::begin();
|
||||
|
||||
|
@@ -45,7 +45,7 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha
|
||||
|
||||
#ifdef VERTEX
|
||||
|
||||
#ifdef TYPE_ENTITY
|
||||
#if defined(TYPE_ENTITY) || defined(TYPE_MIRROR)
|
||||
uniform vec4 uBasis[32 * 2];
|
||||
#else
|
||||
uniform vec4 uBasis[2];
|
||||
@@ -89,7 +89,7 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha
|
||||
}
|
||||
|
||||
vec4 _transform() {
|
||||
#ifdef TYPE_ENTITY
|
||||
#if defined(TYPE_ENTITY) || defined(TYPE_MIRROR)
|
||||
int index = int(aCoord.w * 2.0);
|
||||
vec4 rBasisRot = uBasis[index];
|
||||
vec4 rBasisPos = uBasis[index + 1];
|
||||
@@ -147,7 +147,7 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha
|
||||
#endif
|
||||
|
||||
#ifdef TYPE_MIRROR
|
||||
vDiffuse.xyz = vec3(0.5, 0.5, 2.0); // blue color dodge for crystal
|
||||
vDiffuse.xyz = uMaterial.xyz;
|
||||
#endif
|
||||
|
||||
#ifdef TYPE_FLASH
|
||||
|
@@ -10,8 +10,9 @@ struct Texture {
|
||||
int width, height;
|
||||
Format format;
|
||||
bool cube;
|
||||
bool filter;
|
||||
|
||||
Texture(int width, int height, Format format, bool cube = false, void *data = NULL, bool filter = true, bool mips = false) : cube(cube) {
|
||||
Texture(int width, int height, Format format, bool cube = false, void *data = NULL, bool filter = true, bool mips = false) : cube(cube), filter(filter) {
|
||||
if (!Core::support.texNPOT) {
|
||||
width = nextPow2(width);
|
||||
height = nextPow2(height);
|
||||
@@ -117,12 +118,20 @@ struct Texture {
|
||||
if (!cube) break;
|
||||
}
|
||||
|
||||
if (mips) {
|
||||
glGenerateMipmap(target);
|
||||
if (!cube && filter && (Core::support.maxAniso > 0))
|
||||
glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, min(int(Core::support.maxAniso), 8));
|
||||
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4);
|
||||
}
|
||||
if (mips)
|
||||
generateMipMap();
|
||||
|
||||
this->filter = filter;
|
||||
}
|
||||
|
||||
void generateMipMap() {
|
||||
bind(0);
|
||||
GLenum target = cube ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D;
|
||||
|
||||
glGenerateMipmap(target);
|
||||
if (!cube && filter && (Core::support.maxAniso > 0))
|
||||
glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, min(int(Core::support.maxAniso), 8));
|
||||
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4);
|
||||
}
|
||||
|
||||
virtual ~Texture() {
|
||||
|
@@ -621,7 +621,7 @@ struct Crystal : Controller {
|
||||
Texture *environment;
|
||||
|
||||
Crystal(IGame *game, int entity) : Controller(game, entity) {
|
||||
environment = new Texture(64, 64, Texture::RGBA, true);
|
||||
environment = new Texture(64, 64, Texture::RGBA, true, NULL, true, true);
|
||||
activate();
|
||||
}
|
||||
|
||||
@@ -634,8 +634,7 @@ struct Crystal : Controller {
|
||||
}
|
||||
|
||||
virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) {
|
||||
Shader *sh = Core::active.shader;
|
||||
sh->setParam(uMaterial, vec4(1.0f));
|
||||
Core::active.shader->setParam(uMaterial, vec4(0.5, 0.5, 2.0, 1.0f)); // blue color dodge for crystal
|
||||
environment->bind(sEnvironment);
|
||||
Controller::render(frustum, mesh, type, caustics);
|
||||
}
|
||||
@@ -803,8 +802,8 @@ struct TrapSword : Controller {
|
||||
angle.y += rot * Core::deltaTime;
|
||||
applyGravity(dir.y);
|
||||
pos += dir * (30.0f * Core::deltaTime);
|
||||
if (pos.y > info.floor) {
|
||||
pos.y = info.floor;
|
||||
if (pos.y > float(info.floor)) {
|
||||
pos.y = float(info.floor);
|
||||
game->playSound(TR::SND_SWORD, pos, Sound::PAN);
|
||||
deactivate(true);
|
||||
}
|
||||
@@ -1017,6 +1016,46 @@ struct Lightning : Controller {
|
||||
}
|
||||
};
|
||||
|
||||
struct MidasHand : Controller {
|
||||
TR::Entity::Type invItem;
|
||||
bool interaction;
|
||||
|
||||
MidasHand(IGame *game, int entity) : Controller(game, entity), invItem(TR::Entity::NONE), interaction(false) {
|
||||
activate();
|
||||
}
|
||||
|
||||
virtual void update() {
|
||||
Character *lara = (Character*)level->laraController;
|
||||
|
||||
if (lara->health <= 0.0f || lara->stand != Character::STAND_GROUND || lara->getRoomIndex() != getRoomIndex())
|
||||
return;
|
||||
|
||||
vec3 d = (pos - lara->pos).abs();
|
||||
|
||||
if (d.x < 512.0f && d.z < 512.0f) { // check for same sector
|
||||
lara->hit(1001.0f, this, TR::HIT_MIDAS);
|
||||
deactivate(true);
|
||||
return;
|
||||
}
|
||||
|
||||
interaction = (d.x < 700.0f && d.z < 700.0f) && lara->state == 2; // 2 = Lara::STATE_STOP
|
||||
|
||||
if (interaction) {
|
||||
if (invItem != TR::Entity::NONE) {
|
||||
if (invItem == TR::Entity::INV_LEADBAR) {
|
||||
lara->angle.y = PI * 0.5f;
|
||||
lara->pos.x = pos.x - 612.0f;
|
||||
lara->animation.setAnim(level->models[TR::MODEL_LARA_SPEC].animation);
|
||||
game->invAdd(TR::Entity::PUZZLE_1);
|
||||
} else
|
||||
game->playSound(TR::SND_NO, pos, Sound::PAN); // uncompatible item
|
||||
invItem = TR::Entity::NONE;
|
||||
} else if (Input::state[cAction] && !game->invChooseKey(getEntity().type)) // TODO: add callback for useItem
|
||||
game->playSound(TR::SND_NO, pos, Sound::PAN); // no compatible items in inventory
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct TrapLava : Controller {
|
||||
bool done;
|
||||
|
||||
|
Reference in New Issue
Block a user