/* 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/config-manager.h" #include "common/textconsole.h" #include "common/file.h" #include "common/util.h" #include "common/events.h" #include "graphics/scaler.h" #include "tetraedge/tetraedge.h" #include "tetraedge/game/game.h" #include "tetraedge/game/application.h" #include "tetraedge/game/character.h" #include "tetraedge/game/characters_shadow.h" #include "tetraedge/game/in_game_scene.h" #include "tetraedge/te/te_core.h" #include "tetraedge/te/te_resource_manager.h" #include "tetraedge/te/te_renderer.h" #include "tetraedge/te/te_font3.h" #include "tetraedge/te/te_input_mgr.h" #include "tetraedge/te/te_sound_manager.h" //#define DUMP_LAYOUTS 1 namespace Tetraedge { bool Application::_dontUpdateWhenApplicationPaused = false; Application::Application() : _finishedGame(false), _finishedFremium(false), _captureFade(false), _difficulty(1), _created(false), _tutoActivated(false), _drawShadows(true) { TeCore *core = g_engine->getCore(); core->_coreNotReady = true; core->fileFlagSystemSetFlag("platform", "MacOSX"); core->fileFlagSystemSetFlag("part", "Full"); core->fileFlagSystemSetFlag("distributor", "DefaultDistributor"); TeLuaGUI tempGui; tempGui.load("texts/Part.lua"); _applicationTitle = tempGui.value("applicationTitle").toString(); _versionString = tempGui.value("versionString").toString(); _firstWarpPath = tempGui.value("firstWarpPath").toString(); _firstZone = tempGui.value("firstZone").toString(); _firstScene = tempGui.value("firstScene").toString(); TeSoundManager *soundmgr = g_engine->getSoundManager(); soundmgr->setChannelVolume("sfx", 0.7f); soundmgr->setChannelVolume("music", 0.7f); soundmgr->setChannelVolume("dialog", 0.7f); soundmgr->setChannelVolume("video", 0.7f); // TODO: Configure freemium things here? // Note: original has an app run timer, but it's never used? loadOptions("options.xml"); } Application::~Application() { destroy(); } void Application::create() { // TODO: Move mainWindowCamera to mainWindow? const int winWidth = g_engine->getDefaultScreenWidth(); const int winHeight = g_engine->getDefaultScreenHeight(); // See TeMainWindowBase::initCamera _mainWindowCamera.reset(new TeCamera()); _mainWindowCamera->setName("_mainWinCam"); _mainWindowCamera->setProjMatrixType(4); _mainWindowCamera->viewport(0, 0, winWidth, winHeight); _mainWindowCamera->orthogonalParams(winWidth * -0.5f, winWidth * 0.5f, winHeight * 0.5f, winHeight * -0.5f); _mainWindowCamera->setOrthoPlanes(-2048.0f, 2048.0f); _mainWindow.setSize(TeVector3f32(winWidth, winHeight, 0.0)); _mainWindow.setSizeType(TeILayout::ABSOLUTE); _mainWindow.setPositionType(TeILayout::ABSOLUTE); _mainWindow.setPosition(TeVector3f32(0.0f, 0.0f, 0.0f)); TeResourceManager *resmgr = g_engine->getResourceManager(); _fontComic = resmgr->getResourceNoSearch("Common/Fonts/ComicRelief.ttf"); _fontComic->load("Common/Fonts/ComicRelief.ttf"); _fontArgh = resmgr->getResourceNoSearch("Common/Fonts/Argh.ttf"); _fontArgh->load("Common/Fonts/Argh.ttf"); _fontArial = resmgr->getResourceNoSearch("Common/Fonts/arial.ttf"); _fontArial->load("Common/Fonts/arial.ttf"); _fontChaucer = resmgr->getResourceNoSearch("Common/Fonts/CHAUCER.TTF"); _fontChaucer->load("Common/Fonts/CHAUCER.ttf"); _fontColaborate = resmgr->getResourceNoSearch("Common/Fonts/Colaborate-Regular.otf"); _fontColaborate->load("Common/Fonts/Colaborate-Regular.ttf"); _fontProDisplay = resmgr->getResourceNoSearch("Common/Fonts/ProDisplay.ttf"); _fontProDisplay->load("Common/Fonts/ProDisplay.ttf"); // The app prebuilds some fonts.. cover letters, numbers, a few accented chars, and punctuation. // Skip that here. /* TeTextBase2 textBase; textBase.setText("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/,*?;.:/!\xA7&\xE9\"'(-\xE8_\xE7\xE0)="); textBase.setFont(0, _fontComic); textBase.setRect(TeVector2s32(1, 1)); textBase.setFontSize(12); textBase.build(); textBase.setFontSize(14); textBase.build(); textBase.setFontSize(16); textBase.build(); textBase.setFontSize(18); textBase.build(); textBase.setFontSize(30); textBase.build(); textBase.setFont(0, _fontColaborate); textBase.setFontSize(18); textBase.build(); textBase.setFont(0, _fontProDisplay); textBase.setFontSize(24); textBase.build(); */ TeCore *core = g_engine->getCore(); static const char allLangs[][3] = {"en", "fr", "de", "es", "it", "ru"}; const Common::Path textsPath("texts"); // Try alternate langs.. int i = 0; Common::Path textFilePath; while (i < ARRAYSIZE(allLangs)) { textFilePath = core->findFile(textsPath.join(core->language() + ".xml")); if (Common::File::exists(textFilePath)) break; core->language(allLangs[i]); i++; } if (i == ARRAYSIZE(allLangs)) { error("Couldn't find texts/[lang].xml for any language."); } _loc.load(textFilePath); core->addLoc(&_loc); const Common::Path helpMenuPath("menus/help/help_"); Common::Path helpMenuFilePath; i = 0; while (i < ARRAYSIZE(allLangs)) { helpMenuFilePath = helpMenuPath.append(core->language() + ".xml"); if (Common::File::exists(helpMenuFilePath)) break; core->language(allLangs[i]); i++; } if (i == ARRAYSIZE(allLangs)) { error("Couldn't find menus/help/help_[lang].xml for any language."); } _helpGui.load(helpMenuFilePath); // TODO: set TeCore field 0x74 and 0x78 to true here? Do they do anything?"); // Game calls these here but does nothing with result? //TeGetDeviceDPI(); //TeGetDeviceReferenceDPI(); _backLayout.setName("layoutBack"); _backLayout.setSizeType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT); _backLayout.setSize(TeVector3f32(1.0f, 1.0f, 0.0f)); _mainWindow.addChild(&_backLayout); _frontOrientationLayout.setName("orientationLayoutFront"); _frontOrientationLayout.setSizeType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT); _frontOrientationLayout.setSize(TeVector3f32(1.0f, 1.0f, 0.0f)); _mainWindow.addChild(&_frontOrientationLayout); _frontLayout.setName("layoutFront"); _frontLayout.setSizeType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT); _frontLayout.setSize(TeVector3f32(1.0f, 1.0f, 0.0f)); _frontOrientationLayout.addChild(&_frontLayout); _visFade.init(); _frontOrientationLayout.addChild(&_visFade._fadeCaptureSprite); _frontOrientationLayout.addChild(&_visFade._blackFadeSprite); _frontOrientationLayout.addChild(&_visFade._buttonLayout); _frontLayout.addChild(&_appSpriteLayout); _appSpriteLayout.setSizeType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT); _appSpriteLayout.setSize(TeVector3f32(1.0f, 1.0f, 1.0f)); _appSpriteLayout.setVisible(false); // Note: The games do some loading of a "version.ver" file here to add a // watermark to the backLayout, but that file doesn't exist in any of the // GOG games so it was probably only used during development. const Common::Path verFilePath("version.ver"); if (Common::File::exists(verFilePath)) { warning("Skipping doing anything with version.ver file"); } _mouseCursorLayout.setName("mouseCursor"); // Not needed in scummvm: g_system->showMouse(false); //mainWindow->setNativeCursorVisible(false); _mouseCursorLayout.load("pictures/cursor.png"); _mouseCursorLayout.setAnchor(TeVector3f32(0.3f, 0.1f, 0.0f)); _frontOrientationLayout.addChild(&_mouseCursorLayout); _lockCursorButton.setName("lockCursorButton"); _lockCursorButton.setSizeType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT); _lockCursorButton.setSize(TeVector3f32(2.0f, 0.095f, 0.0f)); _lockCursorButton.setPositionType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT); _lockCursorButton.setPosition(TeVector3f32(0.95f, 0.95f, 0.0f)); _lockCursorButton.setVisible(false); _frontOrientationLayout.addChild(&_lockCursorButton); _lockCursorFromActionButton.setName("lockCursorFromActionButton"); _lockCursorFromActionButton.setSizeType(TeLayout::CoordinatesType::RELATIVE_TO_PARENT); _lockCursorFromActionButton.setSize(TeVector3f32(2.0f, 2.0f, 0.0f)); _lockCursorFromActionButton.setVisible(false); _frontOrientationLayout.addChild(&_lockCursorFromActionButton); _autoSaveIcon1.setName("autosaveIcon"); _autoSaveIcon1.setAnchor(TeVector3f32(0.5f, 0.5f, 0.0f)); _autoSaveIcon1.setPosition(TeVector3f32(0.2f, 0.9f, 0.0f)); _autoSaveIcon1.setSize(TeVector3f32(128.0f, 64.0f, 0.0f)); _autoSaveIcon1.load("menus/inGame/autosave_icon.png"); _autoSaveIcon1.setVisible(false); _frontOrientationLayout.addChild(&_autoSaveIcon1); _autoSaveIconAnim1._runTimer.pausable(false); _autoSaveIconAnim1.pause(); _autoSaveIconAnim1._startVal = TeColor(255, 255, 255, 0); _autoSaveIconAnim1._endVal = TeColor(255, 255, 255, 255); _autoSaveIconAnim1._repeatCount = -1; Common::Array curve; curve.push_back(0.0f); curve.push_back(1.0f); curve.push_back(1.0f); curve.push_back(0.0f); _autoSaveIconAnim1.setCurve(curve); _autoSaveIconAnim1._duration = 4000.0f; _autoSaveIconAnim1._callbackObj = &_autoSaveIcon1; _autoSaveIconAnim1._callbackMethod = &Te3DObject2::setColor; _autoSaveIcon2.setName("autosaveIcon"); _autoSaveIcon2.setAnchor(TeVector3f32(0.5f, 0.5f, 0.0f)); _autoSaveIcon2.setPosition(TeVector3f32(0.2f, 0.7f, 0.0f)); _autoSaveIcon2.setSize(TeVector3f32(64.0f, 86.0f, 0.0f)); _autoSaveIcon2.load("menus/inGame/NoCel.png"); _autoSaveIcon2.setVisible(false); _frontOrientationLayout.addChild(&_autoSaveIcon2); _autoSaveIconAnim2._runTimer.pausable(false); _autoSaveIconAnim2.pause(); _autoSaveIconAnim2._startVal = TeColor(255, 255, 255, 0); _autoSaveIconAnim2._endVal = TeColor(255, 255, 255, 255); _autoSaveIconAnim2._repeatCount = 1; _autoSaveIconAnim2.setCurve(curve); _autoSaveIconAnim2._duration = 4000.0f; _autoSaveIconAnim2._callbackObj = &_autoSaveIcon2; _autoSaveIconAnim2._callbackMethod = &Te3DObject2::setColor; _visFade.blackFadeCurveAnim().onFinished().add(this, &Application::onBlackFadeAnimationFinished); g_engine->getInputMgr()->_mouseMoveSignal.add(this, &Application::onMousePositionChanged); onMainWindowSizeChanged(); _splashScreens.enter(); _drawShadows = (ConfMan.get("disable_shadows") != "true"); _created = true; } void Application::destroy() { Character::animCacheFreeAll(); _globalBonusMenu.unload(); _bonusMenu.unload(); _mainMenu.unload(); _credits.leave(); _ownerErrorMenu.unload(); _splashScreens.unload(); } void Application::startGame(bool newGame, int difficulty) { _appSpriteLayout.setVisible(false); _appSpriteLayout.pause(); _appSpriteLayout.unload(); if (newGame) _difficulty = difficulty; g_engine->getGame()->enter(); } void Application::resume() { error("TODO: Implement Application::resume"); } bool Application::run() { if (_created) { TeTimer::updateAll(); if (!_dontUpdateWhenApplicationPaused) { // Note: we run the inputmgr separately.. probably no need for this. //_inputmgr->update(); TeAnimation::updateAll(); //TeVideo::updateAll(); } _captureFade = false; TeRenderer *renderer = g_engine->getRenderer(); Game *game = g_engine->getGame(); renderer->reset(); game->update(); g_engine->getSoundManager()->update(); performRender(); if (game->_returnToMainMenu) { game->leave(true); if (!game->luaShowOwnerError()) { _mainMenu.enter(); } else { _ownerErrorMenu.enter(); } game->_returnToMainMenu = false; } if (_finishedGame) { game->leave(false); _mainMenu.enter(); if (Common::File::exists("finalURL.lua")) { TeLuaGUI finalGui; finalGui.load("finalURL.lua"); /*TeVariant finalVal =*/ finalGui.value("finalURL"); debug("TODO: use final URL??"); // TODO: Not clear if this variant is ever used in original. finalGui.unload(); } _finishedGame = false; } InGameScene::updateScroll(); TeObject::deleteNow(); } return true; } void Application::suspend() { error("TODO: Implement Application::suspend"); } void Application::showNoCellIcon(bool show) { if (!show) { _autoSaveIconAnim2._repeatCount = 1; } else { _autoSaveIcon2.setVisible(true); _autoSaveIcon2.setColor(TeColor(255, 255, 255, 255)); _autoSaveIconAnim2._repeatCount = -1; _autoSaveIconAnim2.play(); } } void Application::showLoadingIcon(bool show) { if (!show) { _autoSaveIconAnim1._repeatCount = 1; } else { _autoSaveIcon1.setVisible(true); _autoSaveIcon1.setColor(TeColor(255, 255, 255, 255)); _autoSaveIconAnim1._repeatCount = -1; _autoSaveIconAnim1.play(); } } void Application::saveCorrupted(const Common::String &fname) { error("TODO: Implement Application::showLoadingIcon"); } void Application::drawBack() { _mainWindowCamera->apply(); _backLayout.draw(); TeCamera::restore(); g_engine->getRenderer()->loadIdentityMatrix(); } void Application::drawFront() { _mainWindowCamera->apply(); _frontOrientationLayout.draw(); TeCamera::restore(); g_engine->getRenderer()->loadIdentityMatrix(); } #if DUMP_LAYOUTS static int renderCount = 0; static void dumpLayout(TeLayout *layout, Common::String indent = "++") { assert(layout); if (!layout->worldVisible()) return; debug("%s %s pos:%s worldScale:%s userSize:%s size:%s col:%s", indent.c_str(), layout->name().c_str(), layout->position().dump().c_str(), layout->worldScale().dump().c_str(), layout->userSize().dump().c_str(), layout->size().dump().c_str(), layout->color().dump().c_str()); for (auto & child: layout->childList()) { TeLayout *childLayout = dynamic_cast(child); if (childLayout) dumpLayout(childLayout, indent + "++"); } } #endif void Application::performRender() { Game *game = g_engine->getGame(); TeRenderer *renderer = g_engine->getRenderer(); if (_drawShadows && game->running() && game->scene()._character && game->scene().shadowLightNo() != -1 && game->scene().charactersShadow() != nullptr) { renderer->shadowMode(TeRenderer::ShadowMode1); game->scene().charactersShadow()->createTexture(&game->scene()); renderer->shadowMode(TeRenderer::ShadowMode0); } drawBack(); renderer->renderTransparentMeshes(); renderer->clearBuffer(TeRenderer::DepthBuffer); if (game->running()) { if (_drawShadows && game->scene()._character && game->scene().shadowLightNo() != -1 && game->scene().charactersShadow() != nullptr) { TeIntrusivePtr currentCamera = game->scene().currentCamera(); if (currentCamera) { currentCamera->apply(); renderer->shadowMode(TeRenderer::ShadowMode2); game->scene().charactersShadow()->draw(&game->scene()); renderer->shadowMode(TeRenderer::ShadowMode0); } } game->draw(); } renderer->renderTransparentMeshes(); renderer->clearBuffer(TeRenderer::DepthBuffer); drawFront(); renderer->renderTransparentMeshes(); game->scene().drawPath(); g_system->updateScreen(); #if DUMP_LAYOUTS renderCount++; if (renderCount % 100 == 0) { debug("\n--------------------\nFrame %d back layout: ", renderCount); dumpLayout(&_backLayout); debug("\n--------------------\nFrame %d front orientation layout: ", renderCount); dumpLayout(&_frontOrientationLayout); } #endif } //void Application::preloadTextrue(); does nothing.. void Application::fade() { _visFade.animateFade(); } void Application::blackFade() { _visFade.animateBlackFade(); } void Application::captureFade() { if (_captureFade) return; _captureFade = true; performRender(); _visFade.captureFrame(); } void Application::getSavegameThumbnail(Graphics::Surface &thumb) { captureFade(); Graphics::Surface screen; _visFade.texture()->writeTo(screen); screen.flipVertical(Common::Rect(screen.w, screen.h)); Common::ScopedPtr scaledScreen(screen.scale(kThumbnailWidth, kThumbnailHeight2)); thumb.copyFrom(*scaledScreen); screen.free(); scaledScreen->free(); } bool Application::isFading() { return _visFade.blackFading() || _visFade.fading(); } bool Application::onBlackFadeAnimationFinished() { error("TODO: Implement Application::onBlackFadeAnimationFinished"); return false; } bool Application::onMainWindowSizeChanged() { // This sets HD or SD "definition" in the core depending on device DPI. // For now just default to SD. debug("mainWindowSizeChanged: defaulting to SD."); g_engine->getCore()->fileFlagSystemSetFlag("definition", "SD"); return false; } bool Application::onMousePositionChanged(const Common::Point &p) { const TeVector3f32 mainWinSize = _mainWindow.size(); const TeVector3f32 newCursorPos(p.x / mainWinSize.x(), p.y / mainWinSize.y(), 0.0); _mouseCursorLayout.setPosition(newCursorPos); return false; } bool Application::isLockCursor() { return _lockCursorButton.visible() || _lockCursorFromActionButton.visible(); } bool Application::isLockPad() { Game *game = g_engine->getGame(); bool result = isLockCursor() || game->dialog2().isDialogPlaying() || game->isMoviePlaying() || game->question2().gui().layoutChecked("background")->visible() || game->isDocumentOpened(); return result; } void Application::lockCursor(bool lock) { _lockCursorButton.setVisible(lock); } void Application::lockCursorFromAction(bool lock) { _lockCursorFromActionButton.setVisible(lock); g_engine->getGame()->showMarkers(lock); } void Application::loadOptions(const Common::String &fname) { // TODO: Maybe load options here - original uses an // xml file but we would want confman. debug("TODO: Implement Application::loadOptions %s", fname.c_str()); } void Application::saveOptions(const Common::String &fname) { debug("TODO: Implement Application::saveOptions %s", fname.c_str()); } Common::String Application::getHelpText(const Common::String &key) { return _helpGui.value(key); } const char *Application::inAppUnlockFullVersionID() { error("TODO: Implement Application::inAppUnlockFullVersionID"); } } // end namespace Tetraedge