1
0
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:
XProger
2017-10-09 08:18:09 +03:00
parent 59b6bb3488
commit 6bee3d4eda
11 changed files with 312 additions and 123 deletions

View File

@@ -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];

View File

@@ -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);

View File

@@ -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))

View File

@@ -668,6 +668,7 @@ namespace Core {
memset(rtCache, 0, sizeof(rtCache));
defaultTarget = NULL;
glDepthFunc(GL_LEQUAL);
Sound::init();

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);
}
}
};

View File

@@ -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();

View File

@@ -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

View File

@@ -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() {

View File

@@ -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;