/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "common/file.h"
#include "common/path.h"
#include "common/textconsole.h"
#include "tetraedge/tetraedge.h"
#include "tetraedge/game/application.h"
#include "tetraedge/game/billboard.h"
#include "tetraedge/game/syberia_game.h"
#include "tetraedge/game/in_game_scene.h"
#include "tetraedge/game/in_game_scene_xml_parser.h"
#include "tetraedge/game/character.h"
#include "tetraedge/game/characters_shadow.h"
#include "tetraedge/game/object3d.h"
#include "tetraedge/game/particle_xml_parser.h"
#include "tetraedge/game/scene_lights_xml_parser.h"
#include "tetraedge/te/te_bezier_curve.h"
#include "tetraedge/te/te_camera.h"
#include "tetraedge/te/te_core.h"
#include "tetraedge/te/te_free_move_zone.h"
#include "tetraedge/te/te_light.h"
#include "tetraedge/te/te_renderer.h"
#include "tetraedge/te/te_lua_script.h"
#include "tetraedge/te/te_lua_thread.h"
//#define TETRAEDGE_DEBUG_PATHFINDING
//#define TETRAEDGE_DEBUG_LIGHTS
namespace Tetraedge {
/*static*/
const int InGameScene::MAX_FIRE = 50;
const int InGameScene::MAX_SNOW = 250;
const int InGameScene::MAX_SMOKE = 350;
const float InGameScene::DUREE_MAX_FIRE = 32000.0f;
const float InGameScene::SCALE_FIRE = 0.1f;
const int InGameScene::MAX_FLAKE = 10;
const float InGameScene::DUREE_MIN_FLAKE = 3000.0f;
const float InGameScene::DUREE_MAX_FLAKE = 5000.0f;
const float InGameScene::SCALE_FLAKE = 0.1f;
const float InGameScene::DEPTH_MAX_FLAKE = 0.1f;
InGameScene::InGameScene() : _character(nullptr), _charactersShadow(nullptr),
_shadowLightNo(-1), _waitTime(-1.0), _shadowColor(0, 0, 0, 0x80), _shadowFov(20.0f),
_shadowFarPlane(1000), _shadowNearPlane(1), _maskAlpha(false),
_verticalScrollTime(1000000.0f), _verticalScrollPlaying(false) {
}
void InGameScene::activateAnchorZone(const Common::String &name, bool val) {
for (AnchorZone *zone : _anchorZones) {
if (zone->_name == name)
zone->_activated = val;
}
}
void InGameScene::addAnchorZone(const Common::String &s1, const Common::String &name, float radius) {
for (AnchorZone *zone : _anchorZones) {
if (zone->_name == name) {
zone->_radius = radius;
return;
}
}
assert(currentCamera());
currentCamera()->apply();
AnchorZone *zone = new AnchorZone();
zone->_name = name;
zone->_radius = radius;
zone->_activated = true;
if (s1.contains("Int")) {
TeButtonLayout *btn = hitObjectGui().buttonLayoutChecked(name);
TeVector3f32 pos = btn->position();
pos.x() += g_engine->getDefaultScreenWidth() / 2.0f;
pos.y() += g_engine->getDefaultScreenHeight() / 2.0f;
zone->_loc = currentCamera()->worldTransformationMatrix() * currentCamera()->transformPoint2Dto3D(pos);
} else {
if (s1.contains("Dummy")) {
Dummy d = dummy(name);
zone->_loc = d._position;
}
}
_anchorZones.push_back(zone);
}
bool InGameScene::addMarker(const Common::String &markerName, const Common::Path &imgPath, float x, float y, const Common::String &locType, const Common::String &markerVal, float anchorX, float anchorY) {
const TeMarker *marker = findMarker(markerName);
if (!marker) {
SyberiaGame *game = dynamic_cast(g_engine->getGame());
assert(game);
Application *app = g_engine->getApplication();
TeSpriteLayout *markerSprite = new TeSpriteLayout();
// Note: game checks paths here but seems to just use the original?
markerSprite->setName(markerName);
markerSprite->setAnchor(TeVector3f32(anchorX, anchorY, 0.0f));
markerSprite->load(imgPath);
markerSprite->setSizeType(TeILayout::RELATIVE_TO_PARENT);
markerSprite->setPositionType(TeILayout::RELATIVE_TO_PARENT);
TeVector3f32 newPos;
if (locType == "PERCENT") {
TeVector3f32 parentSize;
//if (g_engine->gameType() == TetraedgeEngine::kSyberia)
parentSize = app->frontLayout().userSize();
//else
// parentSize = app->getMainWindow().size();
newPos.x() = parentSize.x() * (x / 100.0f);
newPos.y() = parentSize.y() * (y / 100.0f);
} else {
newPos.x() = x / g_engine->getDefaultScreenWidth();
newPos.y() = y / g_engine->getDefaultScreenHeight();
}
markerSprite->setPosition(newPos);
const TeVector3f32 winSize = app->getMainWindow().size();
float xscale = 1.0f;
float yscale = 1.0f;
// Originally this is only done in Syberia 2, but
// should be fine to calculate in Syberia 1, as long
// as the root layout is loaded.
TeLayout *bglayout = _bgGui.layoutChecked("background");
TeSpriteLayout *rootlayout = Game::findSpriteLayoutByName(bglayout, "root");
if (rootlayout && rootlayout->_tiledSurfacePtr && rootlayout->_tiledSurfacePtr->tiledTexture()) {
TeVector2s32 bgSize = rootlayout->_tiledSurfacePtr->tiledTexture()->totalSize();
xscale = 800.0f / bgSize._x;
yscale = 600.0f / bgSize._y;
}
if (g_engine->getCore()->fileFlagSystemFlag("definition") == "SD") {
markerSprite->setSize(TeVector3f32(xscale * 0.07f, yscale * (4.0f / ((winSize.y() / winSize.x()) * 4.0f)) * 0.07f, 0.0));
} else {
markerSprite->setSize(TeVector3f32(xscale * 0.04f, yscale * (4.0f / ((winSize.y() / winSize.x()) * 4.0f)) * 0.04f, 0.0));
}
markerSprite->setVisible(game->markersVisible());
markerSprite->_tiledSurfacePtr->_frameAnim.setLoopCount(-1);
markerSprite->play();
TeMarker newMarker;
newMarker._name = markerName;
newMarker._val = markerVal;
_markers.push_back(newMarker);
TeLayout *bg = game->forGui().layout("background");
if (bg)
bg->addChild(markerSprite);
_sprites.push_back(markerSprite);
} else {
setImagePathMarker(markerName, imgPath);
}
return true;
}
/*static*/
float InGameScene::angularDistance(float a1, float a2) {
float result = a2 - a1;
if (result >= -M_PI && result > M_PI) {
result = result - (M_PI * 2);
} else {
result = result + (M_PI * 2);
}
return result;
}
bool InGameScene::aroundAnchorZone(const AnchorZone *zone) {
if (!zone->_activated)
return false;
const TeVector3f32 charpos = _character->_model->position();
float xoff = charpos.x() - zone->_loc.x();
float zoff = charpos.z() - zone->_loc.z();
return sqrt(xoff * xoff + zoff * zoff) <= zone->_radius;
}
TeLayout *InGameScene::background() {
return _bgGui.layout("background");
}
Billboard *InGameScene::billboard(const Common::String &name) {
for (Billboard *billboard : _billboards) {
if (billboard->model()->name() == name)
return billboard;
}
return nullptr;
}
bool InGameScene::changeBackground(const Common::Path &name) {
Common::Path path = g_engine->getCore()->findFile(name);
if (Common::File::exists(path)) {
_bgGui.spriteLayoutChecked("root")->load(path);
if (g_engine->gameType() == TetraedgeEngine::kSyberia2)
_bgGui.spriteLayoutChecked("root")->play();
return true;
}
return false;
}
Character *InGameScene::character(const Common::String &name) {
if (_character && _character->_model->name() == name)
return _character;
for (Character *c : _characters) {
if (c->_model->name() == name)
return c;
}
// WORKAROUND: Didn't find char, try again with case insensitive
// for "OScar" typo in scenes/ValTrain/19000.
for (Character *c : _characters) {
if (c->_model->name().compareToIgnoreCase(name) == 0)
return c;
}
return nullptr;
}
void InGameScene::close() {
reset();
_loadedPath = "";
TeScene::close();
freeGeometry();
if (_character && _character->_model && !findKate()) {
models().push_back(_character->_model);
if (_character->_shadowModel[0]) {
models().push_back(_character->_shadowModel[0]);
models().push_back(_character->_shadowModel[1]);
}
}
_objects.clear();
for (TeFreeMoveZone *zone : _freeMoveZones)
delete zone;
_freeMoveZones.clear();
_hitObjects.clear();
for (TePickMesh2 *mesh : _clickMeshes)
delete mesh;
_clickMeshes.clear();
_bezierCurves.clear();
_dummies.clear();
freeSceneObjects();
}
void InGameScene::convertPathToMesh(TeFreeMoveZone *zone) {
TeIntrusivePtr model = new TeModel();
model->meshes().clear();
model->setMeshCount(1);
model->setName("shadowReceiving");
model->setPosition(zone->position());
model->setRotation(zone->rotation());
model->setScale(zone->scale());
uint64 nverticies = zone->freeMoveZoneVerticies().size();
TeMesh *mesh0 = model->meshes()[0].get();
mesh0->setConf(nverticies, nverticies, TeMesh::MeshMode_Triangles, 0, 0);
for (uint i = 0; i < nverticies; i++) {
mesh0->setIndex(i, i);
mesh0->setVertex(i, zone->freeMoveZoneVerticies()[i]);
mesh0->setNormal(i, TeVector3f32(0, 0, 1));
}
_zoneModels.push_back(model);
}
TeIntrusivePtr InGameScene::curve(const Common::String &curveName) {
for (TeIntrusivePtr &c : _bezierCurves) {
if (c->name() == curveName)
return c;
}
return TeIntrusivePtr();
}
void InGameScene::deleteAllCallback() {
for (auto &pair : _callbacks) {
for (auto *cb : pair._value) {
delete cb;
}
pair._value.clear();
}
_callbacks.clear();
}
void InGameScene::deleteMarker(const Common::String &markerName) {
if (!isMarker(markerName))
return;
for (uint i = 0; i < _markers.size(); i++) {
if (_markers[i]._name == markerName) {
_markers.remove_at(i);
break;
}
}
Game *game = g_engine->getGame();
TeLayout *bg = game->forGui().layout("background");
if (!bg)
return;
for (Te3DObject2 *child : bg->childList()) {
if (child->name() == markerName) {
bg->removeChild(child);
break;
}
}
}
void InGameScene::deserializeCam(Common::ReadStream &stream, TeIntrusivePtr &cam) {
cam->setProjMatrixType(2);
cam->viewport(0, 0, _viewportSize.getX(), _viewportSize.getY());
// load name/position/rotation/scale
Te3DObject2::deserialize(stream, *cam);
cam->setFov(stream.readFloatLE());
cam->setAspectRatio(stream.readFloatLE());
// Original loads the second val then ignores it and sets 3000.
cam->setOrthoPlanes(stream.readFloatLE(), 3000.0);
stream.readFloatLE();
}
void InGameScene::deserializeModel(Common::ReadStream &stream, TeIntrusivePtr &model, TePickMesh2 *pickmesh) {
TeVector3f32 vec;
TeVector2f32 vec2;
TeQuaternion rot;
TeColor col;
Common::SharedPtr mesh(TeMesh::makeInstance());
assert(pickmesh);
TeVector3f32::deserialize(stream, vec);
model->setPosition(vec);
pickmesh->setPosition(vec);
TeQuaternion::deserialize(stream, rot);
model->setRotation(rot);
pickmesh->setRotation(rot);
TeVector3f32::deserialize(stream, vec);
model->setScale(vec);
pickmesh->setScale(vec);
uint32 indexcount = stream.readUint32LE();
uint32 vertexcount = stream.readUint32LE();
if (indexcount > 100000 || vertexcount > 100000)
error("InGameScene::deserializeModel: Unxpected counts %d %d", indexcount, vertexcount);
mesh->setConf(vertexcount, indexcount, TeMesh::MeshMode_Triangles, 0, 0);
for (uint i = 0; i < indexcount; i++)
mesh->setIndex(i, stream.readUint32LE());
for (uint i = 0; i < vertexcount; i++) {
TeVector3f32::deserialize(stream, vec);
mesh->setVertex(i, vec);
}
for (uint i = 0; i < vertexcount; i++) {
TeVector3f32::deserialize(stream, vec);
mesh->setNormal(i, vec);
}
for (uint i = 0; i < vertexcount; i++) {
TeVector2f32::deserialize(stream, vec2);
mesh->setTextureUV(i, vec2);
}
for (uint i = 0; i < vertexcount; i++) {
col.deserialize(stream);
mesh->setColor(i, col);
}
pickmesh->setNbTriangles(indexcount / 3);
for (uint i = 0; i < indexcount; i++) {
vec = mesh->vertex(mesh->index(i));
pickmesh->verticies()[i] = vec;
}
model->addMesh(mesh);
}
void InGameScene::draw() {
if (currentCameraIndex() >= (int)cameras().size())
return;
currentCamera()->apply();
drawMask();
drawReflection();
#ifdef TETRAEDGE_DEBUG_PATHFINDING
if (_character && _character->curve()) {
_character->curve()->setVisible(true);
_character->curve()->draw();
}
for (TeFreeMoveZone *zone : _freeMoveZones) {
zone->setVisible(true);
zone->draw();
}
for (TePickMesh2 *mesh : _clickMeshes) {
mesh->setVisible(true);
mesh->draw();
}
#endif
g_engine->getRenderer()->updateGlobalLight();
for (uint i = 0; i < _lights.size(); i++)
_lights[i]->update(i);
TeCamera::restore();
drawKate();
TeScene::draw();
}
void InGameScene::drawKate() {
if (_rippleMasks.size())
error("TODO: Implement InGameScene::drawKate");
}
void InGameScene::drawMask() {
if (_masks.empty())
return;
TeIntrusivePtr cam = currentCamera();
if (!cam)
return;
cam->apply();
TeRenderer *rend = g_engine->getRenderer();
if (!_maskAlpha)
rend->colorMask(false, false, false, false);
for (auto &mask : _masks)
mask->draw();
if (!_maskAlpha)
rend->colorMask(true, true, true, true);
}
void InGameScene::drawReflection() {
if (_rippleMasks.empty() || currentCameraIndex() >= (int)cameras().size())
return;
currentCamera()->apply();
if (!_maskAlpha)
g_engine->getRenderer()->colorMask(false, false, false, false);
for (uint i = _rippleMasks.size() - 1; i > 0; i--) {
_rippleMasks[i]->draw();
}
if (!_maskAlpha)
g_engine->getRenderer()->colorMask(true, true, true, true);
}
void InGameScene::drawPath() {
if (currentCameraIndex() >= (int)cameras().size())
return;
currentCamera()->apply();
g_engine->getRenderer()->disableZBuffer();
for (uint i = 0; i < _freeMoveZones.size(); i++)
_freeMoveZones[i]->draw();
g_engine->getRenderer()->enableZBuffer();
}
InGameScene::Dummy InGameScene::dummy(const Common::String &name) {
for (const Dummy &dummy : _dummies) {
if (dummy._name == name)
return dummy;
}
return Dummy();
}
bool InGameScene::findKate() {
for (auto &m : models()) {
if (m->name() == "Kate")
return true;
}
return false;
}
const InGameScene::TeMarker *InGameScene::findMarker(const Common::String &name) {
for (const TeMarker &marker : _markers) {
if (marker._name == name)
return ▮
}
return nullptr;
}
const InGameScene::TeMarker *InGameScene::findMarkerByInt(const Common::String &val) {
for (const TeMarker &marker : _markers) {
if (marker._val == val)
return ▮
}
return nullptr;
}
InGameScene::SoundStep InGameScene::findSoundStep(const Common::String &name) {
for (const auto &step : _soundSteps) {
if (step._key == name)
return step._value;
}
return SoundStep();
}
void InGameScene::freeGeometry() {
_loadedPath.set("");
_youkiManager.reset();
freeSceneObjects();
if (_character)
_character->setFreeMoveZone(nullptr);
for (Character *character : _characters)
character->setFreeMoveZone(nullptr);
for (TeFreeMoveZone *zone : _freeMoveZones)
delete zone;
_freeMoveZones.clear();
_bezierCurves.clear();
_dummies.clear();
cameras().clear();
models().clear();
_zoneModels.clear();
_masks.clear();
_shadowReceivingObjects.clear();
if (_charactersShadow)
_charactersShadow->destroy();
_sceneLights.clear();
if (_charactersShadow) {
delete _charactersShadow;
_charactersShadow = nullptr;
}
}
void InGameScene::freeSceneObjects() {
if (_character) {
_character->setCharLookingAt(nullptr);
_character->deleteAllCallback();
}
if (_characters.size() == 1) {
_characters[0]->deleteAllCallback();
}
SyberiaGame *game = dynamic_cast(g_engine->getGame());
assert(game);
game->unloadCharacters();
_characters.clear();
for (Object3D *obj : _object3Ds) {
obj->deleteLater();
}
_object3Ds.clear();
for (Billboard *bb : _billboards) {
bb->deleteLater();
}
_billboards.clear();
for (TeSpriteLayout *sprite : _sprites) {
sprite->deleteLater();
}
_sprites.clear();
// TODO: Clean up snows, waterCones, smokes, snowCones
_particles.clear();
TeParticle::deleteAll();
deleteAllCallback();
_markers.clear();
// TODO: Clean up randomAnims
for (RippleMask *rmask : _rippleMasks) {
delete rmask;
}
_rippleMasks.clear();
for (InGameScene::AnchorZone *zone : _anchorZones) {
delete zone;
}
_anchorZones.clear();
}
float InGameScene::getHeadHorizontalRotation(Character *cter, const TeVector3f32 &vec) {
TeVector3f32 pos = vec - cter->_model->position();
TeVector3f32 zvec = TeVector3f32(0, 0, 1.0f);
zvec.rotate(cter->_model->rotation());
float angle = atan2f(-pos.x(), pos.z()) - atan2f(-zvec.x(), zvec.z());
if (angle < -M_PI)
angle += (float)(M_PI * 2);
else if (angle > M_PI)
angle -= (float)(M_PI * 2);
return angle;
}
float InGameScene::getHeadVerticalRotation(Character *cter, const TeVector3f32 &vec) {
TeVector3f32 modelPos = cter->_model->position();
TeVector3f32 headWorldTrans = cter->_model->worldTransformationMatrix() * cter->lastHeadBoneTrans();
modelPos.y() = headWorldTrans.y();
TeVector3f32 offsetPos = vec - modelPos;
currentCamera()->apply();
float angle = atan2f(offsetPos.y(), TeVector2f32(offsetPos.x(), offsetPos.z()).length());
return angle;
}
Common::Path InGameScene::imagePathMarker(const Common::String &name) {
if (!isMarker(name))
return Common::Path();
Game *game = g_engine->getGame();
TeLayout *bg = game->forGui().layoutChecked("background");
for (Te3DObject2 *child : bg->childList()) {
TeSpriteLayout *spritelayout = dynamic_cast(child);
if (spritelayout && spritelayout->name() == name) {
return spritelayout->_tiledSurfacePtr->loadedPath();
}
}
return Common::Path();
}
void InGameScene::initScroll() {
_scrollOffset = TeVector2f32(0.5f, 0.0f);
}
bool InGameScene::isMarker(const Common::String &name) {
for (const TeMarker &marker : _markers) {
if (marker._name == name)
return true;
}
return false;
}
bool InGameScene::isObjectBlocking(const Common::String &name) {
for (const Common::String &b: _blockingObjects) {
if (name == b)
return true;
}
return false;
}
TeVector2f32 InGameScene::layerSize() {
TeLayout *bglayout = _bgGui.layout("background");
TeVector3f32 sz;
if (bglayout) {
TeLayout *rootlayout = Game::findSpriteLayoutByName(bglayout, "root");
if (!rootlayout)
error("InGameScene::layerSize: No root layout inside the background");
sz = rootlayout->size();
_scrollScale = TeVector2f32(sz.x(), sz.y());
} else {
sz = g_engine->getApplication()->getMainWindow().size();
}
return TeVector2f32(sz.x(), sz.y());
}
bool InGameScene::load(const Common::Path &scenePath) {
// Syberia 1 has loadActZones function contents inline.
loadActZones();
if (!_lights.empty()) {
g_engine->getRenderer()->disableAllLights();
for (uint i = 0; i < _lights.size(); i++) {
_lights[i]->disable(i);
}
_lights.clear();
}
_shadowLightNo = -1;
TeCore *core = g_engine->getCore();
const Common::Path lightsPath = core->findFile(getLightsFileName());
if (Common::File::exists(lightsPath))
loadLights(lightsPath);
if (!Common::File::exists(scenePath))
return false;
close();
_loadedPath = scenePath.getParent();
Common::File scenefile;
if (!scenefile.open(scenePath))
return false;
uint32 ncameras = scenefile.readUint32LE();
if (ncameras > 1024)
error("Improbable number of cameras %d", ncameras);
for (uint i = 0; i < ncameras; i++) {
TeIntrusivePtr cam = new TeCamera();
deserializeCam(scenefile, cam);
cameras().push_back(cam);
}
uint32 nobjects = scenefile.readUint32LE();
if (nobjects > 1024)
error("Improbable number of objects %d", nobjects);
for (uint i = 0; i < nobjects; i++) {
TeIntrusivePtr model = new TeModel();
const Common::String modelname = Te3DObject2::deserializeString(scenefile);
model->setName(modelname);
const Common::String objname = Te3DObject2::deserializeString(scenefile);
TePickMesh2 *pickmesh = new TePickMesh2();
deserializeModel(scenefile, model, pickmesh);
if (modelname.contains("Clic")) {
//debug("Loaded clickMesh %s", modelname.c_str());
_hitObjects.push_back(model);
model->setVisible(false);
model->setColor(TeColor(0, 0xff, 0, 0xff));
models().push_back(model);
pickmesh->setName(modelname);
_clickMeshes.push_back(pickmesh);
} else {
delete pickmesh;
if (modelname.substr(0, 2) != "ZB") {
if (objname.empty()) {
debug("[InGameScene::load] Unknown type of object named : %s", modelname.c_str());
} else {
InGameScene::Object obj;
obj._name = objname;
obj._model = model;
_objects.push_back(obj);
model->setVisible(false);
models().push_back(model);
}
}
}
}
uint32 nfreemovezones = scenefile.readUint32LE();
if (nfreemovezones > 1024)
error("Improbable number of free move zones %d", nfreemovezones);
for (uint i = 0; i < nfreemovezones; i++) {
TeFreeMoveZone *zone = new TeFreeMoveZone();
TeFreeMoveZone::deserialize(scenefile, *zone, &_blockers, &_rectBlockers, &_actZones);
_freeMoveZones.push_back(zone);
zone->setVisible(false);
}
uint32 ncurves = scenefile.readUint32LE();
if (ncurves > 1024)
error("Improbable number of curves %d", ncurves);
for (uint i = 0; i < ncurves; i++) {
TeIntrusivePtr curve = new TeBezierCurve();
TeBezierCurve::deserialize(scenefile, *curve);
curve->setVisible(true);
_bezierCurves.push_back(curve);
}
uint32 ndummies = scenefile.readUint32LE();
if (ndummies > 1024)
error("Improbable number of dummies %d", ndummies);
for (uint i = 0; i < ndummies; i++) {
InGameScene::Dummy dummy;
TeVector3f32 vec;
TeQuaternion rot;
dummy._name = Te3DObject2::deserializeString(scenefile);
TeVector3f32::deserialize(scenefile, vec);
dummy._position = vec;
TeQuaternion::deserialize(scenefile, rot);
dummy._rotation = rot;
TeVector3f32::deserialize(scenefile, vec);
dummy._scale = vec;
_dummies.push_back(dummy);
}
for (TeFreeMoveZone *zone : _freeMoveZones) {
convertPathToMesh(zone);
}
_charactersShadow = CharactersShadow::makeInstance();
_charactersShadow->create(this);
onMainWindowSizeChanged();
return true;
}
static Common::Path _sceneFileNameBase(const Common::String &zone, const Common::String &scene) {
Common::Path retval("scenes");
retval.joinInPlace(zone).joinInPlace(scene);
return retval;
}
static Common::Path _sceneFileNameBase() {
const Game *game = g_engine->getGame();
return _sceneFileNameBase(game->currentZone(), game->currentScene());
}
bool InGameScene::loadXml(const Common::String &zone, const Common::String &scene) {
_maskAlpha = false;
_zoneName = zone;
_sceneName = scene;
_blockers.clear();
_rectBlockers.clear();
TeFreeMoveZone::setCollisionSlide(false);
loadBlockers();
Common::Path xmlpath = _sceneFileNameBase(zone, scene).joinInPlace("Scene")
.appendInPlace(scene).appendInPlace(".xml");
Common::Path path = g_engine->getCore()->findFile(xmlpath);
InGameSceneXmlParser parser(this);
parser.setAllowText();
Common::String fixedbuf;
if (g_engine->gameType() == TetraedgeEngine::kSyberia2 && scene == "GangcarVideo") {
//
// WORKAROUND: scenes/A1_RomHaut/GangcarVideo/SceneGangcarVideo.xml
// in Syberia 2 has an embedded comment, which is invalid XML.
// Patch the contents of the file before loading.
//
Common::File xmlFile;
if (!xmlFile.open(path))
error("InGameScene::loadXml: Can't open %s", path.toString(Common::Path::kNativeSeparator).c_str());
const int64 bufsize = xmlFile.size();
char *buf = new char[bufsize+1];
buf[bufsize] = '\0';
xmlFile.read(buf, bufsize);
fixedbuf = Common::String(buf);
delete [] buf;
size_t offset = fixedbuf.find("");
if (offset != Common::String::npos)
fixedbuf.replace(offset + 29, 3, " "); // replace the > at the end
offset = fixedbuf.find("