openmw-tes3coop/apps/openmw/engine.cpp
scrawl 54dac1460c Run global scripts after running local scripts
Turns out to be a compatibility problem with Calvus Horatius contract scripts. The local script needs to be run first, otherwise the cleanup when he quits as result of negative profit does not work properly.
2014-09-18 04:33:49 +02:00

586 lines
19 KiB
C++

#include "engine.hpp"
#include <stdexcept>
#include <iomanip>
#include <OgreRoot.h>
#include <OgreRenderWindow.h>
#include <MyGUI_WidgetManager.h>
#include <SDL.h>
#include <components/compiler/extensions0.hpp>
#include <components/bsa/resources.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/translation/translation.hpp>
#include <components/nifoverrides/nifoverrides.hpp>
#include <components/nifbullet/bulletnifloader.hpp>
#include <components/nifogre/ogrenifloader.hpp>
#include <components/esm/loadcell.hpp>
#include "mwinput/inputmanagerimp.hpp"
#include "mwgui/windowmanagerimp.hpp"
#include "mwscript/scriptmanagerimp.hpp"
#include "mwscript/extensions.hpp"
#include "mwscript/interpretercontext.hpp"
#include "mwsound/soundmanagerimp.hpp"
#include "mwworld/class.hpp"
#include "mwworld/player.hpp"
#include "mwworld/worldimp.hpp"
#include "mwclass/classes.hpp"
#include "mwdialogue/dialoguemanagerimp.hpp"
#include "mwdialogue/journalimp.hpp"
#include "mwmechanics/mechanicsmanagerimp.hpp"
#include "mwstate/statemanagerimp.hpp"
void OMW::Engine::executeLocalScripts()
{
MWWorld::LocalScripts& localScripts = MWBase::Environment::get().getWorld()->getLocalScripts();
localScripts.startIteration();
while (!localScripts.isFinished())
{
std::pair<std::string, MWWorld::Ptr> script = localScripts.getNext();
MWScript::InterpreterContext interpreterContext (
&script.second.getRefData().getLocals(), script.second);
MWBase::Environment::get().getScriptManager()->run (script.first, interpreterContext);
}
localScripts.setIgnore (MWWorld::Ptr());
}
bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt)
{
if (MWBase::Environment::get().getStateManager()->getState()!=
MWBase::StateManager::State_NoGame)
{
bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode();
MWBase::Environment::get().getWorld()->frameStarted(evt.timeSinceLastFrame, paused);
MWBase::Environment::get().getWindowManager ()->frameStarted(evt.timeSinceLastFrame);
}
return true;
}
bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
{
try
{
float frametime = std::min(evt.timeSinceLastFrame, 0.2f);
mEnvironment.setFrameDuration (frametime);
// update input
MWBase::Environment::get().getInputManager()->update(frametime, false);
// When the window is minimized, pause everything. Currently this *has* to be here to work around a MyGUI bug.
// If we are not currently rendering, then RenderItems will not be reused resulting in a memory leak upon changing widget textures.
if (!mOgre->getWindow()->isActive() || !mOgre->getWindow()->isVisible())
return true;
// sound
if (mUseSound)
MWBase::Environment::get().getSoundManager()->update(frametime);
// GUI active? Most game processing will be paused, but scripts still run.
bool guiActive = MWBase::Environment::get().getWindowManager()->isGuiMode();
// Main menu opened? Then scripts are also paused.
bool paused = MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu);
// update game state
MWBase::Environment::get().getStateManager()->update (frametime);
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_Running)
{
if (!paused)
{
// local scripts
executeLocalScripts();
// global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
}
if (!guiActive)
MWBase::Environment::get().getWorld()->advanceTime(
frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600);
}
// update actors
if (MWBase::Environment::get().getStateManager()->getState()!=
MWBase::StateManager::State_NoGame)
{
MWBase::Environment::get().getMechanicsManager()->update(frametime,
guiActive);
}
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_Running)
{
MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr();
if(!guiActive && player.getClass().getCreatureStats(player).isDead())
MWBase::Environment::get().getStateManager()->endGame();
}
// update world
if (MWBase::Environment::get().getStateManager()->getState()!=
MWBase::StateManager::State_NoGame)
{
MWBase::Environment::get().getWorld()->update(frametime, guiActive);
}
// update GUI
MWBase::Environment::get().getWindowManager()->onFrame(frametime);
if (MWBase::Environment::get().getStateManager()->getState()!=
MWBase::StateManager::State_NoGame)
{
Ogre::RenderWindow* window = mOgre->getWindow();
unsigned int tri, batch;
MWBase::Environment::get().getWorld()->getTriangleBatchCount(tri, batch);
MWBase::Environment::get().getWindowManager()->wmUpdateFps(window->getLastFPS(), tri, batch);
MWBase::Environment::get().getWindowManager()->update();
}
}
catch (const std::exception& e)
{
std::cerr << "Error in framelistener: " << e.what() << std::endl;
}
return true;
}
OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
: mOgre (0)
, mFpsLevel(0)
, mVerboseScripts (false)
, mSkipMenu (false)
, mUseSound (true)
, mCompileAll (false)
, mWarningsMode (1)
, mScriptContext (0)
, mFSStrict (false)
, mScriptConsoleMode (false)
, mCfgMgr(configurationManager)
, mEncoding(ToUTF8::WINDOWS_1252)
, mEncoder(NULL)
, mActivationDistanceOverride(-1)
, mGrab(true)
, mScriptBlacklistUse (true)
, mExportFonts(false)
, mNewGame (false)
{
std::srand ( std::time(NULL) );
MWClass::registerClasses();
Uint32 flags = SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE;
if(SDL_WasInit(flags) == 0)
{
//kindly ask SDL not to trash our OGL context
//might this be related to http://bugzilla.libsdl.org/show_bug.cgi?id=748 ?
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
SDL_SetMainReady();
if(SDL_Init(flags) != 0)
{
throw std::runtime_error("Could not initialize SDL! " + std::string(SDL_GetError()));
}
}
}
OMW::Engine::~Engine()
{
mEnvironment.cleanup();
delete mScriptContext;
delete mOgre;
SDL_Quit();
}
// add resources directory
// \note This function works recursively.
void OMW::Engine::addResourcesDirectory (const boost::filesystem::path& path)
{
mOgre->getRoot()->addResourceLocation (path.string(), "FileSystem",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true);
}
void OMW::Engine::addZipResource (const boost::filesystem::path& path)
{
mOgre->getRoot()->addResourceLocation (path.string(), "Zip",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, false);
}
void OMW::Engine::enableFSStrict(bool fsStrict)
{
mFSStrict = fsStrict;
}
// Set data dir
void OMW::Engine::setDataDirs (const Files::PathContainer& dataDirs)
{
mDataDirs = dataDirs;
mFileCollections = Files::Collections (dataDirs, !mFSStrict);
}
// Add BSA archive
void OMW::Engine::addArchive (const std::string& archive) {
mArchives.push_back(archive);
}
// Set resource dir
void OMW::Engine::setResourceDir (const boost::filesystem::path& parResDir)
{
mResDir = parResDir;
}
// Set start cell name (only interiors for now)
void OMW::Engine::setCell (const std::string& cellName)
{
mCellName = cellName;
}
void OMW::Engine::addContentFile(const std::string& file)
{
mContentFiles.push_back(file);
}
void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity)
{
mVerboseScripts = scriptsVerbosity;
}
void OMW::Engine::setSkipMenu (bool skipMenu, bool newGame)
{
mSkipMenu = skipMenu;
mNewGame = newGame;
}
std::string OMW::Engine::loadSettings (Settings::Manager & settings)
{
// Create the settings manager and load default settings file
const std::string localdefault = mCfgMgr.getLocalPath().string() + "/settings-default.cfg";
const std::string globaldefault = mCfgMgr.getGlobalPath().string() + "/settings-default.cfg";
// prefer local
if (boost::filesystem::exists(localdefault))
settings.loadDefault(localdefault);
else if (boost::filesystem::exists(globaldefault))
settings.loadDefault(globaldefault);
else
throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed.");
// load user settings if they exist, otherwise just load the default settings as user settings
const std::string settingspath = mCfgMgr.getUserConfigPath().string() + "/settings.cfg";
if (boost::filesystem::exists(settingspath))
settings.loadUser(settingspath);
else if (boost::filesystem::exists(localdefault))
settings.loadUser(localdefault);
else if (boost::filesystem::exists(globaldefault))
settings.loadUser(globaldefault);
mFpsLevel = settings.getInt("fps", "HUD");
// load nif overrides
NifOverrides::Overrides nifOverrides;
std::string transparencyOverrides = "/transparency-overrides.cfg";
std::string materialOverrides = "/material-overrides.cfg";
if (boost::filesystem::exists(mCfgMgr.getLocalPath().string() + transparencyOverrides))
nifOverrides.loadTransparencyOverrides(mCfgMgr.getLocalPath().string() + transparencyOverrides);
else if (boost::filesystem::exists(mCfgMgr.getGlobalPath().string() + transparencyOverrides))
nifOverrides.loadTransparencyOverrides(mCfgMgr.getGlobalPath().string() + transparencyOverrides);
if (boost::filesystem::exists(mCfgMgr.getLocalPath().string() + materialOverrides))
nifOverrides.loadMaterialOverrides(mCfgMgr.getLocalPath().string() + materialOverrides);
else if (boost::filesystem::exists(mCfgMgr.getGlobalPath().string() + materialOverrides))
nifOverrides.loadMaterialOverrides(mCfgMgr.getGlobalPath().string() + materialOverrides);
return settingspath;
}
void OMW::Engine::prepareEngine (Settings::Manager & settings)
{
mEnvironment.setStateManager (
new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0)));
std::string renderSystem = settings.getString("render system", "Video");
if (renderSystem == "")
{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
renderSystem = "Direct3D9 Rendering Subsystem";
#else
renderSystem = "OpenGL Rendering Subsystem";
#endif
}
mOgre = new OEngine::Render::OgreRenderer;
mOgre->configure(
mCfgMgr.getLogPath().string(),
renderSystem,
Settings::Manager::getString("opengl rtt mode", "Video"));
// This has to be added BEFORE MyGUI is initialized, as it needs
// to find core.xml here.
addResourcesDirectory(mCfgMgr.getCachePath ().string());
addResourcesDirectory(mResDir / "mygui");
addResourcesDirectory(mResDir / "water");
addResourcesDirectory(mResDir / "shadows");
OEngine::Render::WindowSettings windowSettings;
windowSettings.fullscreen = settings.getBool("fullscreen", "Video");
windowSettings.window_x = settings.getInt("resolution x", "Video");
windowSettings.window_y = settings.getInt("resolution y", "Video");
windowSettings.screen = settings.getInt("screen", "Video");
windowSettings.vsync = settings.getBool("vsync", "Video");
windowSettings.icon = "openmw.png";
std::string aa = settings.getString("antialiasing", "Video");
windowSettings.fsaa = (aa.substr(0, 4) == "MSAA") ? aa.substr(5, aa.size()-5) : "0";
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS,
settings.getBool("minimize on focus loss", "Video") ? "1" : "0");
mOgre->createWindow("OpenMW", windowSettings);
Bsa::registerResources (mFileCollections, mArchives, true, mFSStrict);
// Create input and UI first to set up a bootstrapping environment for
// showing a loading screen and keeping the window responsive while doing so
std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input_v2.xml").string();
bool keybinderUserExists = boost::filesystem::exists(keybinderUser);
MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists, mGrab);
mEnvironment.setInputManager (input);
MWGui::WindowManager* window = new MWGui::WindowManager(
mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"),
mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts);
mEnvironment.setWindowManager (window);
// Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound));
if (!mSkipMenu)
{
std::string logo = mFallbackMap["Movies_Company_Logo"];
if (!logo.empty())
window->playVideo(logo, 1);
}
// Create the world
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles,
mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap,
mActivationDistanceOverride, mCellName, mStartupScript));
MWBase::Environment::get().getWorld()->setupPlayer();
input->setPlayer(&mEnvironment.getWorld()->getPlayer());
window->initUI();
window->renderWorldMap();
//Load translation data
mTranslationDataStorage.setEncoder(mEncoder);
for (size_t i = 0; i < mContentFiles.size(); i++)
mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]);
Compiler::registerExtensions (mExtensions);
// Create script system
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full);
mScriptContext->setExtensions (&mExtensions);
mEnvironment.setScriptManager (new MWScript::ScriptManager (MWBase::Environment::get().getWorld()->getStore(),
mVerboseScripts, *mScriptContext, mWarningsMode,
mScriptBlacklistUse ? mScriptBlacklist : std::vector<std::string>()));
// Create game mechanics system
MWMechanics::MechanicsManager* mechanics = new MWMechanics::MechanicsManager;
mEnvironment.setMechanicsManager (mechanics);
// Create dialog system
mEnvironment.setJournal (new MWDialogue::Journal);
mEnvironment.setDialogueManager (new MWDialogue::DialogueManager (mExtensions, mVerboseScripts, mTranslationDataStorage));
mOgre->getRoot()->addFrameListener (this);
// scripts
if (mCompileAll)
{
std::pair<int, int> result = MWBase::Environment::get().getScriptManager()->compileAll();
if (result.first)
std::cout
<< "compiled " << result.second << " of " << result.first << " scripts ("
<< 100*static_cast<double> (result.second)/result.first
<< "%)"
<< std::endl;
}
}
// Initialise and enter main loop.
void OMW::Engine::go()
{
assert (!mContentFiles.empty());
assert (!mOgre);
Settings::Manager settings;
std::string settingspath;
settingspath = loadSettings (settings);
// Create encoder
ToUTF8::Utf8Encoder encoder (mEncoding);
mEncoder = &encoder;
prepareEngine (settings);
// Play some good 'ol tunes
MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore"));
// start in main menu
if (!mSkipMenu)
{
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
try
{
// Is there an ini setting for this filename or something?
MWBase::Environment::get().getSoundManager()->streamMusic("Special/morrowind title.mp3");
std::string logo = mFallbackMap["Movies_Morrowind_Logo"];
if (!logo.empty())
MWBase::Environment::get().getWindowManager()->playVideo(logo, true);
}
catch (...) {}
}
else
{
MWBase::Environment::get().getStateManager()->newGame (!mNewGame);
}
// Start the main rendering loop
while (!mEnvironment.get().getStateManager()->hasQuitRequest())
Ogre::Root::getSingleton().renderOneFrame();
// Save user settings
settings.saveUser(settingspath);
std::cout << "Quitting peacefully." << std::endl;
}
void OMW::Engine::activate()
{
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
return;
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject();
if (ptr.isEmpty())
return;
if (ptr.getClass().getName(ptr) == "") // objects without name presented to user can never be activated
return;
MWBase::Environment::get().getWorld()->activate(ptr, MWBase::Environment::get().getWorld()->getPlayerPtr());
}
void OMW::Engine::screenshot()
{
// Count screenshots.
int shotCount = 0;
const std::string& screenshotPath = mCfgMgr.getUserDataPath().string();
std::string format = Settings::Manager::getString("screenshot format", "General");
// Find the first unused filename with a do-while
std::ostringstream stream;
do
{
// Reset the stream
stream.str("");
stream.clear();
stream << screenshotPath << "screenshot" << std::setw(3) << std::setfill('0') << shotCount++ << "." << format;
} while (boost::filesystem::exists(stream.str()));
mOgre->screenshot(stream.str(), format);
}
void OMW::Engine::setCompileAll (bool all)
{
mCompileAll = all;
}
void OMW::Engine::setSoundUsage(bool soundUsage)
{
mUseSound = soundUsage;
}
void OMW::Engine::showFPS(int level)
{
mFpsLevel = level;
}
void OMW::Engine::setEncoding(const ToUTF8::FromType& encoding)
{
mEncoding = encoding;
}
void OMW::Engine::setFallbackValues(std::map<std::string,std::string> fallbackMap)
{
mFallbackMap = fallbackMap;
}
void OMW::Engine::setScriptConsoleMode (bool enabled)
{
mScriptConsoleMode = enabled;
}
void OMW::Engine::setStartupScript (const std::string& path)
{
mStartupScript = path;
}
void OMW::Engine::setActivationDistanceOverride (int distance)
{
mActivationDistanceOverride = distance;
}
void OMW::Engine::setWarningsMode (int mode)
{
mWarningsMode = mode;
}
void OMW::Engine::setScriptBlacklist (const std::vector<std::string>& list)
{
mScriptBlacklist = list;
}
void OMW::Engine::setScriptBlacklistUse (bool use)
{
mScriptBlacklistUse = use;
}
void OMW::Engine::enableFontExport(bool exportFonts)
{
mExportFonts = exportFonts;
}