scummvm/engines/tetraedge/game/syberia_game.cpp

1453 lines
47 KiB
C++

/* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "common/path.h"
#include "common/str-array.h"
#include "common/system.h"
#include "common/savefile.h"
#include "common/config-manager.h"
#include "tetraedge/game/syberia_game.h"
#include "tetraedge/tetraedge.h"
#include "tetraedge/game/application.h"
#include "tetraedge/game/character.h"
#include "tetraedge/game/in_game_scene.h"
#include "tetraedge/game/game_achievements.h"
#include "tetraedge/game/lua_binds.h"
#include "tetraedge/game/object3d.h"
#include "tetraedge/te/te_camera.h"
#include "tetraedge/te/te_core.h"
#include "tetraedge/te/te_input_mgr.h"
#include "tetraedge/te/te_ray_intersection.h"
#include "tetraedge/te/te_sound_manager.h"
#include "tetraedge/te/te_variant.h"
#include "tetraedge/te/te_lua_thread.h"
namespace Tetraedge {
SyberiaGame::SyberiaGame() : Game(),
_enteredFlag2(false), _movePlayerCharacterDisabled(false),
_noScaleLayout(nullptr), _isCharacterIdle(true),
_sceneCharacterVisibleFromLoad(false), _isCharacterWalking(false),
_lastCharMoveMousePos(0.0f, 0.0f), _randomSoundFinished(false),
_previousMousePos(-1, -1), _markersVisible(true), _saveRequested(false),
_gameLoadState(0), _score(0), _warped(false), _frameCounter(0),
_warpFadeFlag(false), _runModeEnabled(true) {
_randomSound = new RandomSound();
}
SyberiaGame::~SyberiaGame() {
if (_entered) {
leave(true);
}
delete _randomSound;
}
bool SyberiaGame::addAnimToSet(const Common::Path &anim) {
// Get path to lua script, eg scenes/ValVoralberg/14040/Set14040.lua
Common::Path animPath("scenes/");
animPath.joinInPlace(anim);
if (Common::File::exists(animPath)) {
const Common::StringArray parts = anim.splitComponents();
assert(parts.size() >= 2);
const Common::String layoutName = parts[1];
const Common::Path path = Common::Path("scenes/").appendComponent(parts[0]).appendComponent(parts[1]).appendComponent(Common::String::format("Set%s.lua", parts[1].c_str()));
_setAnimGui.load(path);
// Note: game makes this here, but never uses it..
// it seems like a random memory leak??
// TeSpriteLayout *spritelayout = new TeSpriteLayout();
TeSpriteLayout *spritelayout = findSpriteLayoutByName(_setAnimGui.layoutChecked("root"), layoutName);
_scene.bgGui().layoutChecked("root")->addChild(spritelayout);
return true;
}
return false;
}
/*static*/
Common::String SyberiaGame::artworkConfName(const Common::String &name) {
Common::String configName = Common::String::format("artwork_%s", name.c_str());
for (uint i = 0; i < configName.size(); i++) {
if (configName[i] == '/' || configName[i] == '.')
configName.setChar('_', i);
}
return configName;
}
void SyberiaGame::addArtworkUnlocked(const Common::String &name, bool notify) {
const Common::String configName = artworkConfName(name);
if (_unlockedArtwork.contains(configName))
return;
ConfMan.setBool(configName, true);
ConfMan.flushToDisk();
_unlockedArtwork[configName] = true;
if (notify)
_notifier.push("BONUS!", "Inventory/Objects/VPapierCrayon.png");
}
bool SyberiaGame::isArtworkUnlocked(const Common::String &name) const {
const Common::String configName = artworkConfName(name);
return _unlockedArtwork.getValOrDefault(configName, false);
}
void SyberiaGame::addNoScale2Children() {
if (!_noScaleLayout2)
return;
TeLayout *vidbtn = _inGameGui.layout("videoButtonLayout");
if (vidbtn)
_noScaleLayout2->addChild(vidbtn);
TeLayout *bg = _inventory.cellphone()->gui().layout("background");
if (bg)
_noScaleLayout2->addChild(bg);
TeButtonLayout *bgbtn = _objectif.gui1().buttonLayout("background");
if (bgbtn)
_noScaleLayout2->addChild(bgbtn);
}
void SyberiaGame::addNoScaleChildren() {
if (!_noScaleLayout)
return;
TeLayout *inGame = _inGameGui.layout("inGame");
if (inGame)
_noScaleLayout->addChild(inGame);
_noScaleLayout->addChild(&_question2);
Application *app = g_engine->getApplication();
app->frontLayout().addChild(&_dialog2);
_noScaleLayout->addChild(&_inventory);
_noScaleLayout->addChild(&_inventoryMenu);
_noScaleLayout->addChild(&_documentsBrowser);
_noScaleLayout->addChild(&_documentsBrowser.zoomedLayout());
}
void SyberiaGame::addRandomSound(const Common::String &name, const Common::Path &path, float f1, float volume) {
if (!_randomSounds.contains(name)) {
_randomSounds[name] = Common::Array<RandomSound*>();
}
RandomSound *randsound = new RandomSound();
randsound->_path = path;
randsound->_f1 = f1;
randsound->_volume = volume;
randsound->_name = name;
_randomSounds[name].push_back(randsound);
}
void SyberiaGame::addToBag(const Common::String &objid) {
if (_inventory.objectCount(objid) != 0)
return;
_inventory.addObject(objid);
Common::Path imgpath = Common::Path("Inventory/Objects/").appendComponent(objid + ".png");
_notifier.push(_inventory.objectName(objid), imgpath);
for (int i = 0; i < NUM_OBJECTS_TAKEN_IDS; i++) {
if (objid == OBJECTS_TAKEN_IDS[i] && !_objectsTakenBits[i]) {
_objectsTakenBits[i] = true;
_objectsTakenVal++;
}
}
_score += 10;
debug("Updated score: %d", _score);
}
void SyberiaGame::addToHand(const Common::String &objname) {
_inventory.addObject(objname);
_inventory.selectedObject(objname);
}
void SyberiaGame::addToScore(int score) {
_score += score;
}
bool SyberiaGame::changeWarp(const Common::String &zone, const Common::String &scene, bool fadeFlag) {
//debug("Game::changeWarp(%s, %s, %s)", zone.c_str(), scene.c_str(), fadeFlag ? "true" : "false");
Application *app = g_engine->getApplication();
if (fadeFlag && g_engine->gameType() == TetraedgeEngine::kSyberia) {
app->blackFade();
} else {
app->captureFade();
}
// Slight divergence from original.. free after capturing fade so characters don't disappear.
if (g_engine->gameType() == TetraedgeEngine::kSyberia2)
_scene.freeGeometry();
_warpZone = zone;
_warpScene = scene;
_warpFadeFlag = fadeFlag;
_warped = true;
return true;
}
bool SyberiaGame::changeWarp2(const Common::String &zone, const Common::String &scene, bool fadeFlag) {
//debug("Game::changeWarp2(%s, %s, %s)", zone.c_str(), scene.c_str(), fadeFlag ? "true" : "false");
_warped = false;
_movePlayerCharacterDisabled = false;
_sceneCharacterVisibleFromLoad = false;
// TODO? _charMoveMouseEventNo = -1?
_isCharacterIdle = true;
_isCharacterWalking = false;
Common::Path luapath("scenes");
luapath.joinInPlace(zone);
luapath.joinInPlace(scene);
luapath.joinInPlace("Logic");
luapath.appendInPlace(scene);
luapath.appendInPlace(".lua");
if (Common::File::exists(g_engine->getCore()->findFile(luapath))) {
_luaScript.execute("OnLeave");
_luaContext.removeGlobal("On");
_luaContext.removeGlobal("OnEnter");
_luaContext.removeGlobal("OnWarpObjectHit");
_luaContext.removeGlobal("OnButtonDown");
_luaContext.removeGlobal("OnButtonUp");
_luaContext.removeGlobal("OnFinishedAnim");
_luaContext.removeGlobal("OnCharacterAnimationFinished");
_luaContext.removeGlobal("OnCharacterAnimationPlayerFinished");
_luaContext.removeGlobal("OnDisplacementFinished");
_luaContext.removeGlobal("OnFreeSoundFinished");
_luaContext.removeGlobal("OnDocumentClosed");
_luaContext.removeGlobal("OnSelectedObject");
_luaContext.removeGlobal("OnDialogFinished");
_luaContext.removeGlobal("OnAnswered");
_luaContext.removeGlobal("OnLeave");
_luaScript.unload();
}
_forGui.unload();
_prevSceneName = _currentScene;
if (!fadeFlag)
g_engine->getApplication()->fade();
return initWarp(zone, scene, false);
}
void SyberiaGame::deleteNoScale() {
if (_noScaleLayout) {
removeNoScaleChildren();
delete _noScaleLayout;
_noScaleLayout = nullptr;
}
if (_noScaleLayout2) {
removeNoScale2Children();
delete _noScaleLayout2;
_noScaleLayout2 = nullptr;
}
}
void SyberiaGame::draw() {
if (_running) {
_frameCounter++;
_scene.draw();
}
}
void SyberiaGame::enter() {
_enteredFlag2 = true;
_entered = true;
_luaShowOwnerError = false;
_score = 0;
Application *app = g_engine->getApplication();
app->visualFade().init();
// TODO: Original puts this click handler at -10000.. but then it never gets hit?
Common::SharedPtr<TeCallback1Param<SyberiaGame, const Common::Point &>> callbackptr(new TeCallback1Param<SyberiaGame, const Common::Point &>(this, &SyberiaGame::onMouseClick, 10000.0f));
g_engine->getInputMgr()->_mouseLUpSignal.push_back(callbackptr);
_movePlayerCharacterDisabled = false;
// TODO? Set _charMoveMouseEventNo = -1
_isCharacterIdle = true;
_sceneCharacterVisibleFromLoad = false;
Character::loadSettings("models/ModelsSettings.xml");
Object3D::loadSettings("objects/ObjectsSettings.xml");
if (_scene._character) {
_scene._character->onFinished().remove(this, &SyberiaGame::onDisplacementPlayerFinished);
_scene.unloadCharacter(_scene._character->_model->name());
}
bool loaded = loadPlayerCharacter("Kate");
if (!loaded)
error("[Game::enter] Can't load player character");
_scene._character->_model->setVisible(true);
_running = true;
_luaContext.create();
GameAchievements::registerAchievements(_luaContext);
_luaContext.setGlobal("BUTTON_VALID", 1);
_luaContext.setGlobal("BUTTON_CANCEL", 2);
_luaContext.setGlobal("BUTTON_EXTRA1", 4);
_luaContext.setGlobal("BUTTON_EXTRA2", 8);
_luaContext.setGlobal("BUTTON_L1", 0x10);
_luaContext.setGlobal("BUTTON_R1", 0x20);
_luaContext.setGlobal("BUTTON_START", 0x40);
_luaContext.setGlobal("BUTTON_UP", 0x80);
_luaContext.setGlobal("BUTTON_DOWN", 0x100);
_luaContext.setGlobal("BUTTON_LEFT", 0x200);
_luaContext.setGlobal("BUTTON_RIGHT", 0x400);
_luaContext.setGlobal("BUTTON_LS_CLIC", 0x800);
_luaContext.setGlobal("BUTTON_RS_CLIC", 0x1000);
_luaContext.setGlobal("BUTTON_BACK", 0x2000);
_luaContext.setGlobal("BUTTON_SELECT", 0x4000);
_luaContext.setGlobal("BUTTON_L2", 0x8000);
_luaContext.setGlobal("BUTTON_R2", 0x10000);
_luaContext.setGlobal("BUTTON_LS_UP", 0x20000);
_luaContext.setGlobal("BUTTON_LS_DOWN", 0x40000);
_luaContext.setGlobal("BUTTON_LS_LEFT", 0x80000);
_luaContext.setGlobal("BUTTON_LS_RIGHT", 0x100000);
_luaContext.setGlobal("BUTTON_RS_UP", 0x200000);
_luaContext.setGlobal("BUTTON_RS_DOWN", 0x400000);
_luaContext.setGlobal("BUTTON_RS_LEFT", 0x800000);
_luaContext.setGlobal("BUTTON_RS_RIGHT", 0x1000000);
_gameEnterScript.attachToContext(&_luaContext);
if (!_objectif.gui1().loaded()) {
_objectif.load();
}
_question2.load();
_dialog2.load();
_documentsBrowser.load();
_documentsBrowser.loadZoomed();
_inventory.load();
_inventory.cellphone()->onCallNumber().add(this, &SyberiaGame::onCallNumber);
if (hasLoadName()) {
loadBackup(_loadName);
} else {
_gameLoadState = 1;
onFinishedLoadingBackup("");
}
_sceneCharacterVisibleFromLoad = true;
_scene._character->onFinished().remove(this, &SyberiaGame::onDisplacementPlayerFinished);
_scene._character->onFinished().add(this, &SyberiaGame::onDisplacementPlayerFinished);
_prevSceneName.clear();
_notifier.load();
}
void SyberiaGame::finishFreemium() {
Application *app = g_engine->getApplication();
app->setFinishedGame(true);
app->setFinishedFremium(true);
}
void SyberiaGame::finishGame() {
Application *app = g_engine->getApplication();
// FIXME: The original sets this, but then Application::run immediately
// returns to the menu.. how does the original wait for the credits to end??
//app->_finishedGame = true;
_playedTimer.stop();
/* Game does this but does nothing with result?
if (app->difficulty() == 2) {
_playedTimer.getTimeFromStart();
} */
app->credits().enter(false);
}
void SyberiaGame::initLoadedBackupData() {
bool warpFlag = true;
Application *app = g_engine->getApplication();
Common::String firstWarpPath;
if (!_loadName.empty()) {
warpFlag = false;
Common::InSaveFile *saveFile = g_engine->getSaveFileManager()->openForLoading(_loadName);
Common::Error result = g_engine->loadGameStream(saveFile);
if (result.getCode() == Common::kNoError) {
ExtendedSavegameHeader header;
if (MetaEngine::readSavegameHeader(saveFile, &header))
g_engine->setTotalPlayTime(header.playtime);
}
} else {
firstWarpPath = app->firstWarpPath();
_currentScene = app->firstScene();
_currentZone = app->firstZone();
_playedTimer.start();
_objectsTakenVal = 0;
for (int i = 0; i < ARRAYSIZE(_objectsTakenBits); i++) {
_objectsTakenBits[i] = 0;
}
_dialogsTold = 0;
if (_loadName == "NO_OWNER")
_luaShowOwnerError = true;
}
_gameLoadState = 0;
app->showLoadingIcon(false);
_loadName.clear();
initScene(warpFlag, firstWarpPath);
}
void SyberiaGame::initNoScale() {
if (!_noScaleLayout) {
_noScaleLayout = new TeLayout();
_noScaleLayout->setName("noScaleLayout");
_noScaleLayout->setSizeType(TeILayout::RELATIVE_TO_PARENT);
_noScaleLayout->setSize(TeVector3f32(1.0f, 1.0f, 0.0f));
}
if (!_noScaleLayout2) {
_noScaleLayout2 = new TeLayout();
_noScaleLayout2->setName("noScaleLayout2");
_noScaleLayout2->setSizeType(TeILayout::RELATIVE_TO_PARENT);
_noScaleLayout2->setSize(TeVector3f32(1.0f, 1.0f, 0.0f));
}
}
void SyberiaGame::initScene(bool fade, const Common::String &scenePath) {
_luaContext.setGlobal("SHOW_OWNER_ERROR", _luaShowOwnerError);
initWarp(_currentZone, _currentScene, fade);
loadScene(scenePath);
if (_scene._character->_model.get() && !_scene.findKate()) {
_scene.models().push_back(_scene._character->_model);
}
_scene._character->_model->setVisible(true);
}
bool SyberiaGame::initWarp(const Common::String &zone, const Common::String &scene, bool fadeFlag) {
debug("Game::initWarp(%s, %s, %s)", zone.c_str(), scene.c_str(), fadeFlag ? "true" : "false");
_inventoryMenu.unload();
_inGameGui.unload();
_movePlayerCharacterDisabled = false;
_sceneCharacterVisibleFromLoad = true;
if (_scene._character) {
_scene._character->_model->setVisible(true);
_scene._character->deleteAllCallback();
_scene._character->stop();
_scene._character->setAnimation(_scene._character->characterSettings()._idleAnimFileName, true);
if (!_scene.findKate()) {
_scene.models().push_back(_scene._character->_model);
if (_scene._character->_shadowModel[0]) {
_scene.models().push_back(_scene._character->_shadowModel[0]);
_scene.models().push_back(_scene._character->_shadowModel[1]);
}
}
}
_currentZone = zone;
_currentScene = scene;
_scene.loadBlockers();
Common::Path scenePath("scenes");
scenePath.joinInPlace(zone);
scenePath.joinInPlace(scene);
_sceneZonePath = scenePath;
TeCore *core = g_engine->getCore();
const Common::Path intLuaPath = core->findFile(scenePath.join(Common::String::format("Int%s.lua", scene.c_str())));
const Common::Path logicLuaPath = core->findFile(scenePath.join(Common::String::format("Logic%s.lua", scene.c_str())));
const Common::Path setLuaPath = core->findFile(scenePath.join(Common::String::format("Set%s.lua", scene.c_str())));
const Common::Path forLuaPath = core->findFile(scenePath.join(Common::String::format("For%s.lua", scene.c_str())));
const Common::Path markerLuaPath = core->findFile(scenePath.join(Common::String::format("Marker%s.lua", scene.c_str())));
bool intLuaExists = Common::File::exists(intLuaPath);
bool logicLuaExists = Common::File::exists(logicLuaPath);
bool setLuaExists = Common::File::exists(setLuaPath);
bool forLuaExists = Common::File::exists(forLuaPath);
bool markerLuaExists = Common::File::exists(markerLuaPath);
if (!intLuaExists && !logicLuaExists && !setLuaExists && !forLuaExists && !markerLuaExists) {
debug("No lua scripts for scene %s zone %s", scene.c_str(), zone.c_str());
return false;
}
for (auto &sound : _gameSounds) {
sound->setRetain(false);
}
if (logicLuaExists) {
_luaContext.addBindings(LuaBinds::LuaOpenBinds);
_luaScript.attachToContext(&_luaContext);
_luaScript.load(core->findFile("menus/help/help.lua"));
_luaScript.execute();
_luaScript.load(logicLuaPath);
}
if (_forGui.loaded())
_forGui.unload();
_scene.reset();
_scene.bgGui().unload();
_scene.markerGui().unload();
_scene.hitObjectGui().unload();
Common::Path geomPath(Common::String::format("scenes/%s/Geometry%s.bin",
zone.c_str(), zone.c_str()));
geomPath = core->findFile(geomPath);
if (Common::File::exists(geomPath)) {
// Syberia 1, load geom bin
_scene.load(geomPath);
} else {
// Syberia 2, load from xml
_scene.loadXml(zone, scene);
}
_scene.loadBackground(setLuaPath);
Application *app = g_engine->getApplication();
if (forLuaExists) {
_forGui.load(forLuaPath);
TeLayout *bg = _forGui.layoutChecked("background");
bg->setRatioMode(TeILayout::RATIO_MODE_NONE);
app->frontLayout().addChild(bg);
// Note: Game also adds cellphone to both frontLayout *and* noScaleLayout2,
// so we reproduce the broken behavior exactly.
TeLayout *cellbg = _inventory.cellphone()->gui().buttonLayoutChecked("background");
app->frontLayout().removeChild(cellbg);
app->frontLayout().addChild(cellbg);
_objectif.reattachLayout(&app->frontLayout());
}
if (intLuaExists) {
_scene.loadInteractions(intLuaPath);
TeLuaGUI::StringMap<TeButtonLayout *> &blayouts = _scene.hitObjectGui().buttonLayouts();
for (auto &entry : blayouts) {
HitObject *hobj = new HitObject();
TeButtonLayout *btn = entry._value;
hobj->_game = this;
hobj->_button = btn;
hobj->_name = btn->name();
btn->onMouseClickValidated().add(hobj, &HitObject::onValidated);
btn->onButtonChangedToStateDownSignal().add(hobj, &HitObject::onDown);
btn->onButtonChangedToStateUpSignal().add(hobj, &HitObject::onUp);
_gameHitObjects.push_back(hobj);
}
}
_inventoryMenu.load();
_inGameGui.load("InGame.lua");
TeButtonLayout *skipbtn = _inGameGui.buttonLayoutChecked("skipVideoButton");
skipbtn->setVisible(false);
skipbtn->onMouseClickValidated().remove(this, &Game::onSkipVideoButtonValidated);
skipbtn->onMouseClickValidated().add(this, &Game::onSkipVideoButtonValidated);
TeButtonLayout *vidbgbtn = _inGameGui.buttonLayoutChecked("videoBackgroundButton");
vidbgbtn->setVisible(false);
vidbgbtn->onMouseClickValidated().remove(this, &SyberiaGame::onLockVideoButtonValidated);
vidbgbtn->onMouseClickValidated().add(this, &SyberiaGame::onLockVideoButtonValidated);
TeSpriteLayout *video = _inGameGui.spriteLayoutChecked("video");
video->setVisible(false);
video->_tiledSurfacePtr->_frameAnim.onStop().remove(this, &Game::onVideoFinished);
video->_tiledSurfacePtr->_frameAnim.onStop().add(this, &Game::onVideoFinished);
TeButtonLayout *invbtn = _inGameGui.buttonLayoutChecked("inventoryButton");
invbtn->onMouseClickValidated().remove(this, &Game::onInventoryButtonValidated);
invbtn->onMouseClickValidated().add(this, &Game::onInventoryButtonValidated);
invbtn->setSizeType(TeILayout::RELATIVE_TO_PARENT);
const TeVector3f32 winSize = app->getMainWindow().size();
if (g_engine->getCore()->fileFlagSystemFlag("definition") == "SD") {
invbtn->setSize(TeVector3f32(0.12f, (4.0f / ((winSize.y() / winSize.x()) * 4.0f)) * 0.12f, 0.0));
} else {
invbtn->setSize(TeVector3f32(0.08f, (4.0f / ((winSize.y() / winSize.x()) * 4.0f)) * 0.08f, 0.0));
}
TeCheckboxLayout *markersCheckbox = _inGameGui.checkboxLayout("markersVisibleButton");
markersCheckbox->setState(_markersVisible ? TeCheckboxLayout::CheckboxStateActive : TeCheckboxLayout::CheckboxStateUnactive);
markersCheckbox->onStateChangedSignal().add(this, &SyberiaGame::onMarkersVisible);
initNoScale();
removeNoScale2Children();
app->frontLayout().removeChild(_noScaleLayout2);
TeLayout *vidLayout = _inGameGui.layout("videoLayout");
app->frontLayout().removeChild(vidLayout);
removeNoScaleChildren();
app->frontLayout().removeChild(_noScaleLayout);
app->frontLayout().addChild(_noScaleLayout);
addNoScaleChildren();
app->frontLayout().addChild(vidLayout);
app->frontLayout().addChild(_noScaleLayout2);
addNoScale2Children();
if (!fadeFlag) {
if (_inventory.selectedObject().size()) {
_inventory.selectedObject(_inventory.selectedInventoryObject());
}
_inventory.setVisible(false);
_objectif.setVisibleObjectif(false);
_objectif.setVisibleButtonHelp(true);
_running = true;
loadScene("save.xml");
}
app->backLayout().addChild(_scene.background());
if (markerLuaExists) {
TeLayout *bg = _scene.markerGui().layout("background");
app->frontLayout().addChild(bg);
}
Common::String camname = Common::String("Camera") + scene;
_scene.setCurrentCamera(camname);
// Special hacks for certain scenes (don't blame me, original does this..)
if (g_engine->gameType() == TetraedgeEngine::kSyberia) {
if (scene == "14050") {
TeIntrusivePtr<TeCamera> curcamera = _scene.currentCamera();
const TeVector3f32 coords(1200.6f, -1937.5f, 1544.1f);
curcamera->setPosition(coords);
} else if (scene == "34610") {
TeIntrusivePtr<TeCamera> curcamera = _scene.currentCamera();
const TeVector3f32 coords(-328.243f, 340.303f, -1342.84f);
curcamera->setPosition(coords);
const TeQuaternion rot(0.003194f, 0.910923f, -0.009496f, -0.412389f);
curcamera->setRotation(rot);
}
//
// WORKAROUND: Fix the camera in the restored scenes
//
if (zone == "ValStreet" && scene == "11100") {
TeIntrusivePtr<TeCamera> cam = _scene.currentCamera();
cam->setProjMatrixType(3);
cam->setFov(0.5f);
} else if (zone == "ValField" && scene == "11170") {
TeIntrusivePtr<TeCamera> cam = _scene.currentCamera();
cam->setProjMatrixType(3);
// TODO: Is camera position right? Kate not visible..
// default values:
// -494.447998 -79.2976989 1408.5
// scene entrance and exit
// -184, -143, 1563
// -42, -147, 1534
} else if (zone == "BarRiverSide" && scene == "24020") {
TeIntrusivePtr<TeCamera> cam = _scene.currentCamera();
cam->setProjMatrixType(3);
cam->setFov(0.5f);
}
}
if (logicLuaExists) {
_exitZone.clear();
_luaScript.execute();
_luaScript.execute("OnEnter", _prevSceneName);
_luaScript.execute("OnSelectedObject", _inventory.selectedObject());
}
for (uint i = 0; i < _gameSounds.size(); i++) {
if (_gameSounds[i]->retain())
continue;
_gameSounds[i]->stop();
_gameSounds[i]->deleteLater();
_gameSounds.remove_at(i);
i--;
}
// Take a copy and clear the global list first, as deleting a sound can cause the
// next sound to trigger (which might have been deleted already).
Common::HashMap<Common::String, Common::Array<RandomSound *>> rsounds = _randomSounds;
_randomSounds.clear();
for (auto &randsoundlist : rsounds) {
for (auto *randsound : randsoundlist._value) {
delete randsound;
}
randsoundlist._value.clear();
}
_scene.initScroll();
return true;
}
void SyberiaGame::leave(bool flag) {
if (!_enteredFlag2)
return;
Application *app = g_engine->getApplication();
deleteNoScale();
_entered = false;
_running = false;
_notifier.unload();
g_engine->getInputMgr()->_mouseLUpSignal.remove(this, &SyberiaGame::onMouseClick);
_question2.unload();
TeLayout *cellbg = _inventory.cellphone()->gui().buttonLayout("background");
if (cellbg)
app->frontLayout().removeChild(cellbg);
_inventory.cellphone()->leave();
_dialog2.unload();
_inventory.unload();
_documentsBrowser.unload();
_inventoryMenu.unload();
_objectif.unload(); // not done in original, but should be.
_scene.close();
_forGui.unload();
if (_scene._character) {
_scene._character->deleteAllCallback();
_scene._character->stop();
_scene.unloadCharacter(_scene._character->_model->name());
}
// Deleting these sounds can cause events which can modify the list.
Common::Array<GameSound *> sounds = _gameSounds;
_gameSounds.clear();
for (auto &sound : sounds) {
delete sound;
}
for (auto &randsoundlist : _randomSounds) {
for (auto *randsound : randsoundlist._value) {
delete randsound;
}
randsoundlist._value.clear();
}
_randomSounds.clear();
for (auto *hitobj : _gameHitObjects) {
delete hitobj;
}
_gameHitObjects.clear();
// TODO: clear SyberiaGame::HitObject tree here?
_luaContext.destroy();
_running = false;
_inGameGui.buttonLayoutChecked("skipVideoButton")->onMouseClickValidated().remove(this, &Game::onSkipVideoButtonValidated);
_inGameGui.buttonLayoutChecked("videoBackgroundButton")->onMouseClickValidated().remove(this, &Game::onLockVideoButtonValidated);
_inGameGui.spriteLayoutChecked("video")->_tiledSurfacePtr->_frameAnim.onFinished().remove(this, &Game::onSkipVideoButtonValidated);
_inGameGui.buttonLayoutChecked("inventoryButton")->onMouseClickValidated().remove(this, &Game::onInventoryButtonValidated);
_inGameGui.unload();
_playedTimer.stop();
_enteredFlag2 = false;
app->lockCursor(false);
app->lockCursorFromAction(false);
// TODO: Set some inputmgr flag here?
Character::animCacheFreeAll();
}
void SyberiaGame::loadBackup(const Common::String &path) {
if (_gameLoadState == 0) {
_gameLoadState = 1;
g_engine->getApplication()->showLoadingIcon(true);
onFinishedLoadingBackup(path);
}
}
void SyberiaGame::loadUnlockedArtwork() {
Common::ConfigManager::Domain *domain = ConfMan.getActiveDomain();
for (auto &val : *domain) {
if (val._key.substr(0, 8) == "artwork_") {
_unlockedArtwork[val._key] = true;
}
}
}
bool SyberiaGame::loadCharacter(const Common::String &name) {
bool result = true;
Character *character = _scene.character(name);
if (!character) {
result = _scene.loadCharacter(name);
if (result) {
character = _scene.character(name);
assert(character);
character->_onCharacterAnimFinishedSignal.remove(this, &SyberiaGame::onCharacterAnimationFinished);
character->_onCharacterAnimFinishedSignal.add(this, &SyberiaGame::onCharacterAnimationFinished);
// Syberia 2 uses a simplified callback here.
// We have made onDisplacementPlayerFinished more like Syberia 1's onDisplacementFinished.
if (g_engine->gameType() == TetraedgeEngine::kSyberia)
character->onFinished().add(this, &SyberiaGame::onDisplacementPlayerFinished);
else
character->onFinished().add(this, &SyberiaGame::onDisplacementFinished);
}
}
return result;
}
bool SyberiaGame::loadPlayerCharacter(const Common::String &name) {
bool result = _scene.loadPlayerCharacter(name);
if (result) {
_scene._character->_characterAnimPlayerFinishedSignal.remove(this, &SyberiaGame::onCharacterAnimationPlayerFinished);
_scene._character->_characterAnimPlayerFinishedSignal.add(this, &SyberiaGame::onCharacterAnimationPlayerFinished);
_scene._character->onFinished().remove(this, &SyberiaGame::onDisplacementPlayerFinished);
_scene._character->onFinished().add(this, &SyberiaGame::onDisplacementPlayerFinished);
} else {
debug("failed to load player character %s", name.c_str());
}
return result;
}
bool SyberiaGame::loadScene(const Common::String &name) {
TeCore *core = g_engine->getCore();
_gameEnterScript.load(core->findFile("scenes/OnGameEnter.lua"));
_gameEnterScript.execute();
Character *character = _scene._character;
if (character && character->_model->visible()) {
_sceneCharacterVisibleFromLoad = true;
}
return false;
}
bool SyberiaGame::onCallNumber(Common::String val) {
_luaScript.execute("OnCallNumber", val);
return false;
}
bool SyberiaGame::onCharacterAnimationFinished(const Common::String &charName) {
if (!_scene._character)
return false;
if (g_engine->gameType() == TetraedgeEngine::kSyberia2) {
Character *character = scene().character(charName);
if (character) {
const Common::String curAnimName = character->curAnimName();
if (curAnimName == character->walkAnim(Character::WalkPart_EndD)
|| curAnimName == character->walkAnim(Character::WalkPart_EndG)) {
character->updatePosition(1.0);
character->endMove();
}
}
}
for (uint i = 0; i < _yieldedCallbacks.size(); i++) {
YieldedCallback &cb = _yieldedCallbacks[i];
if (cb._luaFnName == "OnCharacterAnimationFinished" && cb._luaParam == charName) {
TeLuaThread *lua = cb._luaThread;
_yieldedCallbacks.remove_at(i);
if (lua) {
lua->resume();
return false;
}
break;
}
}
_luaScript.execute("OnCharacterAnimationFinished", charName);
return false;
}
bool SyberiaGame::onCharacterAnimationPlayerFinished(const Common::String &anim) {
if (_gameLoadState != 0)
return false;
bool callScripts = true;
for (uint i = 0; i < _yieldedCallbacks.size(); i++) {
YieldedCallback &cb = _yieldedCallbacks[i];
// Yes, even Syberia2 checks for Kate here..
if (cb._luaFnName == "OnCharacterAnimationFinished" && cb._luaParam == "Kate") {
TeLuaThread *lua = cb._luaThread;
_yieldedCallbacks.remove_at(i);
if (lua) {
lua->resume();
callScripts = false;
}
break;
}
}
if (callScripts) {
if (g_engine->gameType() == TetraedgeEngine::kSyberia)
_luaScript.execute("OnCharacterAnimationFinished", "Kate");
else
_luaScript.execute("OnCharacterAnimationPlayerFinished", anim);
_luaScript.execute("OnCellCharacterAnimationPlayerFinished", anim);
}
Character *character = scene()._character;
assert(character);
// Note: the above callbacks can change the anim,
// so we have to fetch this *after* them.
const Common::String curAnimName = character->curAnimName();
if (_currentScene == _someSceneName) {
if (curAnimName == character->walkAnim(Character::WalkPart_Start)
|| curAnimName == character->walkAnim(Character::WalkPart_Loop)
|| curAnimName == character->walkAnim(Character::WalkPart_EndD)
|| curAnimName == character->walkAnim(Character::WalkPart_EndG))
character->stop();
} else {
if (!_sceneCharacterVisibleFromLoad && curAnimName == character->walkAnim(Character::WalkPart_Start)) {
character->setAnimation(character->walkAnim(Character::WalkPart_Loop), true);
return false;
}
if (curAnimName == character->walkAnim(Character::WalkPart_EndD)
|| curAnimName == character->walkAnim(Character::WalkPart_EndG)) {
character->updatePosition(1.0);
character->endMove();
// endMove can result in callbacks that change the animation. check again.
if (character->curAnimName() == character->walkAnim(Character::WalkPart_EndD)
|| character->curAnimName() == character->walkAnim(Character::WalkPart_EndG))
character->setAnimation(character->characterSettings()._idleAnimFileName, true);
}
}
return false;
}
bool SyberiaGame::onDialogFinished(const Common::String &val) {
for (uint i = 0; i < _yieldedCallbacks.size(); i++) {
YieldedCallback &cb = _yieldedCallbacks[i];
if (cb._luaFnName == "OnDialogFinished" && cb._luaParam == val) {
TeLuaThread *lua = cb._luaThread;
_yieldedCallbacks.remove_at(i);
if (lua) {
lua->resume();
return false;
}
break;
}
}
_luaScript.execute("OnDialogFinished", val);
_luaScript.execute("OnCellDialogFinished", val);
return false;
}
// This is the Syberia 2 version of this function, not used in Syb 1.
// Syb 1 uses a function much more like onDisplacementPlayerFinished below.
bool SyberiaGame::onDisplacementFinished() {
TeLuaThread *thread = nullptr;
for (uint i = 0; i < _yieldedCallbacks.size(); i++) {
YieldedCallback &cb = _yieldedCallbacks[i];
if (cb._luaFnName == "OnDisplacementFinished") {
thread = cb._luaThread;
_yieldedCallbacks.remove_at(i);
break;
}
}
if (thread) {
thread->resume();
} else {
_luaScript.execute("OnDisplacementFinished");
}
return false;
}
bool SyberiaGame::onDisplacementPlayerFinished() {
_sceneCharacterVisibleFromLoad = true;
assert(_scene._character);
_scene._character->stop();
_scene._character->walkMode("Walk");
_scene._character->setAnimation(_scene._character->characterSettings()._idleAnimFileName, true);
if (_isCharacterWalking) {
_isCharacterWalking = false;
_isCharacterIdle = true;
} else {
_isCharacterIdle = false;
}
TeLuaThread *thread = nullptr;
const char *cbName = (g_engine->gameType() == TetraedgeEngine::kSyberia ?
"OnDisplacementFinished" : "OnDisplacementPlayerFinished");
for (uint i = 0; i < _yieldedCallbacks.size(); i++) {
YieldedCallback &cb = _yieldedCallbacks[i];
if (cb._luaFnName == cbName) {
thread = cb._luaThread;
_yieldedCallbacks.remove_at(i);
break;
}
}
if (thread) {
thread->resume();
} else {
_luaScript.execute(cbName);
}
return false;
}
bool SyberiaGame::onFinishedCheckBackup(bool result) {
if (_gameLoadState == 1) {
_gameLoadState = 0;
return true;
}
return false;
}
bool SyberiaGame::onFinishedLoadingBackup(const Common::String &val) {
if (_gameLoadState == 1) {
_loadName = val;
_gameLoadState = 2;
return true;
}
return false;
}
bool SyberiaGame::onFinishedSavingBackup(int something) {
if (something) {
g_engine->getGame()->_returnToMainMenu = true;
}
g_engine->getApplication()->showLoadingIcon(false);
return true;
}
bool SyberiaGame::onMarkersVisible(TeCheckboxLayout::State state) {
_markersVisible = (state == TeCheckboxLayout::CheckboxStateActive);
showMarkers(state == TeCheckboxLayout::CheckboxStateActive);
return false;
}
bool SyberiaGame::onMouseClick(const Common::Point &pt) {
Application *app = g_engine->getApplication();
if (app->isFading())
return true;
// In case we capture a click during a video or dialog (shouldn't happen?)
if (!_scene.currentCamera() || _dialog2.isDialogPlaying() || _question2.isEntered())
return false;
_posPlayer = TeVector3f32(-1.0f, -1.0f, -1.0f);
if (_previousMousePos == TeVector2s32(-1, -1)) {
_previousMousePos = pt;
} else {
const TeVector3f32 winSize = app->getMainWindow().size();
const TeVector2s32 prevMousePos = _previousMousePos;
_previousMousePos = pt;
float xdist = (pt.x - prevMousePos._x) / winSize.x();
float ydist = (pt.y - prevMousePos._y) / winSize.y();
float sqrdist = xdist * xdist + ydist * ydist;
if (sqrdist < 0.0001 && (!_walkTimer.running() || _walkTimer.timeElapsed() > 300000.0
|| (_scene._character && _scene._character->walkModeStr() != "Walk"))) {
return false;
// Normal walk click
}
}
if (!app->frontLayout().isMouseIn(pt))
return false;
Common::String nearestMeshName = "None";
TeIntrusivePtr<TeCamera> curCamera = _scene.currentCamera();
Common::Array<TePickMesh2*> pickMeshes = _scene.clickMeshes();
TePickMesh2 *nearestMesh = TeFreeMoveZone::findNearestMesh(curCamera, pt, pickMeshes, nullptr, false);
if (nearestMesh) {
nearestMeshName = nearestMesh->name();
debug("Game::onMouseClick: Click near mesh %s", nearestMeshName.c_str());
_lastCharMoveMousePos = TeVector2s32();
}
Character *character = _scene._character;
if (app->isLockCursor() || _movePlayerCharacterDisabled || !character)
return false;
const Common::String &charAnim = character->curAnimName();
if (charAnim == character->characterSettings()._idleAnimFileName
|| charAnim == character->walkAnim(Character::WalkPart_Start)
|| charAnim == character->walkAnim(Character::WalkPart_Loop)
|| charAnim == character->walkAnim(Character::WalkPart_EndD)
|| charAnim == character->walkAnim(Character::WalkPart_EndG)) {
_luaScript.execute("On");
if (!_scene.isObjectBlocking(nearestMeshName) && character->freeMoveZone()) {
const TeVector3f32 charPos = character->_model->position();
TeIntrusivePtr<TeBezierCurve> curve = character->freeMoveZone()->curve(charPos, pt, 8.0, true);
if (!curve)
return false;
_scene.setCurve(curve);
character->setCurveStartLocation(TeVector3f32());
if (curve->controlPoints().size() == 1) {
character->endMove();
} else {
if (!_walkTimer.running() || _walkTimer.timeElapsed() > 300000 || !_runModeEnabled) {
_walkTimer.stop();
_walkTimer.start();
character->walkMode("Walk");
} else {
// Note: original checks the timer elapsed again here.. why?
_walkTimer.stop();
character->walkMode("Jog");
}
character->placeOnCurve(curve);
character->setCurveOffset(0.0);
if (charAnim != character->walkAnim(Character::WalkPart_Start)) {
character->setAnimation(character->walkAnim(Character::WalkPart_Start), false);
}
character->walkTo(1.0, false);
_sceneCharacterVisibleFromLoad = false;
_lastCharMoveMousePos = pt;
}
}
// FIXME: The original never checks for empty/null curve here..
// but it seems our curve can possibly become null.
if (_scene.curve() && _scene.curve()->length()) {
TeVector3f32 lastPoint = _scene.curve()->controlPoints().back();
character->setAnimation(character->walkAnim(Character::WalkPart_Loop), true);
character->walkTo(1.0, false);
_isCharacterWalking = true;
_posPlayer = lastPoint;
}
}
// Note: charAnim above may no longer be valid as anim may have changed.
if (_sceneCharacterVisibleFromLoad || (character->curAnimName() == character->characterSettings()._idleAnimFileName)) {
_lastCharMoveMousePos = TeVector2s32();
_movePlayerCharacterDisabled = false;
_isCharacterIdle = true;
_isCharacterWalking = false;
if (nearestMesh) {
character->stop();
_luaScript.execute("OnWarpObjectHit", nearestMeshName);
}
}
return false;
}
bool SyberiaGame::onVideoFinished() {
if (!_inGameGui.loaded()) {
_videoMusic.stop();
return false;
}
Application *app = g_engine->getApplication();
app->captureFade();
TeSpriteLayout *video = _inGameGui.spriteLayoutChecked("video");
Common::String vidPath = video->_tiledSurfacePtr->loadedPath().toString('/');
TeButtonLayout *btn = _inGameGui.buttonLayoutChecked("videoBackgroundButton");
btn->setVisible(false);
btn = _inGameGui.buttonLayoutChecked("skipVideoButton");
btn->setVisible(false);
video->setVisible(false);
_videoMusic.stop();
_running = true;
bool resumed = false;
for (uint i = 0; i < _yieldedCallbacks.size(); i++) {
YieldedCallback &cb = _yieldedCallbacks[i];
if (cb._luaFnName == "OnMovieFinished" && cb._luaParam == vidPath) {
TeLuaThread *lua = cb._luaThread;
_yieldedCallbacks.remove_at(i);
resumed = true;
if (lua)
lua->resume();
break;
}
}
if (!resumed)
_luaScript.execute("OnMovieFinished", vidPath);
app->fade();
return false;
}
#ifdef TETRAEDGE_ENABLE_CUSTOM_CURSOR_CHECKS
// Note: None of these cursor files seem to be actually shipped with the game
// but the logic is reproduced here just in case there's some different
// version that uses them.
static const char cursorsTable[][2][80] = {
{"H000", "pictures/F000.png"},
{"H045", "pictures/F045.png"},
{"H090", "pictures/F090.png"},
{"H135", "pictures/F135.png"},
{"H180", "pictures/F180.png"},
{"H225", "pictures/F225.png"},
{"H270", "pictures/F270.png"},
{"H315", "pictures/F315.png"},
{"Main", "pictures/Main.png"},
{"Action", "pictures/Action.png"},
{"Parler", "pictures/Cursor_Large/Anim_Cursor_Talking.anim"},
{"Type01", "pictures/Type01.png"},
{"Type02", "pictures/Type02.png"},
{"Type03", "pictures/Type03.png"},
{"Type04", "pictures/Type04.png"},
{"Type05", "pictures/Type05.png"}
};
#endif
/* Unused
void SyberiaGame::pauseMovie() {
_music.pause();
TeSpriteLayout *sprite = _inGameGui.spriteLayoutChecked("video");
sprite->pause();
}
*/
void SyberiaGame::playRandomSound(const Common::String &name) {
if (!_randomSounds.contains(name)) {
warning("Game::playRandomSound: can't find sound list %s", name.c_str());
return;
}
if (!_randomSoundFinished) {
_randomSoundTimer.start();
int r = g_engine->getRandomNumber(RAND_MAX);
float f = (r + 1 + (r / 100) * -100);
uint64 time = 1000000;
if (f >= 25.0) {
time = f * 45000.0;
}
_randomSoundTimer.setAlarmIn(time);
_randomSoundTimer.alarmSignal().remove(_randomSound, &RandomSound::onSoundFinished);
_randomSoundTimer.alarmSignal().add(_randomSound, &RandomSound::onSoundFinished);
_randomSound->_name = name;
} else {
Common::Array<RandomSound *> &sndlist = _randomSounds[name];
float total = 0.0;
for (auto *snd : sndlist) {
total += snd->_f1;
}
int r = g_engine->getRandomNumber(RAND_MAX);
float total2 = 0.0;
uint i = 0;
while (i < sndlist.size() && total2 <= r * 4.656613e-10 * total) {
total2 += sndlist[i]->_f1;
i++;
}
assert(i > 0);
i--;
RandomSound *sound = sndlist[i];
sound->_music.volume(sound->_volume);
sound->_music.onStopSignal().remove(sound, &RandomSound::onSoundFinished);
sound->_music.onStopSignal().add(sound, &RandomSound::onSoundFinished);
sound->_music.load(sound->_path);
sound->_music.repeat(false);
sound->_music.play();
// TODO: set a flag that it's playing?
}
}
void SyberiaGame::removeNoScale2Children() {
if (!_noScaleLayout2)
return;
TeLayout *vidbtn = _inGameGui.layout("videoButtonLayout");
if (vidbtn)
_noScaleLayout2->removeChild(vidbtn);
TeLayout *bg = _inventory.cellphone()->gui().layout("background");
if (bg)
_noScaleLayout2->removeChild(bg);
TeButtonLayout *bgbtn = _objectif.gui1().buttonLayout("background");
if (bgbtn)
_noScaleLayout2->removeChild(bgbtn);
TeLayout *notifier = _notifier.gui().layout("notifier");
if (notifier)
_noScaleLayout2->removeChild(notifier);
}
void SyberiaGame::removeNoScaleChildren() {
if (!_noScaleLayout)
return;
_noScaleLayout->removeChild(&_question2);
Application *app = g_engine->getApplication();
app->frontLayout().removeChild(&_dialog2);
_noScaleLayout->removeChild(&_inventory);
_noScaleLayout->removeChild(&_inventoryMenu);
_noScaleLayout->removeChild(&_documentsBrowser);
_noScaleLayout->removeChild(&_documentsBrowser.zoomedLayout());
}
void SyberiaGame::resetPreviousMousePos() {
_previousMousePos = TeVector2s32(-1, -1);
}
bool SyberiaGame::unloadCharacter(const Common::String &charname) {
Character *c = _scene.character(charname);
if (!c)
return false;
for (uint i = 0; i < _scene.models().size(); i++) {
if (_scene.models()[i] == c->_model) {
_scene.models().remove_at(i);
break;
}
}
c->_onCharacterAnimFinishedSignal.remove(this, &SyberiaGame::onCharacterAnimationFinished);
c->removeAnim();
// Syberia 2 uses a simplified callback here.
// We have made onDisplacementPlayerFinished more like Syberia 1's onDisplacementPlayerFinished.
if (g_engine->gameType() == TetraedgeEngine::kSyberia)
c->onFinished().remove(this, &SyberiaGame::onDisplacementPlayerFinished);
else
c->onFinished().remove(this, &SyberiaGame::onDisplacementFinished);
_scene.unloadCharacter(charname);
return true;
}
bool SyberiaGame::unloadCharacters() {
// Loop will update the array, take a copy first.
Common::Array<Character *> chars = _scene._characters;
for (Character *c : chars) {
unloadCharacter(c->_model->name());
}
return true;
}
bool SyberiaGame::unloadPlayerCharacter(const Common::String &charname) {
Character *c = _scene.character(charname);
if (c) {
c->_onCharacterAnimFinishedSignal.remove(this, &SyberiaGame::onCharacterAnimationPlayerFinished);
c->onFinished().remove(this, &SyberiaGame::onDisplacementPlayerFinished);
_scene.unloadCharacter(charname);
}
return c != nullptr;
}
void SyberiaGame::update() {
if (!_entered)
return;
TeITextLayout *debugTimeTextLayout = _inGameGui.textLayout("debugTimeText1");
if (debugTimeTextLayout) {
warning("TODO: SyberiaGame::update: Fill out debugTimeTextLayout");
}
if (!_warped) {
if (_gameLoadState == 2) {
initLoadedBackupData();
return;
} else if (_gameLoadState != 0) {
return;
}
Application *app = g_engine->getApplication();
if (_scene._character) {
if (!_scene._character->_model->visible())
app->lockCursor(false);
}
TeButtonLayout *invbtn = _inGameGui.buttonLayout("inventoryButton");
if (invbtn)
invbtn->setVisible(!app->isLockCursor() && !_dialog2.isDialogPlaying());
Character *player = _scene._character;
if (player) {
TeIntrusivePtr<TeModel> model = player->_model;
bool modelVisible = model->visible();
if (model->anim())
player->permanentUpdate();
if (modelVisible) {
if (player->needsSomeUpdate()) {
_sceneCharacterVisibleFromLoad = false;
TeVector3f32 charPos = player->_model->position();
const Common::String charName = player->_model->name();
TeFreeMoveZone *zone = _scene.pathZone(player->freeMoveZoneName());
if (zone) {
TeIntrusivePtr<TeCamera> cam = _scene.currentCamera();
zone->setCamera(cam, false);
player->setFreeMoveZone(zone);
_scene.setPositionCharacter(charName, player->freeMoveZoneName(), charPos);
TeIntrusivePtr<TeBezierCurve> curve = zone->curve(model->position(), player->positionCharacter());
_scene.setCurve(curve);
player->setCurveStartLocation(TeVector3f32(0, 0, 0));
player->placeOnCurve(curve);
player->setCurveOffset(0.0f);
player->setAnimation(player->walkAnim(Character::WalkPart_Start), false);
player->walkTo(1.0f, false);
_isCharacterWalking = true;
}
player->setNeedsSomeUpdate(false);
}
const Common::String &charAnim = _scene._character->curAnimName();
bool unlockCursor = (charAnim == _scene._character->walkAnim(Character::WalkPart_Start) ||
charAnim == _scene._character->walkAnim(Character::WalkPart_Loop) ||
charAnim == _scene._character->walkAnim(Character::WalkPart_EndD) ||
charAnim == _scene._character->walkAnim(Character::WalkPart_EndG) ||
charAnim == _scene._character->characterSettings()._idleAnimFileName);
app->lockCursor(!unlockCursor);
}
}
Common::Array<Character *> characters = _scene._characters;
for (Character *c : characters) {
if (c->_model->anim())
c->permanentUpdate();
}
Common::Point mousePos = g_engine->getInputMgr()->lastMousePos();
if (_lastUpdateMousePos != mousePos) {
onMouseMove(mousePos);
_lastUpdateMousePos = mousePos;
}
if (_saveRequested) {
_saveRequested = false;
saveBackup("save.xml");
}
_luaScript.execute("Update");
_objectif.update();
_scene.update();
} else {
TeSoundManager *soundmgr = g_engine->getSoundManager();
// Take a copy in case the active music objects changes as we iterate.
Common::Array<TeMusic *> musics = soundmgr->musics();
for (TeMusic *music : musics) {
const Common::String &chanName = music->channelName();
if (chanName != "music" && chanName != "sfx" && chanName != "dialog")
music->stop();
}
changeWarp2(_warpZone, _warpScene, _warpFadeFlag);
}
}
bool SyberiaGame::HitObject::onChangeWarp() {
// Seems like this function is never used?
error("TODO: Implement SyberiaGame::HitObject::onChangeWarp");
return false;
}
bool SyberiaGame::HitObject::onDown() {
_game->luaScript().execute("OnButtonDown", _name);
_game->_isCharacterIdle = true;
return false;
}
bool SyberiaGame::HitObject::onUp() {
// debug("Game::HitObject mouseup: %s", _name.c_str());
_game->luaScript().execute("OnButtonUp", _name);
_game->_isCharacterIdle = true;
return false;
}
bool SyberiaGame::HitObject::onValidated() {
if (!g_engine->getApplication()->isLockCursor()) {
_game->luaScript().execute("OnWarpObjectHit", _name);
_game->_isCharacterIdle = true;
}
return false;
}
bool SyberiaGame::RandomSound::onSoundFinished() {
SyberiaGame *game = dynamic_cast<SyberiaGame *>(g_engine->getGame());
assert(game);
_music.onStopSignal().remove(this, &RandomSound::onSoundFinished);
if (game->_randomSoundFinished) {
game->_randomSoundFinished = false;
} else {
game->_randomSoundFinished = true;
game->_randomSound->_music.onStopSignal().remove(this, &RandomSound::onSoundFinished);
game->_randomSoundTimer.stop();
}
game->playRandomSound(_name);
return false;
}
} // end namespace Tetraedge