1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-16 18:14:05 +02:00

fix trigger parser, fix unavailable sprite indices for EGYPT.PHD, add save crystal interaction (WIP)

This commit is contained in:
XProger
2018-09-29 05:12:06 +03:00
parent a03b0f3cd4
commit b72c9a21e3
10 changed files with 146 additions and 28 deletions

View File

@@ -51,8 +51,8 @@ struct IGame {
virtual ~IGame() {}
virtual void loadLevel(TR::LevelID id) {}
virtual void loadNextLevel() {}
virtual void loadGame(int slot) {}
virtual void saveGame(int slot) {}
virtual void loadGame() {}
virtual void saveGame(int entityIndex) {}
virtual void applySettings(const Core::Settings &settings) {}
virtual TR::Level* getLevel() { return NULL; }
@@ -86,6 +86,7 @@ struct IGame {
virtual void addMuzzleFlash(Controller *owner, int joint, const vec3 &offset, int lightIndex) {}
virtual void invShow(int playerIndex, int page, int itemIndex = -1) {}
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; }
@@ -402,6 +403,7 @@ struct Controller {
}
case TR::FloorData::TRIGGER : {
if (info.trigCmdCount) break;
info.trigger = (TR::Level::Trigger::Type)cmd.sub;
info.trigCmdCount = 0;
info.trigInfo = (*fd++).triggerInfo;

View File

@@ -1803,13 +1803,14 @@ namespace TR {
return type >= DOOR_1 && type <= DOOR_8;
}
bool isCollider() const {
bool isCollider(TR::Entity::Flags flags) const {
return isEnemy() ||
isVehicle() ||
isDoor() ||
(type == CRYSTAL && flags.collision) ||
(type == DRAWBRIDGE && flags.active != ACTIVE) ||
((type == HAMMER_HANDLE || type == HAMMER_BLOCK) && flags.collision) ||
type == CRYSTAL || type == MOVING_OBJECT || type == SCION_HOLDER;
type == MOVING_OBJECT || type == SCION_HOLDER;
}
static bool isPickup(Type type) {
@@ -1818,6 +1819,7 @@ namespace TR {
(type >= KEY_ITEM_1 && type <= KEY_ITEM_4) ||
(type == MEDIKIT_SMALL || type == MEDIKIT_BIG) ||
(type == SCION_PICKUP_QUALOPEC || type == SCION_PICKUP_DROP || type == SCION_PICKUP_HOLDER || type == LEADBAR) ||
(type == CRYSTAL) ||
(type >= SECRET_1 && type <= SECRET_3) ||
(type == M16 || type == AMMO_M16) ||
(type == MP5 || type == AMMO_MP5) ||
@@ -3977,6 +3979,25 @@ namespace TR {
stream.read(s.sStart);
s.sCount = -s.sCount;
}
// remove unavailable sprites (check EGYPT.PHD)
for (int roomIndex = 0; roomIndex < roomsCount; roomIndex++) {
Room::Data &data = rooms[roomIndex].data;
int i = 0;
while (i < data.sCount)
if (data.sprites[i].vertex >= data.vCount || data.sprites[i].texture >= spriteTexturesCount) {
LOG("! room %d has wrong sprite %d (v:%d/%d t:%d/%d)\n", roomIndex, i, data.sprites[i].vertex, data.vCount, data.sprites[i].texture, spriteTexturesCount);
ASSERT(false);
data.sprites[i] = data.sprites[--data.sCount];
} else
i++;
if (!data.sCount && data.sprites) {
delete[] data.sprites;
data.sprites = NULL;
}
}
}
void readAnimTex(Stream &stream) {

View File

@@ -112,6 +112,13 @@ namespace Game {
}
void updateTick() {
Input::update();
if (!level->level.isTitle()) {
if (Input::lastState[0] == cStart) level->addPlayer(0);
if (Input::lastState[1] == cStart) level->addPlayer(1);
}
float dt = Core::deltaTime;
if (Input::down[ikR]) // slow motion (for animation debugging)
Core::deltaTime /= 10.0f;
@@ -144,8 +151,6 @@ namespace Game {
if (level->isEnded)
return true;
Input::update();
/*
if (level->camera) {
if (Input::down[ikV]) { // third <-> first person view
@@ -165,10 +170,6 @@ namespace Game {
Input::down[ikL] = false;
}
*/
if (!level->level.isTitle()) {
if (Input::lastState[0] == cStart) level->addPlayer(0);
if (Input::lastState[1] == cStart) level->addPlayer(1);
}
if (!level->level.isCutsceneLevel())
delta = min(0.2f, delta);

View File

@@ -207,6 +207,7 @@ struct Inventory {
PAGE_OPTION,
PAGE_INVENTORY,
PAGE_ITEMS,
PAGE_SAVEGAME,
PAGE_MAX
};
@@ -701,7 +702,14 @@ struct Inventory {
if (phaseRing == 0.0f || phaseRing == 1.0f) {
active = !active;
vec3 p;
if (curPage == PAGE_SAVEGAME) {
phaseRing = active ? 1.0f : 0.0f;
slot = 1;
} else {
game->playSound(active ? TR::SND_INV_SHOW : TR::SND_INV_HIDE, p);
}
chosen = false;
if (active) {
@@ -1031,7 +1039,20 @@ struct Inventory {
Item *item = items[getGlobalIndex(page, index)];
if (index == targetIndex && targetPage == page && ready) {
if (page == PAGE_SAVEGAME) {
if (Input::lastState[playerIndex] == cLeft || Input::lastState[playerIndex] == cRight)
slot ^= 1;
if (Input::lastState[playerIndex] == cAction) {
if (slot == 1) {
TR::Entity &e = game->getLevel()->entities[index];
Controller *controller = (Controller*)e.controller;
controller->deactivate(true);
}
toggle(playerIndex, targetPage);
}
} else if (index == targetIndex && targetPage == page && ready) {
if (!chosen) {
if ((key == cUp && !canFlipPage(-1)) || (key == cDown && !canFlipPage( 1)))
key = cMAX;
@@ -1092,7 +1113,7 @@ struct Inventory {
}
} else
if (!game->getLevel()->isTitle())
toggle();
toggle(playerIndex, targetPage);
}
}
lastKey = key;
@@ -1233,7 +1254,7 @@ struct Inventory {
// grayscale
Core::setTarget(background[1], RT_STORE_COLOR);
game->setShader(Core::passFilter, Shader::FILTER_GRAYSCALE, false, false);
Core::active.shader->setParam(uParam, vec4(1, 0, 0, 0));
Core::active.shader->setParam(uParam, vec4(0.75f, 0.75f, 1.0f, 1.0f));
background[0]->bind(sDiffuse);
game->getMesh()->renderQuad();
@@ -1646,7 +1667,7 @@ struct Inventory {
void renderUI() {
if (!active || phaseRing < 1.0f) return;
static const StringID pageTitle[PAGE_MAX] = { STR_OPTION, STR_INVENTORY, STR_ITEMS };
static const StringID pageTitle[PAGE_MAX] = { STR_OPTION, STR_INVENTORY, STR_ITEMS, STR_SAVEGAME };
float eye = UI::width * Core::eye * 0.01f;
@@ -1656,6 +1677,15 @@ struct Inventory {
eye = 0.0f;
}
if (page == PAGE_SAVEGAME) {
UI::renderBar(UI::BAR_OPTION, vec2(-eye + UI::width / 2 - 120, 240 - 14), vec2(240, LINE_HEIGHT - 6), 1.0f, 0x802288FF, 0, 0, 0);
UI::textOut(vec2(-eye, 240), pageTitle[page], UI::aCenter, UI::width);
UI::renderBar(UI::BAR_OPTION, vec2(-eye - 48 * slot + UI::width / 2, 240 + 24 - 16), vec2(48, 18), 1.0f, 0xFFD8377C, 0);
UI::textOut(vec2(-eye - 48 + UI::width / 2, 240 + 24), STR_YES, UI::aCenter, 48);
UI::textOut(vec2(-eye + UI::width / 2, 240 + 24), STR_NO, UI::aCenter, 48);
return;
}
if (!game->getLevel()->isTitle())
UI::textOut(vec2(-eye, 32), pageTitle[page], UI::aCenter, UI::width);

View File

@@ -7,6 +7,7 @@
#include "trigger.h"
#include "sprite.h"
#include "enemy.h"
#include "inventory.h"
// TODO: slide to slide in WALL
// TODO: static sounds in LEVEL3A
@@ -1671,12 +1672,26 @@ struct Lara : Character {
Controller *controller = (Controller*)entity.controller;
if (controller->getRoomIndex() != room || controller->flags.invisible || !canPickup(controller))
if (controller->getRoomIndex() != room || controller->flags.invisible)
continue;
if (entity.type == TR::Entity::CRYSTAL) {
if (Input::lastState[camera->cameraIndex] == cAction) {
vec3 dir = controller->pos - pos;
if (dir.length2() < SQR(350.0f) && getDir().dot(dir.normal()) > COS30) {
pickupListCount = 0;
game->invShow(camera->cameraIndex, Inventory::PAGE_SAVEGAME, i);
return true;
}
}
} else {
if (!canPickup(controller))
continue;
ASSERT(pickupListCount < COUNT(pickupList));
pickupList[pickupListCount++] = controller;
}
}
if (pickupListCount > 0) {
state = STATE_PICK_UP;
@@ -3068,10 +3083,10 @@ struct Lara : Character {
for (int i = 0; i < level->entitiesCount; i++) {
const TR::Entity &e = level->entities[i];
if (!e.controller || !e.isCollider()) continue;
Controller *controller = (Controller*)e.controller;
if (!controller || !e.isCollider(controller->flags)) continue;
if (e.isEnemy()) {
if (e.type != TR::Entity::ENEMY_REX && (controller->flags.active != TR::ACTIVE || ((Character*)controller)->health <= 0)) continue;
} else {

View File

@@ -84,7 +84,13 @@ struct Level : IGame {
loadLevel((level.isEnd() || level.isHome()) ? level.getTitleId() : TR::LevelID(level.id + 1));
}
virtual void saveGame(int slot) {
virtual void invShow(int playerIndex, int page, int itemIndex = -1) {
if (itemIndex != -1)
inventory->pageItemIndex[page] = itemIndex;
inventory->toggle(playerIndex, Inventory::Page(page));
}
virtual void saveGame(int entityIndex) {
LOG("Save Game... ");
char *data = new char[sizeof(TR::SaveGame) + sizeof(TR::SaveGame::Item) * inventory->itemsCount + sizeof(TR::SaveGame::CurrentState) + sizeof(TR::SaveGame::Entity) * level.entitiesCount]; // oversized
@@ -141,7 +147,7 @@ struct Level : IGame {
LOG("Ok\n");
}
virtual void loadGame(int slot) {
virtual void loadGame() {
LOG("Load Game... ");
Stream *stream = NULL;//osLoadGame();
@@ -1199,22 +1205,41 @@ struct Level : IGame {
#ifdef _OS_PSP
atlas = new Texture(level.tiles4, level.tilesCount, level.cluts, level.clutsCount);
#else
TR::Tile32 *tiles = new TR::Tile32[level.tilesCount];
Texture::Tile *tiles = new Texture::Tile[level.tilesCount];
for (int i = 0; i < level.tilesCount; i++) {
tiles[i].width = tiles[i].height = 256;
tiles[i].data = new uint32[256 * 256];
}
for (int i = 0; i < level.objectTexturesCount; i++) {
TR::ObjectTexture &t = level.objectTextures[i];
short4 uv = t.getMinMax();
level.fillObjectTexture(&tiles[t.tile.index], uv, t.tile.index, t.clut);
uv.z++;
uv.w++;
level.fillObjectTexture((TR::Tile32*)tiles[t.tile.index].data, uv, t.tile.index, t.clut);
}
for (int i = 0; i < level.spriteTexturesCount; i++) {
TR::SpriteTexture &t = level.spriteTextures[i];
short4 uv = t.getMinMax();
level.fillObjectTexture(&tiles[t.tile], uv, t.tile, t.clut);
uv.z++;
uv.w++;
level.fillObjectTexture((TR::Tile32*)tiles[t.tile].data, uv, t.tile, t.clut);
}
for (int i = 0; i < level.tilesCount; i++) {
char buf[256];
sprintf(buf, "texture/%s_%d.png", TR::LEVEL_INFO[level.id].name, i);
if (Stream::exists(buf)) {
delete[] tiles[i].data;
tiles[i].data = (uint32*)Texture::LoadPNG(Stream(buf), tiles[i].width, tiles[i].height);
}
}
atlas = new Texture(tiles, level.tilesCount);
for (int i = 0; i < level.tilesCount; i++)
delete[] tiles[i].data;
delete[] tiles;
#endif
@@ -1582,6 +1607,8 @@ struct Level : IGame {
inventory->toggle(playerIndex);
}
bool invActive = inventory->isActive();
inventory->update();
if (inventory->titleTimer > 1.0f)
@@ -1591,7 +1618,7 @@ struct Level : IGame {
float volWater, volTrack;
if (inventory->isActive() || level.isTitle()) {
if (invActive || level.isTitle()) {
Sound::reverb.setRoomSize(vec3(1.0f));
volWater = 0.0f;
volTrack = level.isTitle() ? 0.9f : 0.0f;

View File

@@ -31,12 +31,18 @@ struct Texture : GAPI::Texture {
#else
Texture *tiles[32];
Texture(TR::Tile32 *tiles, int tilesCount) : GAPI::Texture(256, 256, OPT_PROXY) {
struct Tile {
uint32 width;
uint32 height;
uint32 *data;
};
Texture(Tile *tiles, int tilesCount) : GAPI::Texture(256, 256, OPT_PROXY) {
memset(this->tiles, 0, sizeof(this->tiles));
ASSERT(tilesCount < COUNT(this->tiles));
for (int i = 0; i < tilesCount; i++)
this->tiles[i] = new Texture(width, height, FMT_RGBA, 0, tiles + i);
this->tiles[i] = new Texture(tiles[i].width, tiles[i].height, FMT_RGBA, OPT_MIPMAPS, tiles[i].data);
}
#endif

View File

@@ -778,6 +778,7 @@ struct Crystal : Controller {
Crystal(IGame *game, int entity) : Controller(game, entity) {
environment = new Texture(64, 64, FMT_RGBA, OPT_CUBEMAP | OPT_MIPMAPS | OPT_TARGET);
flags.collision = true;
activate();
}
@@ -786,6 +787,8 @@ struct Crystal : Controller {
}
virtual void deactivate(bool removeFromList = false) {
flags.invisible = true;
flags.collision = false;
Controller::deactivate(removeFromList);
getRoom().removeDynLight(entity);
}

View File

@@ -13,6 +13,8 @@ enum StringID {
, STR_HELP_TEXT
, STR_OFF
, STR_ON
, STR_YES
, STR_NO
, STR_SPLIT
, STR_VR
, STR_QUALITY_LOW
@@ -33,6 +35,8 @@ enum StringID {
, STR_OPTION
, STR_INVENTORY
, STR_ITEMS
// save game page
, STR_SAVEGAME
// inventory option
, STR_GAME
, STR_MAP
@@ -123,6 +127,8 @@ const char *STR[STR_MAX] = {
, helpText
, "Off"
, "On"
, "YES"
, "NO"
, "Split Screen"
, "VR"
, "Low"
@@ -143,6 +149,8 @@ const char *STR[STR_MAX] = {
, "OPTION"
, "INVENTORY"
, "ITEMS"
// save game page
, "Save Game?"
// inventory option
, "Game"
, "Map"

View File

@@ -63,6 +63,11 @@
#define PI2 (PI * 2.0f)
#define DEG2RAD (PI / 180.0f)
#define RAD2DEG (180.0f / PI)
#define COS30 0.86602540378f
#define COS45 0.70710678118f
#define COS60 0.50000000000f
#define SQR(x) ((x)*(x))
#define randf() ((float)rand()/RAND_MAX)