Merge remote-tracking branch 'scrawl/master'

actorid
Marc Zinnschlag 11 years ago
commit 60a8a9e684

@ -219,7 +219,7 @@ bool Launcher::MainDialog::showFirstRunDialog()
} }
// Create the file if it doesn't already exist, else the importer will fail // Create the file if it doesn't already exist, else the importer will fail
QString path = QString::fromStdString(mCfgMgr.getUserPath().string()) + QString("openmw.cfg"); QString path = QString::fromStdString(mCfgMgr.getUserConfigPath().string()) + QString("openmw.cfg");
QFile file(path); QFile file(path);
if (!file.exists()) { if (!file.exists()) {
@ -334,7 +334,7 @@ bool Launcher::MainDialog::setupLauncherSettings()
{ {
mLauncherSettings.setMultiValueEnabled(true); mLauncherSettings.setMultiValueEnabled(true);
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string());
QStringList paths; QStringList paths;
paths.append(QString("launcher.cfg")); paths.append(QString("launcher.cfg"));
@ -440,7 +440,7 @@ bool Launcher::expansions(Launcher::UnshieldThread& cd)
bool Launcher::MainDialog::setupGameSettings() bool Launcher::MainDialog::setupGameSettings()
{ {
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string());
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
// Load the user config file first, separately // Load the user config file first, separately
@ -591,7 +591,7 @@ bool Launcher::MainDialog::setupGraphicsSettings()
{ {
mGraphicsSettings.setMultiValueEnabled(false); mGraphicsSettings.setMultiValueEnabled(false);
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string());
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
QFile localDefault(QString("settings-default.cfg")); QFile localDefault(QString("settings-default.cfg"));
@ -678,7 +678,7 @@ bool Launcher::MainDialog::writeSettings()
mGraphicsPage->saveSettings(); mGraphicsPage->saveSettings();
mDataFilesPage->saveSettings(); mDataFilesPage->saveSettings();
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString userPath = QString::fromStdString(mCfgMgr.getUserConfigPath().string());
QDir dir(userPath); QDir dir(userPath);
if (!dir.exists()) { if (!dir.exists()) {

@ -16,7 +16,7 @@ MwIniImporter::MwIniImporter()
const char *map[][2] = const char *map[][2] =
{ {
{ "fps", "General:Show FPS" }, { "fps", "General:Show FPS" },
{ "nosound", "General:Disable Audio" }, { "no-sound", "General:Disable Audio" },
{ 0, 0 } { 0, 0 }
}; };
const char *fallback[] = { const char *fallback[] = {

@ -86,10 +86,6 @@ void CS::Editor::setupDataFiles()
return; return;
} }
// Set the charset for reading the esm/esp files
// QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
//mFileDialog.setEncoding(encoding);
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
mDocumentManager.setResourceDir (variables["resources"].as<std::string>()); mDocumentManager.setResourceDir (variables["resources"].as<std::string>());

@ -42,10 +42,10 @@ int main(int argc, char *argv[])
// TODO: Ogre startup shouldn't be here, but it currently has to: // TODO: Ogre startup shouldn't be here, but it currently has to:
// SceneWidget destructor will delete the created render window, which would be called _after_ Root has shut down :( // SceneWidget destructor will delete the created render window, which would be called _after_ Root has shut down :(
OgreInit::OgreInit ogreInit;
ogreInit.init("./opencsOgre.log"); // TODO log path?
Application mApplication (argc, argv); Application mApplication (argc, argv);
OgreInit::OgreInit ogreInit;
ogreInit.init("./opencsOgre.log"); // TODO log path?
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath()); QDir dir(QCoreApplication::applicationDirPath());

@ -2221,7 +2221,7 @@ void CSMDoc::Document::createBase()
CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_) CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_)
: mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir), : mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir),
mProjectPath ((configuration.getUserPath() / "projects") / mProjectPath ((configuration.getUserDataPath() / "projects") /
(savePath.filename().string() + ".project")), (savePath.filename().string() + ".project")),
mSaving (*this, mProjectPath) mSaving (*this, mProjectPath)
{ {
@ -2254,7 +2254,7 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, co
} }
else else
{ {
boost::filesystem::path locCustomFiltersPath (configuration.getUserPath()); boost::filesystem::path locCustomFiltersPath (configuration.getUserDataPath());
locCustomFiltersPath /= "defaultfilters"; locCustomFiltersPath /= "defaultfilters";
if (boost::filesystem::exists(locCustomFiltersPath)) if (boost::filesystem::exists(locCustomFiltersPath))

@ -15,7 +15,7 @@
CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration) CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration)
: mConfiguration (configuration) : mConfiguration (configuration)
{ {
boost::filesystem::path projectPath = configuration.getUserPath() / "projects"; boost::filesystem::path projectPath = configuration.getUserDataPath() / "projects";
if (!boost::filesystem::is_directory (projectPath)) if (!boost::filesystem::is_directory (projectPath))
boost::filesystem::create_directories (projectPath); boost::filesystem::create_directories (projectPath);
@ -53,4 +53,4 @@ bool CSMDoc::DocumentManager::removeDocument (Document *document)
void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir) void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir)
{ {
mResDir = boost::filesystem::system_complete(parResDir); mResDir = boost::filesystem::system_complete(parResDir);
} }

@ -251,7 +251,7 @@ void CSMSettings::UserSettings::loadSettings (const QString &fileName)
bool localOk = loadFromFile(localFilePath); bool localOk = loadFromFile(localFilePath);
//user //user
mUserFilePath = QString::fromStdString(mCfgMgr.getUserPath().string()) + fileName; mUserFilePath = QString::fromStdString(mCfgMgr.getUserConfigPath().string()) + fileName;
loadFromFile(mUserFilePath); loadFromFile(mUserFilePath);
if (!(localOk || globalOk)) if (!(localOk || globalOk))

@ -20,7 +20,7 @@ add_openmw_dir (mwrender
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery water shadows actors objects renderinginterface localmap occlusionquery water shadows
characterpreview externalrendering globalmap videoplayer ripplesimulation refraction characterpreview externalrendering globalmap videoplayer ripplesimulation refraction
terrainstorage terrainstorage renderconst
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput

@ -65,10 +65,6 @@ void OMW::Engine::executeLocalScripts()
localScripts.setIgnore (MWWorld::Ptr()); localScripts.setIgnore (MWWorld::Ptr());
} }
void OMW::Engine::setAnimationVerbose(bool animverbose)
{
}
bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt) bool OMW::Engine::frameStarted (const Ogre::FrameEvent& evt)
{ {
bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode();
@ -301,7 +297,7 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings)
throw std::runtime_error ("No default settings file found! Make sure the file \"settings-default.cfg\" was properly installed."); 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 // load user settings if they exist, otherwise just load the default settings as user settings
const std::string settingspath = mCfgMgr.getUserPath().string() + "/settings.cfg"; const std::string settingspath = mCfgMgr.getUserConfigPath().string() + "/settings.cfg";
if (boost::filesystem::exists(settingspath)) if (boost::filesystem::exists(settingspath))
settings.loadUser(settingspath); settings.loadUser(settingspath);
else if (boost::filesystem::exists(localdefault)) else if (boost::filesystem::exists(localdefault))
@ -373,7 +369,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
// Create input and UI first to set up a bootstrapping environment for // Create input and UI first to set up a bootstrapping environment for
// showing a loading screen and keeping the window responsive while doing so // showing a loading screen and keeping the window responsive while doing so
std::string keybinderUser = (mCfgMgr.getUserPath() / "input.xml").string(); std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input.xml").string();
bool keybinderUserExists = boost::filesystem::exists(keybinderUser); bool keybinderUserExists = boost::filesystem::exists(keybinderUser);
MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists, mGrab); MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists, mGrab);
mEnvironment.setInputManager (input); mEnvironment.setInputManager (input);
@ -536,7 +532,7 @@ void OMW::Engine::screenshot()
// Count screenshots. // Count screenshots.
int shotCount = 0; int shotCount = 0;
const std::string screenshotPath = mCfgMgr.getUserPath().string(); const std::string& screenshotPath = mCfgMgr.getUserDataPath().string();
// Find the first unused filename with a do-while // Find the first unused filename with a do-while
std::ostringstream stream; std::ostringstream stream;

@ -171,8 +171,6 @@ namespace OMW
/// Font encoding /// Font encoding
void setEncoding(const ToUTF8::FromType& encoding); void setEncoding(const ToUTF8::FromType& encoding);
void setAnimationVerbose(bool animverbose);
void setFallbackValues(std::map<std::string,std::string> map); void setFallbackValues(std::map<std::string,std::string> map);
/// Enable console-only script functionality /// Enable console-only script functionality

@ -121,10 +121,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("content", bpo::value<StringsVector>()->default_value(StringsVector(), "") ("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")
->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon")
("anim-verbose", bpo::value<bool>()->implicit_value(true) ("no-sound", bpo::value<bool>()->implicit_value(true)
->default_value(false), "output animation indices files")
("nosound", bpo::value<bool>()->implicit_value(true)
->default_value(false), "disable all sounds") ->default_value(false), "disable all sounds")
("script-verbose", bpo::value<bool>()->implicit_value(true) ("script-verbose", bpo::value<bool>()->implicit_value(true)
@ -168,8 +165,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
bpo::store(valid_opts, variables); bpo::store(valid_opts, variables);
bpo::notify(variables); bpo::notify(variables);
cfgMgr.readConfiguration(variables, desc);
bool run = true; bool run = true;
if (variables.count ("help")) if (variables.count ("help"))
@ -187,6 +182,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
if (!run) if (!run)
return false; return false;
cfgMgr.readConfiguration(variables, desc);
engine.setGrabMouse(!variables.count("no-grab")); engine.setGrabMouse(!variables.count("no-grab"));
// Font encoding settings // Font encoding settings
@ -237,10 +234,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
engine.setNewGame(variables["new-game"].as<bool>()); engine.setNewGame(variables["new-game"].as<bool>());
// other settings // other settings
engine.setSoundUsage(!variables["nosound"].as<bool>()); engine.setSoundUsage(!variables["no-sound"].as<bool>());
engine.setScriptsVerbosity(variables["script-verbose"].as<bool>()); engine.setScriptsVerbosity(variables["script-verbose"].as<bool>());
engine.setCompileAll(variables["script-all"].as<bool>()); engine.setCompileAll(variables["script-all"].as<bool>());
engine.setAnimationVerbose(variables["anim-verbose"].as<bool>());
engine.setFallbackValues(variables["fallback"].as<FallbackMap>().mMap); engine.setFallbackValues(variables["fallback"].as<FallbackMap>().mMap);
engine.setScriptConsoleMode (variables["script-console"].as<bool>()); engine.setScriptConsoleMode (variables["script-console"].as<bool>());
engine.setStartupScript (variables["script-run"].as<std::string>()); engine.setStartupScript (variables["script-run"].as<std::string>());

@ -141,15 +141,15 @@ void MWBase::Environment::cleanup()
delete mScriptManager; delete mScriptManager;
mScriptManager = 0; mScriptManager = 0;
delete mWindowManager;
mWindowManager = 0;
delete mWorld; delete mWorld;
mWorld = 0; mWorld = 0;
delete mSoundManager; delete mSoundManager;
mSoundManager = 0; mSoundManager = 0;
delete mWindowManager;
mWindowManager = 0;
delete mInputManager; delete mInputManager;
mInputManager = 0; mInputManager = 0;
} }

@ -204,6 +204,7 @@ namespace MWBase
virtual void activateQuickKey (int index) = 0; virtual void activateQuickKey (int index) = 0;
virtual std::string getSelectedSpell() = 0;
virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0; virtual void setSelectedSpell(const std::string& spellId, int successChancePercent) = 0;
virtual void setSelectedEnchantItem(const MWWorld::Ptr& item) = 0; virtual void setSelectedEnchantItem(const MWWorld::Ptr& item) = 0;
virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0; virtual void setSelectedWeapon(const MWWorld::Ptr& item) = 0;

@ -414,6 +414,13 @@ namespace MWBase
virtual bool toggleGodMode() = 0; virtual bool toggleGodMode() = 0;
/**
* @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met.
* @param actor
* @return true if the spell can be casted (i.e. the animation should start)
*/
virtual bool startSpellCast (const MWWorld::Ptr& actor) = 0;
virtual void castSpell (const MWWorld::Ptr& actor) = 0; virtual void castSpell (const MWWorld::Ptr& actor) = 0;
virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects,

@ -466,7 +466,7 @@ namespace MWGui
std::string CreateClassDialog::getName() const std::string CreateClassDialog::getName() const
{ {
return mEditName->getOnlyText(); return mEditName->getCaption();
} }
std::string CreateClassDialog::getDescription() const std::string CreateClassDialog::getDescription() const

@ -228,8 +228,8 @@ namespace MWGui
DescriptionDialog(); DescriptionDialog();
~DescriptionDialog(); ~DescriptionDialog();
std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } std::string getTextInput() const { return mTextEdit->getCaption(); }
void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); } void setTextInput(const std::string &text) { mTextEdit->setCaption(text); }
protected: protected:
void onOkClicked(MyGUI::Widget* _sender); void onOkClicked(MyGUI::Widget* _sender);

@ -406,13 +406,14 @@ namespace MWGui
setTitle("#{sConsoleTitle} (" + object.getCellRef().mRefID + ")"); setTitle("#{sConsoleTitle} (" + object.getCellRef().mRefID + ")");
mPtr = object; mPtr = object;
} }
// User clicked on an object. Restore focus to the console command line.
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine);
} }
else else
{ {
setTitle("#{sConsoleTitle}"); setTitle("#{sConsoleTitle}");
mPtr = MWWorld::Ptr(); mPtr = MWWorld::Ptr();
} }
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCommandLine);
} }
void Console::onReferenceUnavailable() void Console::onReferenceUnavailable()

@ -61,8 +61,9 @@ namespace MWGui
mDraggedWidget = baseWidget; mDraggedWidget = baseWidget;
MyGUI::ImageBox* image = baseWidget->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::ImageBox* image = baseWidget->createWidget<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default);
int pos = path.rfind("."); size_t pos = path.rfind(".");
path.erase(pos); if (pos != std::string::npos)
path.erase(pos);
path.append(".dds"); path.append(".dds");
image->setImageTexture(path); image->setImageTexture(path);
image->setNeedMouseFocus(false); image->setNeedMouseFocus(false);

@ -120,10 +120,13 @@ namespace MWGui
Settings::Manager::getFloat(setting + " y", "Windows") * viewSize.height); Settings::Manager::getFloat(setting + " y", "Windows") * viewSize.height);
MyGUI::IntSize size (Settings::Manager::getFloat(setting + " w", "Windows") * viewSize.width, MyGUI::IntSize size (Settings::Manager::getFloat(setting + " w", "Windows") * viewSize.width,
Settings::Manager::getFloat(setting + " h", "Windows") * viewSize.height); Settings::Manager::getFloat(setting + " h", "Windows") * viewSize.height);
if (size.width != mMainWidget->getWidth() || size.height != mMainWidget->getHeight())
mPreviewDirty = true;
mMainWidget->setPosition(pos); mMainWidget->setPosition(pos);
mMainWidget->setSize(size); mMainWidget->setSize(size);
adjustPanes(); adjustPanes();
mPreviewDirty = true;
} }
TradeItemModel* InventoryWindow::getTradeModel() TradeItemModel* InventoryWindow::getTradeModel()

@ -269,13 +269,39 @@ namespace MWGui
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
MWMechanics::Spells& spells = stats.getSpells(); if (type == Type_Item || type == Type_MagicItem)
{
MWWorld::Ptr item = *button->getChildAt (0)->getUserData<MWWorld::Ptr>();
// make sure the item is available
if (item.getRefData ().getCount() < 1)
{
// Try searching for a compatible replacement
std::string id = item.getCellRef().mRefID;
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, id))
{
item = *it;
button->getChildAt(0)->setUserData(item);
break;
}
}
if (item.getRefData().getCount() < 1)
{
// No replacement was found
MWBase::Environment::get().getWindowManager ()->messageBox (
"#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item));
return;
}
}
}
if (type == Type_Magic) if (type == Type_Magic)
{ {
std::string spellId = button->getChildAt(0)->getUserString("Spell"); std::string spellId = button->getChildAt(0)->getUserString("Spell");
spells.setSelectedSpell(spellId);
store.setSelectedEnchantItem(store.end()); store.setSelectedEnchantItem(store.end());
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
} }
@ -283,15 +309,6 @@ namespace MWGui
{ {
MWWorld::Ptr item = *button->getChildAt (0)->getUserData<MWWorld::Ptr>(); MWWorld::Ptr item = *button->getChildAt (0)->getUserData<MWWorld::Ptr>();
// make sure the item is available
if (item.getRefData ().getCount() < 1)
{
// TODO: Try to find a replacement with the same ID?
MWBase::Environment::get().getWindowManager ()->messageBox (
"#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item));
return;
}
boost::shared_ptr<MWWorld::Action> action = MWWorld::Class::get(item).use(item); boost::shared_ptr<MWWorld::Action> action = MWWorld::Class::get(item).use(item);
action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); action->execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
@ -310,14 +327,6 @@ namespace MWGui
{ {
MWWorld::Ptr item = *button->getChildAt (0)->getUserData<MWWorld::Ptr>(); MWWorld::Ptr item = *button->getChildAt (0)->getUserData<MWWorld::Ptr>();
// make sure the item is available
if (item.getRefData ().getCount() == 0)
{
MWBase::Environment::get().getWindowManager ()->messageBox (
"#{sQuickMenu5} " + MWWorld::Class::get(item).getName(item));
return;
}
// retrieve ContainerStoreIterator to the item // retrieve ContainerStoreIterator to the item
MWWorld::ContainerStoreIterator it = store.begin(); MWWorld::ContainerStoreIterator it = store.begin();
for (; it != store.end(); ++it) for (; it != store.end(); ++it)
@ -336,13 +345,9 @@ namespace MWGui
MWWorld::ActionEquip action(item); MWWorld::ActionEquip action(item);
action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()); action.execute (MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ());
// since we changed equipping status, update the inventory window
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->updateItemView();
} }
store.setSelectedEnchantItem(it); store.setSelectedEnchantItem(it);
spells.setSelectedSpell("");
MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item);
} }
} }

@ -86,61 +86,8 @@ namespace MWGui
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
MWMechanics::Spells& spells = stats.getSpells(); MWMechanics::Spells& spells = stats.getSpells();
// the following code switches between selected enchanted item and selected spell (only one of these
// can be active at a time)
std::string selectedSpell = spells.getSelectedSpell();
MWWorld::Ptr selectedItem;
if (store.getSelectedEnchantItem() != store.end())
{
selectedSpell = "";
selectedItem = *store.getSelectedEnchantItem();
bool allowSelectedItem = true;
// make sure that the item is still in the player inventory, otherwise it can't be selected
bool found = false;
for (MWWorld::ContainerStoreIterator it(store.begin()); it != store.end(); ++it)
{
if (*it == selectedItem)
found = true;
}
if (!found)
allowSelectedItem = false;
// if the selected item can be equipped, make sure that it actually is equipped
std::pair<std::vector<int>, bool> slots_;
slots_ = MWWorld::Class::get(selectedItem).getEquipmentSlots(selectedItem);
if (!slots_.first.empty())
{
bool equipped = false;
for (int i=0; i < MWWorld::InventoryStore::Slots; ++i)
{
if (store.getSlot(i) != store.end() && *store.getSlot(i) == selectedItem)
{
equipped = true;
break;
}
}
if (!equipped)
allowSelectedItem = false;
}
if (!allowSelectedItem)
{
store.setSelectedEnchantItem(store.end());
spells.setSelectedSpell("");
MWBase::Environment::get().getWindowManager()->unsetSelectedSpell();
selectedItem = MWWorld::Ptr();
}
}
for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it) for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
{
spellList.push_back (it->first); spellList.push_back (it->first);
}
const MWWorld::ESMStore &esmStore = const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore(); MWBase::Environment::get().getWorld()->getStore();
@ -210,7 +157,7 @@ namespace MWGui
t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel);
t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected); t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected);
if (*it == selectedSpell) if (*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell())
t->setStateSelected(true); t->setStateSelected(true);
mHeight += spellHeight; mHeight += spellHeight;
@ -229,7 +176,7 @@ namespace MWGui
t->setUserString("Spell", *it); t->setUserString("Spell", *it);
t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel);
t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected); t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onSpellSelected);
t->setStateSelected(*it == selectedSpell); t->setStateSelected(*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell());
// cost / success chance // cost / success chance
MyGUI::Button* costChance = mSpellView->createWidget<MyGUI::Button>("SpellText", MyGUI::Button* costChance = mSpellView->createWidget<MyGUI::Button>("SpellText",
@ -239,7 +186,7 @@ namespace MWGui
costChance->setCaption(cost + "/" + chance); costChance->setCaption(cost + "/" + chance);
costChance->setTextAlign(MyGUI::Align::Right); costChance->setTextAlign(MyGUI::Align::Right);
costChance->setNeedMouseFocus(false); costChance->setNeedMouseFocus(false);
costChance->setStateSelected(*it == selectedSpell); costChance->setStateSelected(*it == MWBase::Environment::get().getWindowManager()->getSelectedSpell());
mHeight += spellHeight; mHeight += spellHeight;
@ -276,7 +223,9 @@ namespace MWGui
t->setUserString("Equipped", equipped ? "true" : "false"); t->setUserString("Equipped", equipped ? "true" : "false");
t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onEnchantedItemSelected); t->eventMouseButtonClick += MyGUI::newDelegate(this, &SpellWindow::onEnchantedItemSelected);
t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel); t->eventMouseWheel += MyGUI::newDelegate(this, &SpellWindow::onMouseWheel);
t->setStateSelected(item == selectedItem); if (store.getSelectedEnchantItem() != store.end())
t->setStateSelected(item == *store.getSelectedEnchantItem());
// cost / charge // cost / charge
MyGUI::Button* costCharge = mSpellView->createWidget<MyGUI::Button>(equipped ? "SpellText" : "SpellTextUnequipped", MyGUI::Button* costCharge = mSpellView->createWidget<MyGUI::Button>(equipped ? "SpellText" : "SpellTextUnequipped",
@ -302,7 +251,8 @@ namespace MWGui
costCharge->setCaption(cost + "/" + charge); costCharge->setCaption(cost + "/" + charge);
costCharge->setTextAlign(MyGUI::Align::Right); costCharge->setTextAlign(MyGUI::Align::Right);
costCharge->setNeedMouseFocus(false); costCharge->setNeedMouseFocus(false);
costCharge->setStateSelected(item == selectedItem); if (store.getSelectedEnchantItem() != store.end())
costCharge->setStateSelected(item == *store.getSelectedEnchantItem());
mHeight += spellHeight; mHeight += spellHeight;
} }
@ -349,9 +299,7 @@ namespace MWGui
void SpellWindow::onEnchantedItemSelected(MyGUI::Widget* _sender) void SpellWindow::onEnchantedItemSelected(MyGUI::Widget* _sender)
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
MWMechanics::Spells& spells = stats.getSpells();
MWWorld::Ptr item = *_sender->getUserData<MWWorld::Ptr>(); MWWorld::Ptr item = *_sender->getUserData<MWWorld::Ptr>();
// retrieve ContainerStoreIterator to the item // retrieve ContainerStoreIterator to the item
@ -379,7 +327,6 @@ namespace MWGui
} }
store.setSelectedEnchantItem(it); store.setSelectedEnchantItem(it);
spells.setSelectedSpell("");
MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item);
updateSpells(); updateSpells();
@ -389,9 +336,7 @@ namespace MWGui
{ {
std::string spellId = _sender->getUserString("Spell"); std::string spellId = _sender->getUserString("Spell");
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player); MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
MWMechanics::Spells& spells = stats.getSpells();
if (MyGUI::InputManager::getInstance().isShiftPressed()) if (MyGUI::InputManager::getInstance().isShiftPressed())
{ {
@ -419,7 +364,6 @@ namespace MWGui
} }
else else
{ {
spells.setSelectedSpell(spellId);
store.setSelectedEnchantItem(store.end()); store.setSelectedEnchantItem(store.end());
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
} }
@ -450,10 +394,7 @@ namespace MWGui
MWMechanics::Spells& spells = stats.getSpells(); MWMechanics::Spells& spells = stats.getSpells();
if (spells.getSelectedSpell() == mSpellToDelete) if (spells.getSelectedSpell() == mSpellToDelete)
{
spells.setSelectedSpell("");
MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); MWBase::Environment::get().getWindowManager()->unsetSelectedSpell();
}
spells.remove(mSpellToDelete); spells.remove(mSpellToDelete);

@ -15,8 +15,8 @@ namespace MWGui
public: public:
TextInputDialog(); TextInputDialog();
std::string getTextInput() const { return mTextEdit ? mTextEdit->getOnlyText() : ""; } std::string getTextInput() const { return mTextEdit->getCaption(); }
void setTextInput(const std::string &text) { if (mTextEdit) mTextEdit->setOnlyText(text); } void setTextInput(const std::string &text) { mTextEdit->setCaption(text); }
void setNextButtonShow(bool shown); void setNextButtonShow(bool shown);
void setTextLabel(const std::string &label); void setTextLabel(const std::string &label);

@ -1010,6 +1010,7 @@ namespace MWGui
void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent) void WindowManager::setSelectedSpell(const std::string& spellId, int successChancePercent)
{ {
mSelectedSpell = spellId;
mHud->setSelectedSpell(spellId, successChancePercent); mHud->setSelectedSpell(spellId, successChancePercent);
const ESM::Spell* spell = const ESM::Spell* spell =
@ -1020,6 +1021,7 @@ namespace MWGui
void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item) void WindowManager::setSelectedEnchantItem(const MWWorld::Ptr& item)
{ {
mSelectedSpell = "";
const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>() const ESM::Enchantment* ench = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>()
.find(MWWorld::Class::get(item).getEnchantment(item)); .find(MWWorld::Class::get(item).getEnchantment(item));
@ -1039,6 +1041,7 @@ namespace MWGui
void WindowManager::unsetSelectedSpell() void WindowManager::unsetSelectedSpell()
{ {
mSelectedSpell = "";
mHud->unsetSelectedSpell(); mHud->unsetSelectedSpell();
mSpellWindow->setTitle("#{sNone}"); mSpellWindow->setTitle("#{sNone}");
} }

@ -200,6 +200,7 @@ namespace MWGui
virtual void activateQuickKey (int index); virtual void activateQuickKey (int index);
virtual std::string getSelectedSpell() { return mSelectedSpell; }
virtual void setSelectedSpell(const std::string& spellId, int successChancePercent); virtual void setSelectedSpell(const std::string& spellId, int successChancePercent);
virtual void setSelectedEnchantItem(const MWWorld::Ptr& item); virtual void setSelectedEnchantItem(const MWWorld::Ptr& item);
virtual void setSelectedWeapon(const MWWorld::Ptr& item); virtual void setSelectedWeapon(const MWWorld::Ptr& item);
@ -288,6 +289,8 @@ namespace MWGui
void trackWindow(OEngine::GUI::Layout* layout, const std::string& name); void trackWindow(OEngine::GUI::Layout* layout, const std::string& name);
void onWindowChangeCoord(MyGUI::Window* _sender); void onWindowChangeCoord(MyGUI::Window* _sender);
std::string mSelectedSpell;
OEngine::GUI::MyGUIManager *mGuiManager; OEngine::GUI::MyGUIManager *mGuiManager;
OEngine::Render::OgreRenderer *mRendering; OEngine::Render::OgreRenderer *mRendering;
HUD *mHud; HUD *mHud;

@ -378,10 +378,9 @@ namespace MWInput
MWBase::Environment::get().getWorld()->togglePreviewMode(true); MWBase::Environment::get().getWorld()->togglePreviewMode(true);
} }
} else { } else {
if (mPreviewPOVDelay > 0.5) { //disable preview mode
//disable preview mode MWBase::Environment::get().getWorld()->togglePreviewMode(false);
MWBase::Environment::get().getWorld()->togglePreviewMode(false); if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= 0.5) {
} else if (mPreviewPOVDelay > 0.f) {
MWBase::Environment::get().getWorld()->togglePOV(); MWBase::Environment::get().getWorld()->togglePOV();
} }
mPreviewPOVDelay = 0.f; mPreviewPOVDelay = 0.f;

@ -350,7 +350,6 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mSkipAnim(false) , mSkipAnim(false)
, mSecondsOfRunning(0) , mSecondsOfRunning(0)
, mSecondsOfSwimming(0) , mSecondsOfSwimming(0)
, mFallHeight(0)
{ {
if(!mAnimation) if(!mAnimation)
return; return;
@ -426,10 +425,10 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
{ {
forcestateupdate = true; forcestateupdate = true;
// Shields shouldn't be visible during spellcasting // Shields/torches shouldn't be visible during spellcasting or hand-to-hand
// There seems to be no text keys for this purpose, except maybe for "[un]equip start/stop", // There seems to be no text keys for this purpose, except maybe for "[un]equip start/stop",
// but they are also present in weapon drawing animation. // but they are also present in weapon drawing animation.
mAnimation->showShield(weaptype != WeapType_Spell); mAnimation->showCarriedLeft(weaptype != WeapType_Spell && weaptype != WeapType_HandToHand);
std::string weapgroup; std::string weapgroup;
if(weaptype == WeapType_None) if(weaptype == WeapType_None)
@ -505,10 +504,21 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
mAttackType.clear(); mAttackType.clear();
if(mWeaponType == WeapType_Spell) if(mWeaponType == WeapType_Spell)
{ {
// Unset casting flag, otherwise pressing the mouse button down would
// continue casting every frame if there is no animation
mPtr.getClass().getCreatureStats(mPtr).setAttackingOrSpell(false);
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const std::string spellid = stats.getSpells().getSelectedSpell(); // For the player, set the spell we want to cast
if(!spellid.empty()) // This has to be done at the start of the casting animation,
// *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation)
if (mPtr.getRefData().getHandle() == "player")
stats.getSpells().setSelectedSpell(MWBase::Environment::get().getWindowManager()->getSelectedSpell());
std::string spellid = stats.getSpells().getSelectedSpell();
if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr))
{ {
static const std::string schools[] = { static const std::string schools[] = {
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
@ -709,7 +719,8 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()
&& mWeaponType != WeapType_Spell && mWeaponType != WeapType_HandToHand)
{ {
if(!mAnimation->isPlaying("torch")) if(!mAnimation->isPlaying("torch"))
mAnimation->play("torch", Priority_Torch, mAnimation->play("torch", Priority_Torch,
@ -789,10 +800,7 @@ void CharacterController::update(float duration)
} }
if(sneak || inwater || flying) if(sneak || inwater || flying)
{
vec.z = 0.0f; vec.z = 0.0f;
mFallHeight = mPtr.getRefData().getPosition().pos[2];
}
if(!onground && !flying && !inwater) if(!onground && !flying && !inwater)
{ {
@ -801,11 +809,7 @@ void CharacterController::update(float duration)
if (world->isSlowFalling(mPtr)) if (world->isSlowFalling(mPtr))
{ {
// SlowFalling spell effect is active, do not keep previous fall height // SlowFalling spell effect is active, do not keep previous fall height
mFallHeight = mPtr.getRefData().getPosition().pos[2]; cls.getCreatureStats(mPtr).land();
}
else
{
mFallHeight = std::max(mFallHeight, mPtr.getRefData().getPosition().pos[2]);
} }
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
@ -861,7 +865,8 @@ void CharacterController::update(float duration)
mJumpState = JumpState_Landing; mJumpState = JumpState_Landing;
vec.z = 0.0f; vec.z = 0.0f;
float healthLost = cls.getFallDamage(mPtr, mFallHeight - mPtr.getRefData().getPosition().pos[2]); float height = cls.getCreatureStats(mPtr).land();
float healthLost = cls.getFallDamage(mPtr, height);
if (healthLost > 0.0f) if (healthLost > 0.0f)
{ {
const float fatigueTerm = cls.getCreatureStats(mPtr).getFatigueTerm(); const float fatigueTerm = cls.getCreatureStats(mPtr).getFatigueTerm();
@ -882,8 +887,6 @@ void CharacterController::update(float duration)
//TODO: actor falls over //TODO: actor falls over
} }
} }
mFallHeight = mPtr.getRefData().getPosition().pos[2];
} }
else else
{ {
@ -922,6 +925,9 @@ void CharacterController::update(float duration)
} }
} }
if (onground || inwater || flying)
cls.getCreatureStats(mPtr).land();
if(movestate != CharState_None) if(movestate != CharState_None)
clearAnimQueue(); clearAnimQueue();

@ -156,9 +156,6 @@ class CharacterController
float mSecondsOfSwimming; float mSecondsOfSwimming;
float mSecondsOfRunning; float mSecondsOfRunning;
// used for acrobatics progress and fall damages
float mFallHeight;
std::string mAttackType; // slash, chop or thrust std::string mAttackType; // slash, chop or thrust
void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false);

@ -14,7 +14,8 @@ namespace MWMechanics
mTalkedTo (false), mAlarmed (false), mTalkedTo (false), mAlarmed (false),
mAttacked (false), mHostile (false), mAttacked (false), mHostile (false),
mAttackingOrSpell(false), mAttackType(AT_Chop), mAttackingOrSpell(false), mAttackType(AT_Chop),
mIsWerewolf(false) mIsWerewolf(false),
mFallHeight(0)
{ {
for (int i=0; i<4; ++i) for (int i=0; i<4; ++i)
mAiSettings[i] = 0; mAiSettings[i] = 0;
@ -356,4 +357,16 @@ namespace MWMechanics
{ {
mUsedPowers[power] = MWBase::Environment::get().getWorld()->getTimeStamp(); mUsedPowers[power] = MWBase::Environment::get().getWorld()->getTimeStamp();
} }
void CreatureStats::addToFallHeight(float height)
{
mFallHeight += height;
}
float CreatureStats::land()
{
float height = mFallHeight;
mFallHeight = 0;
return height;
}
} }

@ -36,6 +36,8 @@ namespace MWMechanics
bool mHostile; bool mHostile;
bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not. bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not.
float mFallHeight;
int mAttackType; int mAttackType;
std::string mLastHitObject; // The last object to hit this actor std::string mLastHitObject; // The last object to hit this actor
@ -49,6 +51,12 @@ namespace MWMechanics
public: public:
CreatureStats(); CreatureStats();
void addToFallHeight(float height);
/// Reset the fall height
/// @return total fall height
float land();
bool canUsePower (const std::string& power) const; bool canUsePower (const std::string& power) const;
void usePower (const std::string& power); void usePower (const std::string& power);

@ -300,10 +300,11 @@ namespace MWMechanics
if (item.getCellRef().mEnchantmentCharge == -1) if (item.getCellRef().mEnchantmentCharge == -1)
item.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge; item.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge;
if (mCaster.getRefData().getHandle() == "player" && item.getCellRef().mEnchantmentCharge < castCost) if (item.getCellRef().mEnchantmentCharge < castCost)
{ {
// TODO: Should there be a sound here? // TODO: Should there be a sound here?
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); if (mCaster.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}");
return false; return false;
} }
@ -370,33 +371,7 @@ namespace MWMechanics
fatigue.setCurrent(std::max(0.f, fatigue.getCurrent() - fatigueLoss)); fatigue.setCurrent(std::max(0.f, fatigue.getCurrent() - fatigueLoss));
stats.setFatigue(fatigue); stats.setFatigue(fatigue);
// Check mana
bool fail = false; bool fail = false;
DynamicStat<float> magicka = stats.getMagicka();
if (magicka.getCurrent() < spell->mData.mCost)
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientSP}");
fail = true;
}
// Reduce mana
if (!fail)
{
magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost);
stats.setMagicka(magicka);
}
// If this is a power, check if it was already used in last 24h
if (!fail && spell->mData.mType & ESM::Spell::ST_Power)
{
if (stats.canUsePower(spell->mId))
stats.usePower(spell->mId);
else
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sPowerAlreadyUsed}");
fail = true;
}
}
// Check success // Check success
int successChance = getSpellSuccessChance(spell, mCaster); int successChance = getSpellSuccessChance(spell, mCaster);

@ -125,7 +125,7 @@ namespace MWMechanics
const ESM::Spell *spell = const ESM::Spell *spell =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find (iter->first); MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find (iter->first);
if (spell->mData.mType & ESM::Spell::ST_Disease) if (spell->mData.mType == ESM::Spell::ST_Disease)
mSpells.erase(iter++); mSpells.erase(iter++);
else else
iter++; iter++;
@ -139,7 +139,7 @@ namespace MWMechanics
const ESM::Spell *spell = const ESM::Spell *spell =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find (iter->first); MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find (iter->first);
if (spell->mData.mType & ESM::Spell::ST_Blight) if (spell->mData.mType == ESM::Spell::ST_Blight)
mSpells.erase(iter++); mSpells.erase(iter++);
else else
iter++; iter++;

@ -150,9 +150,12 @@ void Actors::removeCell(MWWorld::Ptr::CellStore* store)
} }
} }
void Actors::update (float duration) void Actors::update (Ogre::Camera* camera)
{ {
// Nothing to do for(PtrAnimationMap::iterator iter = mAllActors.begin();iter != mAllActors.end(); ++iter)
{
iter->second->preRender(camera);
}
} }
Animation* Actors::getAnimation(const MWWorld::Ptr &ptr) Animation* Actors::getAnimation(const MWWorld::Ptr &ptr)

@ -47,7 +47,7 @@ namespace MWRender
void removeCell(MWWorld::CellStore* store); void removeCell(MWWorld::CellStore* store);
void update (float duration); void update (Ogre::Camera* camera);
/// Updates containing cell for object rendering data /// Updates containing cell for object rendering data
void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur); void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur);

@ -146,29 +146,21 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly)
struct AddGlow struct AddGlow
{ {
Ogre::Vector3* mColor; Ogre::Vector3* mColor;
AddGlow(Ogre::Vector3* col) : mColor(col) {} NifOgre::MaterialControllerManager* mMaterialControllerMgr;
AddGlow(Ogre::Vector3* col, NifOgre::MaterialControllerManager* materialControllerMgr)
: mColor(col)
, mMaterialControllerMgr(materialControllerMgr)
{}
// TODO: integrate this with material controllers?
void operator()(Ogre::Entity* entity) const void operator()(Ogre::Entity* entity) const
{ {
unsigned int numsubs = entity->getNumSubEntities(); if (!entity->getNumSubEntities())
for(unsigned int i = 0;i < numsubs;++i) return;
{ Ogre::MaterialPtr writableMaterial = mMaterialControllerMgr->getWritableMaterial(entity);
unsigned int numsubs = entity->getNumSubEntities(); sh::MaterialInstance* instance = sh::Factory::getInstance().getMaterialInstance(writableMaterial->getName());
for(unsigned int i = 0;i < numsubs;++i)
{ instance->setProperty("env_map", sh::makeProperty(new sh::BooleanValue(true)));
Ogre::SubEntity* subEnt = entity->getSubEntity(i); instance->setProperty("env_map_color", sh::makeProperty(new sh::Vector3(mColor->x, mColor->y, mColor->z)));
std::string newName = subEnt->getMaterialName() + "@fx";
if (sh::Factory::getInstance().searchInstance(newName) == NULL)
{
sh::MaterialInstance* instance =
sh::Factory::getInstance().createMaterialInstance(newName, subEnt->getMaterialName());
instance->setProperty("env_map", sh::makeProperty(new sh::BooleanValue(true)));
instance->setProperty("env_map_color", sh::makeProperty(new sh::Vector3(mColor->x, mColor->y, mColor->z)));
}
subEnt->setMaterialName(newName);
}
}
} }
}; };
@ -216,7 +208,7 @@ void Animation::setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint3
if (enchantedGlow) if (enchantedGlow)
std::for_each(objlist->mEntities.begin(), objlist->mEntities.end(), std::for_each(objlist->mEntities.begin(), objlist->mEntities.end(),
AddGlow(glowColor)); AddGlow(glowColor, &objlist->mMaterialControllerMgr));
} }
@ -1004,6 +996,16 @@ void Animation::detachObjectFromBone(Ogre::MovableObject *obj)
mSkelBase->detachObjectFromBone(obj); mSkelBase->detachObjectFromBone(obj);
} }
bool Animation::isPlaying(Group group) const
{
for (AnimStateMap::const_iterator stateiter = mStates.begin(); stateiter != mStates.end(); ++stateiter)
{
if(stateiter->second.mGroups == group)
return true;
}
return false;
}
void Animation::addEffect(const std::string &model, int effectId, bool loop, const std::string &bonename, std::string texture) void Animation::addEffect(const std::string &model, int effectId, bool loop, const std::string &bonename, std::string texture)
{ {
// Early out if we already have this effect // Early out if we already have this effect
@ -1025,6 +1027,10 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
params.mObjects = NifOgre::Loader::createObjects(mInsert, model); params.mObjects = NifOgre::Loader::createObjects(mInsert, model);
else else
params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model);
setRenderProperties(params.mObjects, RV_Misc,
RQG_Main, RQG_Alpha, 0.f, false, NULL);
params.mLoop = loop; params.mLoop = loop;
params.mEffectId = effectId; params.mEffectId = effectId;
params.mBoneName = bonename; params.mBoneName = bonename;
@ -1040,17 +1046,12 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con
for(size_t i = 0;i < params.mObjects->mParticles.size(); ++i) for(size_t i = 0;i < params.mObjects->mParticles.size(); ++i)
{ {
Ogre::ParticleSystem* partSys = params.mObjects->mParticles[i]; Ogre::ParticleSystem* partSys = params.mObjects->mParticles[i];
sh::Factory::getInstance()._ensureMaterial(partSys->getMaterialName(), "Default");
Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().getByName(partSys->getMaterialName()); Ogre::MaterialPtr mat = params.mObjects->mMaterialControllerMgr.getWritableMaterial(partSys);
static int count = 0;
Ogre::String materialName = "openmw/" + Ogre::StringConverter::toString(count++); for (int t=0; t<mat->getNumTechniques(); ++t)
// TODO: destroy when effect is removed
Ogre::MaterialPtr newMat = mat->clone(materialName);
partSys->setMaterialName(materialName);
for (int t=0; t<newMat->getNumTechniques(); ++t)
{ {
Ogre::Technique* tech = newMat->getTechnique(t); Ogre::Technique* tech = mat->getTechnique(t);
for (int p=0; p<tech->getNumPasses(); ++p) for (int p=0; p<tech->getNumPasses(); ++p)
{ {
Ogre::Pass* pass = tech->getPass(p); Ogre::Pass* pass = tech->getPass(p);
@ -1125,6 +1126,16 @@ void Animation::updateEffects(float duration)
} }
} }
void Animation::preRender(Ogre::Camera *camera)
{
for (std::vector<EffectParams>::iterator it = mEffects.begin(); it != mEffects.end(); ++it)
{
NifOgre::ObjectScenePtr objects = it->mObjects;
objects->rotateBillboardNodes(camera);
}
mObjectRoot->rotateBillboardNodes(camera);
}
// TODO: Should not be here // TODO: Should not be here
Ogre::Vector3 Animation::getEnchantmentColor(MWWorld::Ptr item) Ogre::Vector3 Animation::getEnchantmentColor(MWWorld::Ptr item)
{ {
@ -1188,6 +1199,8 @@ bool ObjectAnimation::canBatch() const
{ {
if(!mObjectRoot->mParticles.empty() || !mObjectRoot->mLights.empty() || !mObjectRoot->mControllers.empty()) if(!mObjectRoot->mParticles.empty() || !mObjectRoot->mLights.empty() || !mObjectRoot->mControllers.empty())
return false; return false;
if (!mObjectRoot->mBillboardNodes.empty())
return false;
return std::find_if(mObjectRoot->mEntities.begin(), mObjectRoot->mEntities.end(), return std::find_if(mObjectRoot->mEntities.begin(), mObjectRoot->mEntities.end(),
FindEntityTransparency()) == mObjectRoot->mEntities.end(); FindEntityTransparency()) == mObjectRoot->mEntities.end();
} }

@ -216,6 +216,9 @@ public:
void removeEffect (int effectId); void removeEffect (int effectId);
void getLoopingEffects (std::vector<int>& out); void getLoopingEffects (std::vector<int>& out);
/// Prepare this animation for being rendered with \a camera (rotates billboard nodes)
virtual void preRender (Ogre::Camera* camera);
virtual void setAlpha(float alpha) {} virtual void setAlpha(float alpha) {}
private: private:
void updateEffects(float duration); void updateEffects(float duration);
@ -255,6 +258,8 @@ public:
/** Returns true if the named animation group is playing. */ /** Returns true if the named animation group is playing. */
bool isPlaying(const std::string &groupname) const; bool isPlaying(const std::string &groupname) const;
bool isPlaying(Group group) const;
/** Gets info about the given animation group. /** Gets info about the given animation group.
* \param groupname Animation group to check. * \param groupname Animation group to check.
* \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc. * \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc.
@ -274,7 +279,7 @@ public:
virtual Ogre::Vector3 runAnimation(float duration); virtual Ogre::Vector3 runAnimation(float duration);
virtual void showWeapons(bool showWeapon); virtual void showWeapons(bool showWeapon);
virtual void showShield(bool show) {} virtual void showCarriedLeft(bool show) {}
void enableLights(bool enable); void enableLights(bool enable);

@ -29,7 +29,9 @@ namespace MWRender
mNearest(30.f), mNearest(30.f),
mFurthest(800.f), mFurthest(800.f),
mIsNearest(false), mIsNearest(false),
mIsFurthest(false) mIsFurthest(false),
mVanityToggleQueued(false),
mViewModeToggleQueued(false)
{ {
mVanity.enabled = false; mVanity.enabled = false;
mVanity.allowed = true; mVanity.allowed = true;
@ -103,6 +105,23 @@ namespace MWRender
void Camera::update(float duration, bool paused) void Camera::update(float duration, bool paused)
{ {
if (!mAnimation->isPlaying(MWRender::Animation::Group_UpperBody))
{
// Now process the view changes we queued earlier
if (mVanityToggleQueued)
{
toggleVanityMode(!mVanity.enabled);
mVanityToggleQueued = false;
}
if (mViewModeToggleQueued)
{
togglePreviewMode(false);
toggleViewMode();
mViewModeToggleQueued = false;
}
}
updateListener(); updateListener();
if (paused) if (paused)
return; return;
@ -121,6 +140,14 @@ namespace MWRender
void Camera::toggleViewMode() void Camera::toggleViewMode()
{ {
// Changing the view will stop all playing animations, so if we are playing
// anything important, queue the view change for later
if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody))
{
mViewModeToggleQueued = true;
return;
}
mFirstPersonView = !mFirstPersonView; mFirstPersonView = !mFirstPersonView;
processViewChange(); processViewChange();
@ -140,6 +167,14 @@ namespace MWRender
bool Camera::toggleVanityMode(bool enable) bool Camera::toggleVanityMode(bool enable)
{ {
// Changing the view will stop all playing animations, so if we are playing
// anything important, queue the view change for later
if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody))
{
mVanityToggleQueued = true;
return false;
}
if(!mVanity.allowed && enable) if(!mVanity.allowed && enable)
return false; return false;
@ -168,6 +203,9 @@ namespace MWRender
void Camera::togglePreviewMode(bool enable) void Camera::togglePreviewMode(bool enable)
{ {
if (mAnimation->isPlaying(MWRender::Animation::Group_UpperBody))
return;
if(mPreviewMode == enable) if(mPreviewMode == enable)
return; return;
@ -184,7 +222,6 @@ namespace MWRender
} }
mCamera->setPosition(0.f, 0.f, offset); mCamera->setPosition(0.f, 0.f, offset);
rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false);
} }
void Camera::setSneakOffset() void Camera::setSneakOffset()
@ -319,6 +356,7 @@ namespace MWRender
mAnimation->setViewMode(NpcAnimation::VM_Normal); mAnimation->setViewMode(NpcAnimation::VM_Normal);
mCameraNode->attachObject(mCamera); mCameraNode->attachObject(mCamera);
} }
rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false);
} }
void Camera::getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera) void Camera::getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera)

@ -47,6 +47,9 @@ namespace MWRender
bool mDistanceAdjusted; bool mDistanceAdjusted;
bool mVanityToggleQueued;
bool mViewModeToggleQueued;
/// Updates sound manager listener data /// Updates sound manager listener data
void updateListener(); void updateListener();
@ -77,6 +80,7 @@ namespace MWRender
bool toggleVanityMode(bool enable); bool toggleVanityMode(bool enable);
void allowVanityMode(bool allow); void allowVanityMode(bool allow);
/// @note this may be ignored if an important animation is currently playing
void togglePreviewMode(bool enable); void togglePreviewMode(bool enable);
/// \brief Lowers the camera for sneak. /// \brief Lowers the camera for sneak.

@ -117,7 +117,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v
mListenerDisabled(disableListener), mListenerDisabled(disableListener),
mViewMode(viewMode), mViewMode(viewMode),
mShowWeapons(false), mShowWeapons(false),
mShowShield(true), mShowCarriedLeft(true),
mFirstPersonOffset(0.f, 0.f, 0.f), mFirstPersonOffset(0.f, 0.f, 0.f),
mAlpha(1.f) mAlpha(1.f)
{ {
@ -319,7 +319,7 @@ void NpcAnimation::updateParts()
} }
showWeapons(mShowWeapons); showWeapons(mShowWeapons);
showShield(mShowShield); showCarriedLeft(mShowCarriedLeft);
// Remember body parts so we only have to search through the store once for each race/gender/viewmode combination // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination
static std::map< std::pair<std::string,int>,std::vector<const ESM::BodyPart*> > sRaceMapping; static std::map< std::pair<std::string,int>,std::vector<const ESM::BodyPart*> > sRaceMapping;
@ -654,32 +654,24 @@ void NpcAnimation::showWeapons(bool showWeapon)
mAlpha = 1.f; mAlpha = 1.f;
} }
void NpcAnimation::showShield(bool show) void NpcAnimation::showCarriedLeft(bool show)
{ {
mShowShield = show; mShowCarriedLeft = show;
MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); MWWorld::InventoryStore &inv = MWWorld::Class::get(mPtr).getInventoryStore(mPtr);
MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if (shield != inv.end() && shield->getTypeName() == typeid(ESM::Light).name()) if(show && iter != inv.end())
{ {
// ... Except for lights, which are still shown during spellcasting since they Ogre::Vector3 glowColor = getEnchantmentColor(*iter);
// have their own (one-handed) casting animations std::string mesh = MWWorld::Class::get(*iter).getModel(*iter);
show = true;
}
if(show && shield != inv.end())
{
Ogre::Vector3 glowColor = getEnchantmentColor(*shield);
std::string mesh = MWWorld::Class::get(*shield).getModel(*shield);
addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1,
mesh, !shield->getClass().getEnchantment(*shield).empty(), &glowColor); mesh, !iter->getClass().getEnchantment(*iter).empty(), &glowColor);
if (shield->getTypeName() == typeid(ESM::Light).name()) if (iter->getTypeName() == typeid(ESM::Light).name())
addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], shield->get<ESM::Light>()->mBase); addExtraLight(mInsert->getCreator(), mObjectParts[ESM::PRT_Shield], iter->get<ESM::Light>()->mBase);
} }
else else
{
removeIndividualPart(ESM::PRT_Shield); removeIndividualPart(ESM::PRT_Shield);
}
} }
void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound) void NpcAnimation::permanentEffectAdded(const ESM::MagicEffect *magicEffect, bool isNew, bool playSound)
@ -730,6 +722,17 @@ void NpcAnimation::setAlpha(float alpha)
} }
} }
void NpcAnimation::preRender(Ogre::Camera *camera)
{
Animation::preRender(camera);
for (int i=0; i<ESM::PRT_Count; ++i)
{
if (mObjectParts[i].isNull())
continue;
mObjectParts[i]->rotateBillboardNodes(camera);
}
}
void NpcAnimation::applyAlpha(float alpha, Ogre::Entity *ent, NifOgre::ObjectScenePtr scene) void NpcAnimation::applyAlpha(float alpha, Ogre::Entity *ent, NifOgre::ObjectScenePtr scene)
{ {
ent->getSubEntity(0)->setRenderQueueGroup(alpha != 1.f || ent->getSubEntity(0)->getMaterial()->isTransparent() ent->getSubEntity(0)->setRenderQueueGroup(alpha != 1.f || ent->getSubEntity(0)->getMaterial()->isTransparent()

@ -53,7 +53,7 @@ private:
std::string mHairModel; std::string mHairModel;
ViewMode mViewMode; ViewMode mViewMode;
bool mShowWeapons; bool mShowWeapons;
bool mShowShield; bool mShowCarriedLeft;
int mVisibilityFlags; int mVisibilityFlags;
@ -100,7 +100,7 @@ public:
virtual Ogre::Vector3 runAnimation(float timepassed); virtual Ogre::Vector3 runAnimation(float timepassed);
virtual void showWeapons(bool showWeapon); virtual void showWeapons(bool showWeapon);
virtual void showShield(bool showShield); virtual void showCarriedLeft(bool showa);
void setViewMode(ViewMode viewMode); void setViewMode(ViewMode viewMode);
@ -116,6 +116,9 @@ public:
/// Make the NPC only partially visible /// Make the NPC only partially visible
virtual void setAlpha(float alpha); virtual void setAlpha(float alpha);
/// Prepare this animation for being rendered with \a camera (rotates billboard nodes)
virtual void preRender (Ogre::Camera* camera);
}; };
} }

@ -245,11 +245,16 @@ void Objects::disableLights()
it->second->enableLights(false); it->second->enableLights(false);
} }
void Objects::update(const float dt) void Objects::update(float dt, Ogre::Camera* camera)
{ {
PtrAnimationMap::const_iterator it = mObjects.begin(); PtrAnimationMap::const_iterator it = mObjects.begin();
for(;it != mObjects.end();it++) for(;it != mObjects.end();it++)
it->second->runAnimation(dt); it->second->runAnimation(dt);
it = mObjects.begin();
for(;it != mObjects.end();it++)
it->second->preRender(camera);
} }
void Objects::rebuildStaticGeometry() void Objects::rebuildStaticGeometry()

@ -48,7 +48,7 @@ public:
void enableLights(); void enableLights();
void disableLights(); void disableLights();
void update (const float dt); void update (float dt, Ogre::Camera* camera);
///< per-frame update ///< per-frame update
Ogre::AxisAlignedBox getDimensions(MWWorld::CellStore*); Ogre::AxisAlignedBox getDimensions(MWWorld::CellStore*);

@ -372,9 +372,9 @@ void RenderingManager::update (float duration, bool paused)
if(paused) if(paused)
return; return;
mActors->update (duration); mActors->update (mRendering.getCamera());
mObjects->update (duration); mPlayerAnimation->preRender(mRendering.getCamera());
mObjects->update (duration, mRendering.getCamera());
mSkyManager->update(duration); mSkyManager->update(duration);

@ -989,9 +989,12 @@ void VideoState::deinit()
this->audioq.cond.notify_one(); this->audioq.cond.notify_one();
this->videoq.cond.notify_one(); this->videoq.cond.notify_one();
this->parse_thread.join(); if (this->parse_thread.joinable())
this->video_thread.join(); this->parse_thread.join();
this->refresh_thread.join(); if (this->video_thread.joinable())
this->video_thread.join();
if (this->refresh_thread.joinable())
this->refresh_thread.join();
if(this->audio_st) if(this->audio_st)
avcodec_close((*this->audio_st)->codec); avcodec_close((*this->audio_st)->codec);

@ -131,6 +131,15 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr
MWWorld::ContainerStoreIterator it = addImp(itemPtr); MWWorld::ContainerStoreIterator it = addImp(itemPtr);
MWWorld::Ptr item = *it; MWWorld::Ptr item = *it;
// we may have copied an item from the world, so reset a few things first
item.getRefData().setBaseNode(NULL);
item.getCellRef().mPos.rot[0] = 0;
item.getCellRef().mPos.rot[1] = 0;
item.getCellRef().mPos.rot[2] = 0;
item.getCellRef().mPos.pos[0] = 0;
item.getCellRef().mPos.pos[1] = 0;
item.getCellRef().mPos.pos[2] = 0;
std::string script = MWWorld::Class::get(item).getScript(item); std::string script = MWWorld::Class::get(item).getScript(item);
if(script != "") if(script != "")
{ {

@ -94,6 +94,8 @@ namespace MWWorld
ContainerStoreIterator addNewStack (const Ptr& ptr); ContainerStoreIterator addNewStack (const Ptr& ptr);
///< Add the item to this container (do not try to stack it onto existing items) ///< Add the item to this container (do not try to stack it onto existing items)
virtual void flagAsModified();
public: public:
virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2);
@ -105,10 +107,6 @@ namespace MWWorld
void clear(); void clear();
///< Empty container. ///< Empty container.
virtual void flagAsModified();
///< \attention This function is internal to the world model and should not be called from
/// outside.
float getWeight() const; float getWeight() const;
///< Return total weight of the items contained in *this. ///< Return total weight of the items contained in *this.

@ -180,6 +180,29 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& actor)
std::pair<std::vector<int>, bool> itemsSlots = std::pair<std::vector<int>, bool> itemsSlots =
MWWorld::Class::get (*iter).getEquipmentSlots (*iter); MWWorld::Class::get (*iter).getEquipmentSlots (*iter);
// Skip items that have *only* harmful permanent effects
if (!test.getClass().getEnchantment(test).empty())
{
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Enchantment* enchantment = store.get<ESM::Enchantment>().find(test.getClass().getEnchantment(test));
bool harmfulEffect = false;
bool usefulEffect = false;
if (enchantment->mData.mType == ESM::Enchantment::ConstantEffect)
{
for (std::vector<ESM::ENAMstruct>::const_iterator it = enchantment->mEffects.mList.begin();
it != enchantment->mEffects.mList.end(); ++it)
{
const ESM::MagicEffect* effect = store.get<ESM::MagicEffect>().find(it->mEffectID);
if (effect->mData.mFlags & ESM::MagicEffect::Harmful)
harmfulEffect = true;
else
usefulEffect = true;
}
}
if (harmfulEffect && !usefulEffect)
continue;
}
for (std::vector<int>::const_iterator iter2 (itemsSlots.first.begin()); for (std::vector<int>::const_iterator iter2 (itemsSlots.first.begin());
iter2!=itemsSlots.first.end(); ++iter2) iter2!=itemsSlots.first.end(); ++iter2)
{ {

@ -18,6 +18,8 @@
#include "../mwbase/world.hpp" // FIXME #include "../mwbase/world.hpp" // FIXME
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include <components/esm/loadgmst.hpp> #include <components/esm/loadgmst.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
@ -573,9 +575,17 @@ namespace MWWorld
if(cell->hasWater()) if(cell->hasWater())
waterlevel = cell->mWater; waterlevel = cell->mWater;
float oldHeight = iter->first.getRefData().getPosition().pos[2];
Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum, Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum,
world->isFlying(iter->first), world->isFlying(iter->first),
waterlevel, mEngine); waterlevel, mEngine);
float heightDiff = newpos.z - oldHeight;
if (heightDiff < 0)
iter->first.getClass().getCreatureStats(iter->first).addToFallHeight(-heightDiff);
mMovementResults.push_back(std::make_pair(iter->first, newpos)); mMovementResults.push_back(std::make_pair(iter->first, newpos));
} }

@ -25,9 +25,6 @@ ESM::CellRef& MWWorld::Ptr::getCellRef() const
{ {
assert(mRef); assert(mRef);
if (mContainerStore)
mContainerStore->flagAsModified();
return mRef->mRef; return mRef->mRef;
} }
@ -35,9 +32,6 @@ MWWorld::RefData& MWWorld::Ptr::getRefData() const
{ {
assert(mRef); assert(mRef);
if (mContainerStore)
mContainerStore->flagAsModified();
return mRef->mData; return mRef->mData;
} }

@ -2049,15 +2049,56 @@ namespace MWWorld
} }
} }
bool World::startSpellCast(const Ptr &actor)
{
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
std::string message;
bool fail = false;
bool isPlayer = (actor == getPlayer().getPlayer());
std::string selectedSpell = stats.getSpells().getSelectedSpell();
if (!selectedSpell.empty())
{
const ESM::Spell* spell = getStore().get<ESM::Spell>().search(selectedSpell);
// Check mana
MWMechanics::DynamicStat<float> magicka = stats.getMagicka();
if (magicka.getCurrent() < spell->mData.mCost)
{
message = "#{sMagicInsufficientSP}";
fail = true;
}
// If this is a power, check if it was already used in the last 24h
if (!fail && spell->mData.mType == ESM::Spell::ST_Power)
{
if (stats.canUsePower(spell->mId))
stats.usePower(spell->mId);
else
{
message = "#{sPowerAlreadyUsed}";
fail = true;
}
}
// Reduce mana
magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost);
stats.setMagicka(magicka);
}
if (isPlayer && fail)
MWBase::Environment::get().getWindowManager()->messageBox(message);
return !fail;
}
void World::castSpell(const Ptr &actor) void World::castSpell(const Ptr &actor)
{ {
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
InventoryStore& inv = actor.getClass().getInventoryStore(actor); InventoryStore& inv = actor.getClass().getInventoryStore(actor);
// Unset casting flag, otherwise pressing the mouse button down would continue casting every frame if using an enchantment
// (which casts instantly without an animation)
stats.setAttackingOrSpell(false);
MWWorld::Ptr target = getFacedObject(); MWWorld::Ptr target = getFacedObject();
std::string selectedSpell = stats.getSpells().getSelectedSpell(); std::string selectedSpell = stats.getSpells().getSelectedSpell();

@ -499,6 +499,17 @@ namespace MWWorld
virtual bool toggleGodMode(); virtual bool toggleGodMode();
/**
* @brief startSpellCast attempt to start casting a spell. Might fail immediately if conditions are not met.
* @param actor
* @return true if the spell can be casted (i.e. the animation should start)
*/
virtual bool startSpellCast (const MWWorld::Ptr& actor);
/**
* @brief Cast the actual spell, should be called mid-animation
* @param actor
*/
virtual void castSpell (const MWWorld::Ptr& actor); virtual void castSpell (const MWWorld::Ptr& actor);
virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects,

@ -26,9 +26,10 @@ ConfigurationManager::ConfigurationManager()
{ {
setupTokensMapping(); setupTokensMapping();
boost::filesystem::create_directories(mFixedPath.getUserPath()); boost::filesystem::create_directories(mFixedPath.getUserConfigPath());
boost::filesystem::create_directories(mFixedPath.getUserDataPath());
mLogPath = mFixedPath.getUserPath(); mLogPath = mFixedPath.getUserConfigPath();
} }
ConfigurationManager::~ConfigurationManager() ConfigurationManager::~ConfigurationManager()
@ -39,19 +40,19 @@ void ConfigurationManager::setupTokensMapping()
{ {
mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath)); mTokensMapping.insert(std::make_pair(mwToken, &FixedPath<>::getInstallPath));
mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath)); mTokensMapping.insert(std::make_pair(localToken, &FixedPath<>::getLocalPath));
mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserPath)); mTokensMapping.insert(std::make_pair(userToken, &FixedPath<>::getUserConfigPath));
mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath)); mTokensMapping.insert(std::make_pair(globalToken, &FixedPath<>::getGlobalDataPath));
} }
void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables, void ConfigurationManager::readConfiguration(boost::program_options::variables_map& variables,
boost::program_options::options_description& description) boost::program_options::options_description& description)
{ {
loadConfig(mFixedPath.getUserPath(), variables, description); loadConfig(mFixedPath.getUserConfigPath(), variables, description);
boost::program_options::notify(variables); boost::program_options::notify(variables);
loadConfig(mFixedPath.getLocalPath(), variables, description); loadConfig(mFixedPath.getLocalPath(), variables, description);
boost::program_options::notify(variables); boost::program_options::notify(variables);
loadConfig(mFixedPath.getGlobalPath(), variables, description); loadConfig(mFixedPath.getGlobalConfigPath(), variables, description);
boost::program_options::notify(variables); boost::program_options::notify(variables);
} }
@ -141,12 +142,17 @@ void ConfigurationManager::loadConfig(const boost::filesystem::path& path,
const boost::filesystem::path& ConfigurationManager::getGlobalPath() const const boost::filesystem::path& ConfigurationManager::getGlobalPath() const
{ {
return mFixedPath.getGlobalPath(); return mFixedPath.getGlobalConfigPath();
} }
const boost::filesystem::path& ConfigurationManager::getUserPath() const const boost::filesystem::path& ConfigurationManager::getUserConfigPath() const
{ {
return mFixedPath.getUserPath(); return mFixedPath.getUserConfigPath();
}
const boost::filesystem::path& ConfigurationManager::getUserDataPath() const
{
return mFixedPath.getUserDataPath();
} }
const boost::filesystem::path& ConfigurationManager::getLocalPath() const const boost::filesystem::path& ConfigurationManager::getLocalPath() const

@ -36,7 +36,7 @@ struct ConfigurationManager
/**< Fixed paths */ /**< Fixed paths */
const boost::filesystem::path& getGlobalPath() const; const boost::filesystem::path& getGlobalPath() const;
const boost::filesystem::path& getUserPath() const; const boost::filesystem::path& getUserConfigPath() const;
const boost::filesystem::path& getLocalPath() const; const boost::filesystem::path& getLocalPath() const;
const boost::filesystem::path& getGlobalDataPath() const; const boost::filesystem::path& getGlobalDataPath() const;

@ -48,8 +48,9 @@ struct FixedPath
*/ */
FixedPath(const std::string& application_name) FixedPath(const std::string& application_name)
: mPath(application_name + "/") : mPath(application_name + "/")
, mUserPath(mPath.getUserPath()) , mUserConfigPath(mPath.getUserConfigPath())
, mGlobalPath(mPath.getGlobalPath()) , mUserDataPath(mPath.getUserDataPath())
, mGlobalConfigPath(mPath.getGlobalConfigPath())
, mLocalPath(mPath.getLocalPath()) , mLocalPath(mPath.getLocalPath())
, mGlobalDataPath(mPath.getGlobalDataPath()) , mGlobalDataPath(mPath.getGlobalDataPath())
, mInstallPath(mPath.getInstallPath()) , mInstallPath(mPath.getInstallPath())
@ -59,28 +60,27 @@ struct FixedPath
/** /**
* \brief Return path pointing to the user local configuration directory. * \brief Return path pointing to the user local configuration directory.
*
* \return boost::filesystem::path
*/ */
const boost::filesystem::path& getUserPath() const const boost::filesystem::path& getUserConfigPath() const
{
return mUserConfigPath;
}
const boost::filesystem::path& getUserDataPath() const
{ {
return mUserPath; return mUserDataPath;
} }
/** /**
* \brief Return path pointing to the global (system) configuration directory. * \brief Return path pointing to the global (system) configuration directory.
*
* \return boost::filesystem::path
*/ */
const boost::filesystem::path& getGlobalPath() const const boost::filesystem::path& getGlobalConfigPath() const
{ {
return mGlobalPath; return mGlobalConfigPath;
} }
/** /**
* \brief Return path pointing to the directory where application was started. * \brief Return path pointing to the directory where application was started.
*
* \return boost::filesystem::path
*/ */
const boost::filesystem::path& getLocalPath() const const boost::filesystem::path& getLocalPath() const
{ {
@ -105,8 +105,9 @@ struct FixedPath
private: private:
PathType mPath; PathType mPath;
boost::filesystem::path mUserPath; /**< User path */ boost::filesystem::path mUserConfigPath; /**< User path */
boost::filesystem::path mGlobalPath; /**< Global path */ boost::filesystem::path mUserDataPath;
boost::filesystem::path mGlobalConfigPath; /**< Global path */
boost::filesystem::path mLocalPath; /**< It is the same directory where application was run */ boost::filesystem::path mLocalPath; /**< It is the same directory where application was run */
boost::filesystem::path mGlobalDataPath; /**< Global application data path */ boost::filesystem::path mGlobalDataPath; /**< Global application data path */

@ -8,6 +8,39 @@
#include <unistd.h> #include <unistd.h>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
namespace
{
boost::filesystem::path getUserHome()
{
const char* dir = getenv("HOME");
if (dir == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
dir = pwd->pw_dir;
}
}
if (dir == NULL)
return boost::filesystem::path();
else
return boost::filesystem::path(dir);
}
boost::filesystem::path getEnv(const std::string& envVariable, const boost::filesystem::path& fallback)
{
const char* result = getenv(envVariable.c_str());
if (!result)
return fallback;
boost::filesystem::path dir(result);
if (dir.empty())
return fallback;
else
return dir;
}
}
/** /**
* \namespace Files * \namespace Files
*/ */
@ -19,51 +52,22 @@ LinuxPath::LinuxPath(const std::string& application_name)
{ {
} }
boost::filesystem::path LinuxPath::getUserPath() const boost::filesystem::path LinuxPath::getUserConfigPath() const
{ {
boost::filesystem::path userPath("."); return getEnv("XDG_CONFIG_HOME", getUserHome() / ".config") / mName;
}
const char* theDir = getenv("HOME");
if (theDir == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
theDir = pwd->pw_dir;
}
}
if (theDir != NULL)
{
userPath = boost::filesystem::path(theDir);
}
return userPath / ".config" / mName; boost::filesystem::path LinuxPath::getUserDataPath() const
{
return getEnv("XDG_DATA_HOME", getUserHome() / ".local/share") / mName;
} }
boost::filesystem::path LinuxPath::getCachePath() const boost::filesystem::path LinuxPath::getCachePath() const
{ {
boost::filesystem::path userPath("."); return getEnv("XDG_CACHE_HOME", getUserHome() / ".cache") / mName;
const char* theDir = getenv("HOME");
if (theDir == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
theDir = pwd->pw_dir;
}
}
if (theDir != NULL)
{
userPath = boost::filesystem::path(theDir);
}
return userPath / ".cache" / mName;
} }
boost::filesystem::path LinuxPath::getGlobalPath() const boost::filesystem::path LinuxPath::getGlobalConfigPath() const
{ {
boost::filesystem::path globalPath("/etc/"); boost::filesystem::path globalPath("/etc/");
return globalPath / mName; return globalPath / mName;
@ -84,17 +88,9 @@ boost::filesystem::path LinuxPath::getInstallPath() const
{ {
boost::filesystem::path installPath; boost::filesystem::path installPath;
char *homePath = getenv("HOME"); boost::filesystem::path homePath = getUserHome();
if (homePath == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
homePath = pwd->pw_dir;
}
}
if (homePath != NULL) if (!homePath.empty())
{ {
boost::filesystem::path wineDefaultRegistry(homePath); boost::filesystem::path wineDefaultRegistry(homePath);
wineDefaultRegistry /= ".wine/system.reg"; wineDefaultRegistry /= ".wine/system.reg";

@ -20,44 +20,34 @@ struct LinuxPath
/** /**
* \brief Return path to the user directory. * \brief Return path to the user directory.
*
* \return boost::filesystem::path
*/ */
boost::filesystem::path getUserPath() const; boost::filesystem::path getUserConfigPath() const;
boost::filesystem::path getUserDataPath() const;
/** /**
* \brief Return path to the global (system) directory where game files could be placed. * \brief Return path to the global (system) directory where config files can be placed.
*
* \return boost::filesystem::path
*/ */
boost::filesystem::path getGlobalPath() const; boost::filesystem::path getGlobalConfigPath() const;
/** /**
* \brief Return path to the runtime configuration directory which is the * \brief Return path to the runtime configuration directory which is the
* place where an application was started. * place where an application was started.
*
* \return boost::filesystem::path
*/ */
boost::filesystem::path getLocalPath() const; boost::filesystem::path getLocalPath() const;
/** /**
* \brief * \brief Return path to the global (system) directory where game files can be placed.
*
* \return boost::filesystem::path
*/ */
boost::filesystem::path getGlobalDataPath() const; boost::filesystem::path getGlobalDataPath() const;
/** /**
* \brief * \brief
*
* \return boost::filesystem::path
*/ */
boost::filesystem::path getCachePath() const; boost::filesystem::path getCachePath() const;
/** /**
* \brief Gets the path of the installed Morrowind version if there is one. * \brief Gets the path of the installed Morrowind version if there is one.
*
* \return boost::filesystem::path
*/ */
boost::filesystem::path getInstallPath() const; boost::filesystem::path getInstallPath() const;

@ -11,9 +11,26 @@
* FIXME: Someone with MacOS system should check this and correct if necessary * FIXME: Someone with MacOS system should check this and correct if necessary
*/ */
/** namespace
* \namespace Files {
*/ boost::filesystem::path getUserHome()
{
const char* dir = getenv("HOME");
if (dir == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
dir = pwd->pw_dir;
}
}
if (dir == NULL)
return boost::filesystem::path();
else
return boost::filesystem::path(dir);
}
}
namespace Files namespace Files
{ {
@ -22,28 +39,24 @@ MacOsPath::MacOsPath(const std::string& application_name)
{ {
} }
boost::filesystem::path MacOsPath::getUserPath() const boost::filesystem::path MacOsPath::getUserConfigPath() const
{ {
boost::filesystem::path userPath("."); boost::filesystem::path userPath (getUserHome());
userPath /= "Library/Preferences/";
const char* theDir = getenv("HOME"); return userPath / mName;
if (theDir == NULL) }
{
struct passwd* pwd = getpwuid(getuid()); boost::filesystem::path MacOsPath::getUserDataPath() const
if (pwd != NULL) {
{ // TODO: probably wrong?
theDir = pwd->pw_dir; boost::filesystem::path userPath (getUserHome());
} userPath /= "Library/Preferences/";
}
if (theDir != NULL)
{
userPath = boost::filesystem::path(theDir) / "Library/Preferences/";
}
return userPath / mName; return userPath / mName;
} }
boost::filesystem::path MacOsPath::getGlobalPath() const boost::filesystem::path MacOsPath::getGlobalConfigPath() const
{ {
boost::filesystem::path globalPath("/Library/Preferences/"); boost::filesystem::path globalPath("/Library/Preferences/");
return globalPath / mName; return globalPath / mName;
@ -51,23 +64,9 @@ boost::filesystem::path MacOsPath::getGlobalPath() const
boost::filesystem::path MacOsPath::getCachePath() const boost::filesystem::path MacOsPath::getCachePath() const
{ {
boost::filesystem::path userPath("."); boost::filesystem::path userPath (getUserHome());
userPath /= "Library/Caches";
const char* theDir = getenv("HOME"); return userPath / mName;
if (theDir == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
theDir = pwd->pw_dir;
}
}
if (theDir != NULL)
{
userPath = boost::filesystem::path(theDir) / "Library/Caches" / mName;
}
return userPath;
} }
boost::filesystem::path MacOsPath::getLocalPath() const boost::filesystem::path MacOsPath::getLocalPath() const
@ -85,17 +84,9 @@ boost::filesystem::path MacOsPath::getInstallPath() const
{ {
boost::filesystem::path installPath; boost::filesystem::path installPath;
char *homePath = getenv("HOME"); boost::filesystem::path homePath = getUserHome();
if (homePath == NULL)
{
struct passwd* pwd = getpwuid(getuid());
if (pwd != NULL)
{
homePath = pwd->pw_dir;
}
}
if (homePath != NULL) if (!homePath.empty())
{ {
boost::filesystem::path wineDefaultRegistry(homePath); boost::filesystem::path wineDefaultRegistry(homePath);
wineDefaultRegistry /= ".wine/system.reg"; wineDefaultRegistry /= ".wine/system.reg";

@ -23,14 +23,16 @@ struct MacOsPath
* *
* \return boost::filesystem::path * \return boost::filesystem::path
*/ */
boost::filesystem::path getUserPath() const; boost::filesystem::path getUserConfigPath() const;
boost::filesystem::path getUserDataPath() const;
/** /**
* \brief Return path to the global (system) directory. * \brief Return path to the global (system) directory.
* *
* \return boost::filesystem::path * \return boost::filesystem::path
*/ */
boost::filesystem::path getGlobalPath() const; boost::filesystem::path getGlobalConfigPath() const;
/** /**
* \brief Return path to the runtime directory which is the * \brief Return path to the runtime directory which is the

@ -25,7 +25,7 @@ WindowsPath::WindowsPath(const std::string& application_name)
{ {
} }
boost::filesystem::path WindowsPath::getUserPath() const boost::filesystem::path WindowsPath::getUserConfigPath() const
{ {
boost::filesystem::path userPath("."); boost::filesystem::path userPath(".");
@ -41,7 +41,13 @@ boost::filesystem::path WindowsPath::getUserPath() const
return userPath / mName; return userPath / mName;
} }
boost::filesystem::path WindowsPath::getGlobalPath() const boost::filesystem::path WindowsPath::getUserDataPath() const
{
// Have some chaos, windows people!
return getUserConfigPath();
}
boost::filesystem::path WindowsPath::getGlobalConfigPath() const
{ {
boost::filesystem::path globalPath("."); boost::filesystem::path globalPath(".");

@ -29,14 +29,16 @@ struct WindowsPath
* *
* \return boost::filesystem::path * \return boost::filesystem::path
*/ */
boost::filesystem::path getUserPath() const; boost::filesystem::path getUserConfigPath() const;
boost::filesystem::path getUserDataPath() const;
/** /**
* \brief Returns "X:\Program Files\" * \brief Returns "X:\Program Files\"
* *
* \return boost::filesystem::path * \return boost::filesystem::path
*/ */
boost::filesystem::path getGlobalPath() const; boost::filesystem::path getGlobalConfigPath() const;
/** /**
* \brief Return local path which is a location where * \brief Return local path which is a location where

@ -210,7 +210,7 @@ static const RecordFactoryEntry recordFactories [] = {
{ "AvoidNode", &construct <NiNode >, RC_AvoidNode }, { "AvoidNode", &construct <NiNode >, RC_AvoidNode },
{ "NiBSParticleNode", &construct <NiNode >, RC_NiBSParticleNode }, { "NiBSParticleNode", &construct <NiNode >, RC_NiBSParticleNode },
{ "NiBSAnimationNode", &construct <NiNode >, RC_NiBSAnimationNode }, { "NiBSAnimationNode", &construct <NiNode >, RC_NiBSAnimationNode },
{ "NiBillboardNode", &construct <NiNode >, RC_NiNode }, { "NiBillboardNode", &construct <NiNode >, RC_NiBillboardNode },
{ "NiTriShape", &construct <NiTriShape >, RC_NiTriShape }, { "NiTriShape", &construct <NiTriShape >, RC_NiTriShape },
{ "NiRotatingParticles", &construct <NiRotatingParticles >, RC_NiRotatingParticles }, { "NiRotatingParticles", &construct <NiRotatingParticles >, RC_NiRotatingParticles },
{ "NiAutoNormalParticles", &construct <NiAutoNormalParticles >, RC_NiAutoNormalParticles }, { "NiAutoNormalParticles", &construct <NiAutoNormalParticles >, RC_NiAutoNormalParticles },

@ -36,6 +36,7 @@ enum RecordType
{ {
RC_MISSING = 0, RC_MISSING = 0,
RC_NiNode, RC_NiNode,
RC_NiBillboardNode,
RC_AvoidNode, RC_AvoidNode,
RC_NiTriShape, RC_NiTriShape,
RC_NiRotatingParticles, RC_NiRotatingParticles,

@ -324,6 +324,12 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture])); instance->setProperty("normalMap", sh::makeProperty(texName[Nif::NiTexturingProperty::BumpTexture]));
instance->setProperty("detailMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DetailTexture])); instance->setProperty("detailMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DetailTexture]));
instance->setProperty("emissiveMap", sh::makeProperty(texName[Nif::NiTexturingProperty::GlowTexture])); instance->setProperty("emissiveMap", sh::makeProperty(texName[Nif::NiTexturingProperty::GlowTexture]));
instance->setProperty("darkMap", sh::makeProperty(texName[Nif::NiTexturingProperty::DarkTexture]));
if (!texName[Nif::NiTexturingProperty::BaseTexture].empty())
{
instance->setProperty("use_diffuse_map", sh::makeProperty(new sh::BooleanValue(true)));
instance->setProperty("diffuseMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::BaseTexture].uvSet)));
}
if (!texName[Nif::NiTexturingProperty::GlowTexture].empty()) if (!texName[Nif::NiTexturingProperty::GlowTexture].empty())
{ {
instance->setProperty("use_emissive_map", sh::makeProperty(new sh::BooleanValue(true))); instance->setProperty("use_emissive_map", sh::makeProperty(new sh::BooleanValue(true)));
@ -334,6 +340,11 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
instance->setProperty("use_detail_map", sh::makeProperty(new sh::BooleanValue(true))); instance->setProperty("use_detail_map", sh::makeProperty(new sh::BooleanValue(true)));
instance->setProperty("detailMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DetailTexture].uvSet))); instance->setProperty("detailMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DetailTexture].uvSet)));
} }
if (!texName[Nif::NiTexturingProperty::DarkTexture].empty())
{
instance->setProperty("use_dark_map", sh::makeProperty(new sh::BooleanValue(true)));
instance->setProperty("darkMapUVSet", sh::makeProperty(new sh::IntValue(texprop->textures[Nif::NiTexturingProperty::DarkTexture].uvSet)));
}
bool useParallax = !texName[Nif::NiTexturingProperty::BumpTexture].empty() bool useParallax = !texName[Nif::NiTexturingProperty::BumpTexture].empty()
&& texName[Nif::NiTexturingProperty::BumpTexture].find("_nh.") != std::string::npos; && texName[Nif::NiTexturingProperty::BumpTexture].find("_nh.") != std::string::npos;
@ -343,25 +354,30 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
{ {
if(i == Nif::NiTexturingProperty::BaseTexture || if(i == Nif::NiTexturingProperty::BaseTexture ||
i == Nif::NiTexturingProperty::DetailTexture || i == Nif::NiTexturingProperty::DetailTexture ||
i == Nif::NiTexturingProperty::DarkTexture ||
i == Nif::NiTexturingProperty::BumpTexture || i == Nif::NiTexturingProperty::BumpTexture ||
i == Nif::NiTexturingProperty::GlowTexture) i == Nif::NiTexturingProperty::GlowTexture)
continue; continue;
if(!texName[i].empty()) if(!texName[i].empty())
warn("Ignored texture "+texName[i]+" on layer "+Ogre::StringConverter::toString(i)); warn("Ignored texture "+texName[i]+" on layer "+Ogre::StringConverter::toString(i) + " in " + name);
} }
if (vertexColour) if (vertexColour)
instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true))); instance->setProperty("has_vertex_colour", sh::makeProperty(new sh::BooleanValue(true)));
// Add transparency if NiAlphaProperty was present // Override alpha flags based on our override list (transparency-overrides.cfg)
NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]); if (!texName[0].empty())
if (result.first)
{ {
alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */ NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName[0]);
alphaTest = result.second; if (result.first)
depthFlags = (1<<0) | (1<<1); // depth_write on, depth_check on {
alphaFlags = (1<<9) | (6<<10); /* alpha_rejection enabled, greater_equal */
alphaTest = result.second;
depthFlags = (1<<0) | (1<<1); // depth_write on, depth_check on
}
} }
// Add transparency if NiAlphaProperty was present
if((alphaFlags&1)) if((alphaFlags&1))
{ {
std::string blend_mode; std::string blend_mode;
@ -390,7 +406,6 @@ Ogre::String NIFMaterialLoader::getMaterial(const Nif::ShapeData *shapedata,
instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue(((depthFlags>>1)&1) ? "on" : "off"))); instance->setProperty("depth_write", sh::makeProperty(new sh::StringValue(((depthFlags>>1)&1) ? "on" : "off")));
// depth_func??? // depth_func???
sh::Factory::getInstance()._ensureMaterial(name, "Default");
return name; return name;
} }

@ -116,21 +116,6 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC; Ogre::HardwareBuffer::Usage vertUsage = Ogre::HardwareBuffer::HBU_STATIC;
bool vertShadowBuffer = false; bool vertShadowBuffer = false;
bool geomMorpherController = false;
if(!shape->controller.empty())
{
Nif::ControllerPtr ctrl = shape->controller;
do {
if(ctrl->recType == Nif::RC_NiGeomMorpherController)
{
vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY;
vertShadowBuffer = true;
geomMorpherController = true;
break;
}
} while(!(ctrl=ctrl->next).empty());
}
if(skin != NULL) if(skin != NULL)
{ {
vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY; vertUsage = Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY;
@ -350,10 +335,39 @@ void NIFMeshLoader::createSubMesh(Ogre::Mesh *mesh, const Nif::NiTriShape *shape
mesh->buildTangentVectors(Ogre::VES_TANGENT, src,dest); mesh->buildTangentVectors(Ogre::VES_TANGENT, src,dest);
} }
// Create a dummy vertex animation track if there's a geom morpher controller
// This is required to make Ogre create the buffers we will use for software vertex animation if(!shape->controller.empty())
if (srcVerts.size() && geomMorpherController) {
mesh->createAnimation("dummy", 0)->createVertexTrack(1, sub->vertexData, Ogre::VAT_MORPH); Nif::ControllerPtr ctrl = shape->controller;
do {
// Load GeomMorpherController into an Ogre::Pose and Animation
if(ctrl->recType == Nif::RC_NiGeomMorpherController)
{
const Nif::NiGeomMorpherController *geom =
static_cast<const Nif::NiGeomMorpherController*>(ctrl.getPtr());
const std::vector<Nif::NiMorphData::MorphData>& morphs = geom->data.getPtr()->mMorphs;
// Note we are not interested in morph 0, which just contains the original vertices
for (unsigned int i = 1; i < morphs.size(); ++i)
{
Ogre::Pose* pose = mesh->createPose(i);
const Nif::NiMorphData::MorphData& data = morphs[i];
for (unsigned int v = 0; v < data.mVertices.size(); ++v)
pose->addVertex(v, data.mVertices[v]);
Ogre::String animationID = Ogre::StringConverter::toString(ctrl->recIndex)
+ "_" + Ogre::StringConverter::toString(i);
Ogre::VertexAnimationTrack* track =
mesh->createAnimation(animationID, 0)
->createVertexTrack(1, Ogre::VAT_POSE);
Ogre::VertexPoseKeyFrame* keyframe = track->createVertexPoseKeyFrame(0);
keyframe->addPoseReference(i-1, 1);
}
break;
}
} while(!(ctrl=ctrl->next).empty());
}
} }

@ -110,6 +110,17 @@ ObjectScene::~ObjectScene()
mSkelBase = NULL; mSkelBase = NULL;
} }
void ObjectScene::rotateBillboardNodes(Ogre::Camera *camera)
{
for (std::vector<Ogre::Node*>::iterator it = mBillboardNodes.begin(); it != mBillboardNodes.end(); ++it)
{
assert(mSkelBase);
Ogre::Node* node = *it;
node->_setDerivedOrientation(mSkelBase->getParentNode()->_getDerivedOrientation().Inverse() *
camera->getRealOrientation());
}
}
// Animates a texture // Animates a texture
class FlipController class FlipController
{ {
@ -167,6 +178,7 @@ public:
if ((texture->getName() == "diffuseMap" && mTexSlot == Nif::NiTexturingProperty::BaseTexture) if ((texture->getName() == "diffuseMap" && mTexSlot == Nif::NiTexturingProperty::BaseTexture)
|| (texture->getName() == "normalMap" && mTexSlot == Nif::NiTexturingProperty::BumpTexture) || (texture->getName() == "normalMap" && mTexSlot == Nif::NiTexturingProperty::BumpTexture)
|| (texture->getName() == "detailMap" && mTexSlot == Nif::NiTexturingProperty::DetailTexture) || (texture->getName() == "detailMap" && mTexSlot == Nif::NiTexturingProperty::DetailTexture)
|| (texture->getName() == "darkMap" && mTexSlot == Nif::NiTexturingProperty::DarkTexture)
|| (texture->getName() == "emissiveMap" && mTexSlot == Nif::NiTexturingProperty::GlowTexture)) || (texture->getName() == "emissiveMap" && mTexSlot == Nif::NiTexturingProperty::GlowTexture))
texture->setTextureName(mTextures[curTexture]); texture->setTextureName(mTextures[curTexture]);
} }
@ -295,9 +307,6 @@ public:
return mData.back().isSet; return mData.back().isSet;
} }
// FIXME: We are not getting all objects here. Skinned meshes get
// attached to the object's root node, and won't be connected via a
// TagPoint.
static void setVisible(Ogre::Node *node, int vis) static void setVisible(Ogre::Node *node, int vis)
{ {
Ogre::Node::ChildNodeIterator iter = node->getChildIterator(); Ogre::Node::ChildNodeIterator iter = node->getChildIterator();
@ -306,6 +315,12 @@ public:
node = iter.getNext(); node = iter.getNext();
setVisible(node, vis); setVisible(node, vis);
// Skinned meshes and particle systems are attached to the scene node, not the bone.
// We use the Node's user data to connect it with the mesh / particle system.
Ogre::Any customData = node->getUserObjectBindings().getUserAny();
if (!customData.isEmpty())
Ogre::any_cast<Ogre::MovableObject*>(customData)->setVisible(vis);
Ogre::TagPoint *tag = dynamic_cast<Ogre::TagPoint*>(node); Ogre::TagPoint *tag = dynamic_cast<Ogre::TagPoint*>(node);
if(tag != NULL) if(tag != NULL)
{ {
@ -519,18 +534,18 @@ public:
class Value : public Ogre::ControllerValue<Ogre::Real>, public ValueInterpolator class Value : public Ogre::ControllerValue<Ogre::Real>, public ValueInterpolator
{ {
private: private:
Ogre::SubEntity *mSubEntity; Ogre::Entity *mEntity;
std::vector<Nif::NiMorphData::MorphData> mMorphs; std::vector<Nif::NiMorphData::MorphData> mMorphs;
std::vector<float> mValues; size_t mControllerIndex;
std::vector<Ogre::Vector3> mVertices; std::vector<Ogre::Vector3> mVertices;
public: public:
Value(Ogre::SubEntity *subent, const Nif::NiMorphData *data) Value(Ogre::Entity *ent, const Nif::NiMorphData *data, size_t controllerIndex)
: mSubEntity(subent) : mEntity(ent)
, mMorphs(data->mMorphs) , mMorphs(data->mMorphs)
, mControllerIndex(controllerIndex)
{ {
mValues.resize(mMorphs.size()-1, 0.f);
} }
virtual Ogre::Real getValue() const virtual Ogre::Real getValue() const
@ -543,21 +558,7 @@ public:
{ {
if (mMorphs.size() <= 1) if (mMorphs.size() <= 1)
return; return;
int i = 1;
#if OGRE_DOUBLE_PRECISION
#error "This code needs to be rewritten for double precision mode"
#endif
Ogre::VertexData* data = mSubEntity->_getSoftwareVertexAnimVertexData();
const Ogre::VertexElement* posElem =
data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
Ogre::HardwareVertexBufferSharedPtr vbuf =
data->vertexBufferBinding->getBuffer(posElem->getSource());
bool needToUpdate = false;
int i=0;
for (std::vector<Nif::NiMorphData::MorphData>::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i) for (std::vector<Nif::NiMorphData::MorphData>::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i)
{ {
float val = 0; float val = 0;
@ -565,37 +566,13 @@ public:
val = interpKey(it->mData.mKeys, time); val = interpKey(it->mData.mKeys, time);
val = std::max(0.f, std::min(1.f, val)); val = std::max(0.f, std::min(1.f, val));
if (val != mValues[i]) Ogre::String animationID = Ogre::StringConverter::toString(mControllerIndex)
needToUpdate = true; + "_" + Ogre::StringConverter::toString(i);
mValues[i] = val;
}
if (!needToUpdate)
return;
// The first morph key always contains the original positions
mVertices = mMorphs[0].mVertices;
i = 0;
for (std::vector<Nif::NiMorphData::MorphData>::iterator it = mMorphs.begin()+1; it != mMorphs.end(); ++it,++i)
{
float val = mValues[i];
if (it->mVertices.size() != mMorphs[0].mVertices.size())
continue;
if (val != 0) Ogre::AnimationState* state = mEntity->getAnimationState(animationID);
{ state->setEnabled(val > 0);
for (unsigned int v=0; v<mVertices.size(); ++v) state->setWeight(val);
mVertices[v] += it->mVertices[v] * val;
}
} }
if (mVertices.size() * sizeof(float)*3 != vbuf->getSizeInBytes())
return;
vbuf->writeData(0, vbuf->getSizeInBytes(), &mVertices[0]);
mSubEntity->_markBuffersUsedForAnimation();
} }
}; };
@ -614,13 +591,6 @@ class NIFObjectLoader
std::cerr << "NIFObjectLoader: Warn: " << msg << std::endl; std::cerr << "NIFObjectLoader: Warn: " << msg << std::endl;
} }
static void fail(const std::string &msg)
{
std::cerr << "NIFObjectLoader: Fail: "<< msg << std::endl;
abort();
}
static void createEntity(const std::string &name, const std::string &group, static void createEntity(const std::string &name, const std::string &group,
Ogre::SceneManager *sceneMgr, ObjectScenePtr scene, Ogre::SceneManager *sceneMgr, ObjectScenePtr scene,
const Nif::Node *node, int flags, int animflags) const Nif::Node *node, int flags, int animflags)
@ -648,6 +618,7 @@ class NIFObjectLoader
{ {
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex); int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, shape->recIndex);
Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
trgtbone->getUserObjectBindings().setUserAny(Ogre::Any(static_cast<Ogre::MovableObject*>(entity)));
scene->mSkelBase->attachObjectToBone(trgtbone->getName(), entity); scene->mSkelBase->attachObjectToBone(trgtbone->getName(), entity);
} }
} }
@ -677,7 +648,8 @@ class NIFObjectLoader
Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ? Ogre::ControllerValueRealPtr srcval((animflags&Nif::NiNode::AnimFlag_AutoPlay) ?
Ogre::ControllerManager::getSingleton().getFrameTimeSource() : Ogre::ControllerManager::getSingleton().getFrameTimeSource() :
Ogre::ControllerValueRealPtr()); Ogre::ControllerValueRealPtr());
Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(entity->getSubEntity(0), geom->data.getPtr())); Ogre::ControllerValueRealPtr dstval(OGRE_NEW GeomMorpherController::Value(
entity, geom->data.getPtr(), geom->recIndex));
GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay)); GeomMorpherController::Function* function = OGRE_NEW GeomMorpherController::Function(geom, (animflags&Nif::NiNode::AnimFlag_AutoPlay));
scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength); scene->mMaxControllerLength = std::max(function->mStopTime, scene->mMaxControllerLength);
@ -773,8 +745,6 @@ class NIFObjectLoader
emitter->setParameter("vertical_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->verticalAngle).valueDegrees())); emitter->setParameter("vertical_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->verticalAngle).valueDegrees()));
emitter->setParameter("horizontal_direction", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalDir).valueDegrees())); emitter->setParameter("horizontal_direction", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalDir).valueDegrees()));
emitter->setParameter("horizontal_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalAngle).valueDegrees())); emitter->setParameter("horizontal_angle", Ogre::StringConverter::toString(Ogre::Radian(partctrl->horizontalAngle).valueDegrees()));
emitter->setParameter("skelbase", skelBaseName);
emitter->setParameter("bone", bone->getName());
Nif::ExtraPtr e = partctrl->extra; Nif::ExtraPtr e = partctrl->extra;
while(!e.empty()) while(!e.empty())
@ -867,7 +837,9 @@ class NIFObjectLoader
partsys->setParticleQuota(particledata->numParticles); partsys->setParticleQuota(particledata->numParticles);
partsys->setKeepParticlesInLocalSpace(partflags & (Nif::NiNode::ParticleFlag_LocalSpace)); partsys->setKeepParticlesInLocalSpace(partflags & (Nif::NiNode::ParticleFlag_LocalSpace));
sceneNode->attachObject(partsys); int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partnode->recIndex);
Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
scene->mSkelBase->attachObjectToBone(trgtbone->getName(), partsys);
Nif::ControllerPtr ctrl = partnode->controller; Nif::ControllerPtr ctrl = partnode->controller;
while(!ctrl.empty()) while(!ctrl.empty())
@ -880,6 +852,9 @@ class NIFObjectLoader
{ {
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex); int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, partctrl->emitter->recIndex);
Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid); Ogre::Bone *trgtbone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
// Set the emitter bone as user data on the particle system
// so the emitters/affectors can access it easily.
partsys->getUserObjectBindings().setUserAny(Ogre::Any(trgtbone));
createParticleEmitterAffectors(partsys, partctrl, trgtbone, scene->mSkelBase->getName()); createParticleEmitterAffectors(partsys, partctrl, trgtbone, scene->mSkelBase->getName());
} }
@ -1007,6 +982,17 @@ class NIFObjectLoader
else else
flags |= node->flags; flags |= node->flags;
if (node->recType == Nif::RC_NiBillboardNode)
{
// TODO: figure out what the flags mean.
// NifSkope has names for them, but doesn't implement them.
// Change mBillboardNodes to map <Bone, billboard type>
int trgtid = NIFSkeletonLoader::lookupOgreBoneHandle(name, node->recIndex);
Ogre::Bone* bone = scene->mSkelBase->getSkeleton()->getBone(trgtid);
bone->setManuallyControlled(true);
scene->mBillboardNodes.push_back(bone);
}
Nif::ExtraPtr e = node->extra; Nif::ExtraPtr e = node->extra;
while(!e.empty()) while(!e.empty())
{ {
@ -1243,17 +1229,6 @@ ObjectScenePtr Loader::createObjects(Ogre::Entity *parent, const std::string &bo
} }
} }
for(size_t i = 0;i < scene->mParticles.size();i++)
{
Ogre::ParticleSystem *partsys = scene->mParticles[i];
if(partsys->isAttached())
partsys->detachFromParent();
Ogre::TagPoint *tag = scene->mSkelBase->attachObjectToBone(
scene->mSkelBase->getSkeleton()->getRootBone()->getName(), partsys);
tag->setScale(scale);
}
return scene; return scene;
} }

@ -45,6 +45,8 @@ class MaterialControllerManager
{ {
public: public:
~MaterialControllerManager(); ~MaterialControllerManager();
/// @attention if \a movable is an Entity, it needs to have *one* SubEntity
Ogre::MaterialPtr getWritableMaterial (Ogre::MovableObject* movable); Ogre::MaterialPtr getWritableMaterial (Ogre::MovableObject* movable);
private: private:
@ -59,6 +61,9 @@ struct ObjectScene {
std::vector<Ogre::ParticleSystem*> mParticles; std::vector<Ogre::ParticleSystem*> mParticles;
std::vector<Ogre::Light*> mLights; std::vector<Ogre::Light*> mLights;
// Nodes that should always face the camera when rendering
std::vector<Ogre::Node*> mBillboardNodes;
Ogre::SceneManager* mSceneMgr; Ogre::SceneManager* mSceneMgr;
// The maximum length on any of the controllers. For animations with controllers, but no text keys, consider this the animation length. // The maximum length on any of the controllers. For animations with controllers, but no text keys, consider this the animation length.
@ -74,6 +79,9 @@ struct ObjectScene {
{ } { }
~ObjectScene(); ~ObjectScene();
// Rotate nodes in mBillboardNodes so they face the given camera
void rotateBillboardNodes(Ogre::Camera* camera);
}; };
typedef Ogre::SharedPtr<ObjectScene> ObjectScenePtr; typedef Ogre::SharedPtr<ObjectScene> ObjectScenePtr;

@ -16,46 +16,11 @@
class NifEmitter : public Ogre::ParticleEmitter class NifEmitter : public Ogre::ParticleEmitter
{ {
public: public:
std::string mSkelBaseName; Ogre::Bone* mEmitterBone;
Ogre::Bone* mBone; Ogre::Bone* mParticleBone;
Ogre::ParticleSystem* getPartSys() { return mParent; } Ogre::ParticleSystem* getPartSys() { return mParent; }
class CmdSkelBase : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
assert(false && "Unimplemented");
return "";
}
void doSet(void *target, const Ogre::String &val)
{
NifEmitter* emitter = static_cast<NifEmitter*>(target);
emitter->mSkelBaseName = val;
}
};
class CmdBone : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
assert(false && "Unimplemented");
return "";
}
void doSet(void *target, const Ogre::String &val)
{
NifEmitter* emitter = static_cast<NifEmitter*>(target);
assert(!emitter->mSkelBaseName.empty() && "Base entity needs to be set first");
Ogre::ParticleSystem* partsys = emitter->getPartSys();
Ogre::Entity* ent = partsys->getParentSceneNode()->getCreator()->getEntity(emitter->mSkelBaseName);
Ogre::Bone* bone = ent->getSkeleton()->getBone(val);
assert(bone);
emitter->mBone = bone;
}
};
/** Command object for the emitter width (see Ogre::ParamCommand).*/ /** Command object for the emitter width (see Ogre::ParamCommand).*/
class CmdWidth : public Ogre::ParamCommand class CmdWidth : public Ogre::ParamCommand
{ {
@ -165,8 +130,10 @@ public:
NifEmitter(Ogre::ParticleSystem *psys) NifEmitter(Ogre::ParticleSystem *psys)
: Ogre::ParticleEmitter(psys) : Ogre::ParticleEmitter(psys)
, mBone(NULL)
{ {
mEmitterBone = Ogre::any_cast<Ogre::Bone*>(psys->getUserObjectBindings().getUserAny());
Ogre::TagPoint* tag = static_cast<Ogre::TagPoint*>(mParent->getParentNode());
mParticleBone = static_cast<Ogre::Bone*>(tag->getParent());
initDefaults("Nif"); initDefaults("Nif");
} }
@ -180,7 +147,6 @@ public:
/** See Ogre::ParticleEmitter. */ /** See Ogre::ParticleEmitter. */
void _initParticle(Ogre::Particle *particle) void _initParticle(Ogre::Particle *particle)
{ {
assert (mBone && "No node set");
Ogre::Vector3 xOff, yOff, zOff; Ogre::Vector3 xOff, yOff, zOff;
// Call superclass // Call superclass
@ -189,23 +155,41 @@ public:
xOff = Ogre::Math::SymmetricRandom() * mXRange; xOff = Ogre::Math::SymmetricRandom() * mXRange;
yOff = Ogre::Math::SymmetricRandom() * mYRange; yOff = Ogre::Math::SymmetricRandom() * mYRange;
zOff = Ogre::Math::SymmetricRandom() * mZRange; zOff = Ogre::Math::SymmetricRandom() * mZRange;
particle->position = mBone->_getDerivedPosition() + xOff + yOff + zOff; #if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
Ogre::Vector3& position = particle->mPosition;
Ogre::Vector3& direction = particle->mDirection;
Ogre::ColourValue& colour = particle->mColour;
Ogre::Real& totalTimeToLive = particle->mTotalTimeToLive;
Ogre::Real& timeToLive = particle->mTimeToLive;
#else
Ogre::Vector3& position = particle->position;
Ogre::Vector3& direction = particle->direction;
Ogre::ColourValue& colour = particle->colour;
Ogre::Real& totalTimeToLive = particle->totalTimeToLive;
Ogre::Real& timeToLive = particle->timeToLive;
#endif
position = xOff + yOff + zOff +
mParticleBone->_getDerivedOrientation().Inverse() * (mEmitterBone->_getDerivedPosition()
- mParticleBone->_getDerivedPosition());
// Generate complex data by reference // Generate complex data by reference
genEmissionColour(particle->colour); genEmissionColour(colour);
// NOTE: We do not use mDirection/mAngle for the initial direction. // NOTE: We do not use mDirection/mAngle for the initial direction.
Ogre::Radian hdir = mHorizontalDir + mHorizontalAngle*Ogre::Math::SymmetricRandom(); Ogre::Radian hdir = mHorizontalDir + mHorizontalAngle*Ogre::Math::SymmetricRandom();
Ogre::Radian vdir = mVerticalDir + mVerticalAngle*Ogre::Math::SymmetricRandom(); Ogre::Radian vdir = mVerticalDir + mVerticalAngle*Ogre::Math::SymmetricRandom();
particle->direction = (mBone->_getDerivedOrientation() * Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) * direction = (mParticleBone->_getDerivedOrientation().Inverse()
* mEmitterBone->_getDerivedOrientation() *
Ogre::Quaternion(hdir, Ogre::Vector3::UNIT_Z) *
Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) * Ogre::Quaternion(vdir, Ogre::Vector3::UNIT_X)) *
Ogre::Vector3::UNIT_Z; Ogre::Vector3::UNIT_Z;
genEmissionVelocity(particle->direction); genEmissionVelocity(direction);
// Generate simpler data // Generate simpler data
particle->timeToLive = particle->totalTimeToLive = genEmissionTTL(); timeToLive = totalTimeToLive = genEmissionTTL();
} }
/** Overloaded to update the trans. matrix */ /** Overloaded to update the trans. matrix */
@ -361,16 +345,6 @@ protected:
Ogre::PT_REAL), Ogre::PT_REAL),
&msHorizontalAngleCmd); &msHorizontalAngleCmd);
dict->addParameter(Ogre::ParameterDef("bone",
"The bone where the particles should be spawned",
Ogre::PT_STRING),
&msBoneCmd);
dict->addParameter(Ogre::ParameterDef("skelbase",
"The name of the entity containing the bone (see 'bone' parameter)",
Ogre::PT_STRING),
&msSkelBaseCmd);
return true; return true;
} }
return false; return false;
@ -384,8 +358,6 @@ protected:
static CmdVerticalAngle msVerticalAngleCmd; static CmdVerticalAngle msVerticalAngleCmd;
static CmdHorizontalDir msHorizontalDirCmd; static CmdHorizontalDir msHorizontalDirCmd;
static CmdHorizontalAngle msHorizontalAngleCmd; static CmdHorizontalAngle msHorizontalAngleCmd;
static CmdBone msBoneCmd;
static CmdSkelBase msSkelBaseCmd;
}; };
NifEmitter::CmdWidth NifEmitter::msWidthCmd; NifEmitter::CmdWidth NifEmitter::msWidthCmd;
NifEmitter::CmdHeight NifEmitter::msHeightCmd; NifEmitter::CmdHeight NifEmitter::msHeightCmd;
@ -394,8 +366,6 @@ NifEmitter::CmdVerticalDir NifEmitter::msVerticalDirCmd;
NifEmitter::CmdVerticalAngle NifEmitter::msVerticalAngleCmd; NifEmitter::CmdVerticalAngle NifEmitter::msVerticalAngleCmd;
NifEmitter::CmdHorizontalDir NifEmitter::msHorizontalDirCmd; NifEmitter::CmdHorizontalDir NifEmitter::msHorizontalDirCmd;
NifEmitter::CmdHorizontalAngle NifEmitter::msHorizontalAngleCmd; NifEmitter::CmdHorizontalAngle NifEmitter::msHorizontalAngleCmd;
NifEmitter::CmdBone NifEmitter::msBoneCmd;
NifEmitter::CmdSkelBase NifEmitter::msSkelBaseCmd;
Ogre::ParticleEmitter* NifEmitterFactory::createEmitter(Ogre::ParticleSystem *psys) Ogre::ParticleEmitter* NifEmitterFactory::createEmitter(Ogre::ParticleSystem *psys)
{ {
@ -466,9 +436,13 @@ public:
/** See Ogre::ParticleAffector. */ /** See Ogre::ParticleAffector. */
void _initParticle(Ogre::Particle *particle) void _initParticle(Ogre::Particle *particle)
{ {
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
const Ogre::Real life_time = particle->mTotalTimeToLive;
Ogre::Real particle_time = particle->mTimeToLive;
#else
const Ogre::Real life_time = particle->totalTimeToLive; const Ogre::Real life_time = particle->totalTimeToLive;
Ogre::Real particle_time = particle->timeToLive; Ogre::Real particle_time = particle->timeToLive;
#endif
Ogre::Real width = mParent->getDefaultWidth(); Ogre::Real width = mParent->getDefaultWidth();
Ogre::Real height = mParent->getDefaultHeight(); Ogre::Real height = mParent->getDefaultHeight();
if(life_time-particle_time < mGrowTime) if(life_time-particle_time < mGrowTime)
@ -493,9 +467,13 @@ public:
while (!pi.end()) while (!pi.end())
{ {
Ogre::Particle *p = pi.getNext(); Ogre::Particle *p = pi.getNext();
const Ogre::Real life_time = p->totalTimeToLive; #if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
Ogre::Real particle_time = p->timeToLive; const Ogre::Real life_time = p->mTotalTimeToLive;
Ogre::Real particle_time = p->mTimeToLive;
#else
const Ogre::Real life_time = p->totalTimeToLive;
Ogre::Real particle_time = p->timeToLive;
#endif
Ogre::Real width = mParent->getDefaultWidth(); Ogre::Real width = mParent->getDefaultWidth();
Ogre::Real height = mParent->getDefaultHeight(); Ogre::Real height = mParent->getDefaultHeight();
if(life_time-particle_time < mGrowTime) if(life_time-particle_time < mGrowTime)
@ -554,47 +532,11 @@ class GravityAffector : public Ogre::ParticleAffector
}; };
public: public:
std::string mSkelBaseName; Ogre::Bone* mEmitterBone;
Ogre::Bone* mBone; Ogre::Bone* mParticleBone;
Ogre::ParticleSystem* getPartSys() { return mParent; } Ogre::ParticleSystem* getPartSys() { return mParent; }
class CmdSkelBase : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
assert(false && "Unimplemented");
return "";
}
void doSet(void *target, const Ogre::String &val)
{
GravityAffector* affector = static_cast<GravityAffector*>(target);
affector->mSkelBaseName = val;
}
};
class CmdBone : public Ogre::ParamCommand
{
public:
Ogre::String doGet(const void *target) const
{
assert(false && "Unimplemented");
return "";
}
void doSet(void *target, const Ogre::String &val)
{
GravityAffector* affector = static_cast<GravityAffector*>(target);
assert(!affector->mSkelBaseName.empty() && "Base entity needs to be set first");
Ogre::ParticleSystem* partsys = affector->getPartSys();
Ogre::Entity* ent = partsys->getParentSceneNode()->getCreator()->getEntity(affector->mSkelBaseName);
Ogre::Bone* bone = ent->getSkeleton()->getBone(val);
assert(bone);
affector->mBone = bone;
}
};
/** Command object for force (see Ogre::ParamCommand).*/ /** Command object for force (see Ogre::ParamCommand).*/
class CmdForce : public Ogre::ParamCommand class CmdForce : public Ogre::ParamCommand
{ {
@ -688,8 +630,11 @@ public:
, mForceType(Type_Wind) , mForceType(Type_Wind)
, mPosition(0.0f) , mPosition(0.0f)
, mDirection(0.0f) , mDirection(0.0f)
, mBone(NULL)
{ {
mEmitterBone = Ogre::any_cast<Ogre::Bone*>(psys->getUserObjectBindings().getUserAny());
Ogre::TagPoint* tag = static_cast<Ogre::TagPoint*>(mParent->getParentNode());
mParticleBone = static_cast<Ogre::Bone*>(tag->getParent());
mType = "Gravity"; mType = "Gravity";
// Init parameters // Init parameters
@ -710,16 +655,6 @@ public:
dict->addParameter(Ogre::ParameterDef(force_type_title, force_type_descr, Ogre::PT_STRING), &msForceTypeCmd); dict->addParameter(Ogre::ParameterDef(force_type_title, force_type_descr, Ogre::PT_STRING), &msForceTypeCmd);
dict->addParameter(Ogre::ParameterDef(direction_title, direction_descr, Ogre::PT_VECTOR3), &msDirectionCmd); dict->addParameter(Ogre::ParameterDef(direction_title, direction_descr, Ogre::PT_VECTOR3), &msDirectionCmd);
dict->addParameter(Ogre::ParameterDef(position_title, position_descr, Ogre::PT_VECTOR3), &msPositionCmd); dict->addParameter(Ogre::ParameterDef(position_title, position_descr, Ogre::PT_VECTOR3), &msPositionCmd);
dict->addParameter(Ogre::ParameterDef("bone",
"The bone where the particles should be spawned",
Ogre::PT_STRING),
&msBoneCmd);
dict->addParameter(Ogre::ParameterDef("skelbase",
"The name of the entity containing the bone (see 'bone' parameter)",
Ogre::PT_STRING),
&msSkelBaseCmd);
} }
} }
@ -761,18 +696,20 @@ public:
static CmdForceType msForceTypeCmd; static CmdForceType msForceTypeCmd;
static CmdDirection msDirectionCmd; static CmdDirection msDirectionCmd;
static CmdPosition msPositionCmd; static CmdPosition msPositionCmd;
static CmdBone msBoneCmd;
static CmdSkelBase msSkelBaseCmd;
protected: protected:
void applyWindForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed) void applyWindForce(Ogre::ParticleSystem *psys, Ogre::Real timeElapsed)
{ {
const Ogre::Vector3 vec = mBone->_getDerivedOrientation() * mDirection * mForce * timeElapsed; const Ogre::Vector3 vec = mDirection * mForce * timeElapsed;
Ogre::ParticleIterator pi = psys->_getIterator(); Ogre::ParticleIterator pi = psys->_getIterator();
while (!pi.end()) while (!pi.end())
{ {
Ogre::Particle *p = pi.getNext(); Ogre::Particle *p = pi.getNext();
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
p->mDirection += vec;
#else
p->direction += vec; p->direction += vec;
#endif
} }
} }
@ -783,9 +720,18 @@ protected:
while (!pi.end()) while (!pi.end())
{ {
Ogre::Particle *p = pi.getNext(); Ogre::Particle *p = pi.getNext();
const Ogre::Vector3 vec = ( #if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
(mBone->_getDerivedOrientation() * mPosition + mBone->_getDerivedPosition()) - p->position).normalisedCopy() * force; Ogre::Vector3 position = p->mPosition;
#else
Ogre::Vector3 position = p->position;
#endif
Ogre::Vector3 vec = (mPosition - position).normalisedCopy() * force;
#if OGRE_VERSION >= (1 << 16 | 10 << 8 | 0)
p->mDirection += vec;
#else
p->direction += vec; p->direction += vec;
#endif
} }
} }
@ -801,8 +747,6 @@ GravityAffector::CmdForce GravityAffector::msForceCmd;
GravityAffector::CmdForceType GravityAffector::msForceTypeCmd; GravityAffector::CmdForceType GravityAffector::msForceTypeCmd;
GravityAffector::CmdDirection GravityAffector::msDirectionCmd; GravityAffector::CmdDirection GravityAffector::msDirectionCmd;
GravityAffector::CmdPosition GravityAffector::msPositionCmd; GravityAffector::CmdPosition GravityAffector::msPositionCmd;
GravityAffector::CmdBone GravityAffector::msBoneCmd;
GravityAffector::CmdSkelBase GravityAffector::msSkelBaseCmd;
Ogre::ParticleAffector *GravityAffectorFactory::createAffector(Ogre::ParticleSystem *psys) Ogre::ParticleAffector *GravityAffectorFactory::createAffector(Ogre::ParticleSystem *psys)
{ {

@ -30,6 +30,7 @@ void NIFSkeletonLoader::buildBones(Ogre::Skeleton *skel, const Nif::Node *node,
node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */ node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */
node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */ node->recType == Nif::RC_NiTriShape || /* Handled in the mesh loader */
node->recType == Nif::RC_NiBSAnimationNode || /* Handled in the object loader */ node->recType == Nif::RC_NiBSAnimationNode || /* Handled in the object loader */
node->recType == Nif::RC_NiBillboardNode || /* Handled in the object loader */
node->recType == Nif::RC_NiBSParticleNode || node->recType == Nif::RC_NiBSParticleNode ||
node->recType == Nif::RC_NiCamera || node->recType == Nif::RC_NiCamera ||
node->recType == Nif::RC_NiAutoNormalParticles || node->recType == Nif::RC_NiAutoNormalParticles ||

@ -259,9 +259,8 @@ namespace sh
Platform* mPlatform; Platform* mPlatform;
MaterialInstance* findInstance (const std::string& name); MaterialInstance* findInstance (const std::string& name);
public:
MaterialInstance* searchInstance (const std::string& name);
private: private:
MaterialInstance* searchInstance (const std::string& name);
/// @return was anything removed? /// @return was anything removed?
bool removeCache (const std::string& pattern); bool removeCache (const std::string& pattern);

@ -8,10 +8,15 @@ material openmw_objects_base
diffuseMap black.png diffuseMap black.png
normalMap normalMap
emissiveMap emissiveMap
darkMap
use_emissive_map false use_emissive_map false
use_detail_map false use_detail_map false
use_diffuse_map false
use_dark_map false
emissiveMapUVSet 0 emissiveMapUVSet 0
detailMapUVSet 0 detailMapUVSet 0
diffuseMapUVSet 0
darkMapUVSet 0
use_parallax false use_parallax false
scene_blend default scene_blend default
@ -34,8 +39,12 @@ material openmw_objects_base
normalMap $normalMap normalMap $normalMap
emissiveMapUVSet $emissiveMapUVSet emissiveMapUVSet $emissiveMapUVSet
detailMapUVSet $detailMapUVSet detailMapUVSet $detailMapUVSet
diffuseMapUVSet $diffuseMapUVSet
darkMapUVSet $darkMapUVSet
emissiveMap $emissiveMap emissiveMap $emissiveMap
detailMap $detailMap detailMap $detailMap
diffuseMap $diffuseMap
darkMap $darkMap
env_map $env_map env_map $env_map
env_map_color $env_map_color env_map_color $env_map_color
use_parallax $use_parallax use_parallax $use_parallax
@ -55,8 +64,8 @@ material openmw_objects_base
texture_unit diffuseMap texture_unit diffuseMap
{ {
direct_texture $diffuseMap direct_texture $diffuseMap
create_in_ffp true create_in_ffp $use_diffuse_map
tex_coord_set $emissiveMapUVSet tex_coord_set $diffuseMapUVSet
} }
texture_unit normalMap texture_unit normalMap
@ -66,12 +75,13 @@ material openmw_objects_base
num_mipmaps 4 num_mipmaps 4
} }
texture_unit emissiveMap texture_unit darkMap
{ {
create_in_ffp $use_emissive_map create_in_ffp $use_dark_map
colour_op add colour_op_ex modulate src_current src_texture
direct_texture $emissiveMap alpha_op_ex modulate src_current src_texture
tex_coord_set $emissiveMapUVSet direct_texture $darkMap
tex_coord_set $darkMapUVSet
} }
texture_unit detailMap texture_unit detailMap
@ -82,6 +92,14 @@ material openmw_objects_base
tex_coord_set $detailMapUVSet tex_coord_set $detailMapUVSet
} }
texture_unit emissiveMap
{
create_in_ffp $use_emissive_map
colour_op add
direct_texture $emissiveMap
tex_coord_set $emissiveMapUVSet
}
texture_unit envMap texture_unit envMap
{ {
create_in_ffp $env_map create_in_ffp $env_map

@ -17,13 +17,15 @@
#define NORMAL_MAP @shPropertyHasValue(normalMap) #define NORMAL_MAP @shPropertyHasValue(normalMap)
#define EMISSIVE_MAP @shPropertyHasValue(emissiveMap) #define EMISSIVE_MAP @shPropertyHasValue(emissiveMap)
#define DETAIL_MAP @shPropertyHasValue(detailMap) #define DETAIL_MAP @shPropertyHasValue(detailMap)
#define DIFFUSE_MAP @shPropertyHasValue(diffuseMap)
#define DARK_MAP @shPropertyHasValue(darkMap)
#define PARALLAX @shPropertyBool(use_parallax) #define PARALLAX @shPropertyBool(use_parallax)
#define PARALLAX_SCALE 0.04 #define PARALLAX_SCALE 0.04
#define PARALLAX_BIAS -0.02 #define PARALLAX_BIAS -0.02
// right now we support 2 UV sets max. implementing them is tedious, and we're probably not going to need more // right now we support 2 UV sets max. implementing them is tedious, and we're probably not going to need more
#define SECOND_UV_SET (@shPropertyString(emissiveMapUVSet) || @shPropertyString(detailMapUVSet)) #define SECOND_UV_SET (@shPropertyString(emissiveMapUVSet) || @shPropertyString(detailMapUVSet) || @shPropertyString(diffuseMapUVSet) || @shPropertyString(darkMapUVSet))
// if normal mapping is enabled, we force pixel lighting // if normal mapping is enabled, we force pixel lighting
#define VERTEX_LIGHTING (!@shPropertyHasValue(normalMap)) #define VERTEX_LIGHTING (!@shPropertyHasValue(normalMap))
@ -246,20 +248,26 @@
#endif #endif
SH_BEGIN_PROGRAM SH_BEGIN_PROGRAM
#if DIFFUSE_MAP
shSampler2D(diffuseMap) shSampler2D(diffuseMap)
#endif
#if NORMAL_MAP #if NORMAL_MAP
shSampler2D(normalMap) shSampler2D(normalMap)
#endif #endif
#if EMISSIVE_MAP #if DARK_MAP
shSampler2D(emissiveMap) shSampler2D(darkMap)
#endif #endif
#if DETAIL_MAP #if DETAIL_MAP
shSampler2D(detailMap) shSampler2D(detailMap)
#endif #endif
#if EMISSIVE_MAP
shSampler2D(emissiveMap)
#endif
#if ENV_MAP #if ENV_MAP
shSampler2D(envMap) shSampler2D(envMap)
shUniform(float3, env_map_color) @shUniformProperty3f(env_map_color, env_map_color) shUniform(float3, env_map_color) @shUniformProperty3f(env_map_color, env_map_color)
@ -376,17 +384,33 @@
newUV += (TSeyeDir.xyxy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS )).xyxy; newUV += (TSeyeDir.xyxy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS )).xyxy;
#endif #endif
#if DIFFUSE_MAP
#if @shPropertyString(diffuseMapUVSet)
float4 diffuse = shSample(diffuseMap, newUV.zw);
#else
float4 diffuse = shSample(diffuseMap, newUV.xy); float4 diffuse = shSample(diffuseMap, newUV.xy);
shOutputColour(0) = diffuse; #endif
#else
float4 diffuse = float4(1,1,1,1);
#endif
#if DETAIL_MAP #if DETAIL_MAP
#if @shPropertyString(detailMapUVSet) #if @shPropertyString(detailMapUVSet)
shOutputColour(0) *= shSample(detailMap, newUV.zw)*2; diffuse *= shSample(detailMap, newUV.zw)*2;
#else
diffuse *= shSample(detailMap, newUV.xy)*2;
#endif
#endif
#if DARK_MAP
#if @shPropertyString(darkMapUVSet)
diffuse *= shSample(darkMap, newUV.zw);
#else #else
shOutputColour(0) *= shSample(detailMap, newUV.xy)*2; diffuse *= shSample(darkMap, newUV.xy);
#endif #endif
#endif #endif
shOutputColour(0) = diffuse;
#if !VERTEX_LIGHTING #if !VERTEX_LIGHTING
float3 viewPos = shMatrixMult(worldView, float4(objSpacePositionPassthrough.xyz,1)).xyz; float3 viewPos = shMatrixMult(worldView, float4(objSpacePositionPassthrough.xyz,1)).xyz;

@ -345,6 +345,7 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5;
#endif #endif
@shForeach(@shPropertyString(num_layers)) @shForeach(@shPropertyString(num_layers))
thisLayerUV = layerUV;
#if @shPropertyBool(use_normal_map_@shIterator) #if @shPropertyBool(use_normal_map_@shIterator)
normalTex = shSample(normalMap@shIterator, thisLayerUV); normalTex = shSample(normalMap@shIterator, thisLayerUV);
#if @shIterator == 0 && IS_FIRST_PASS #if @shIterator == 0 && IS_FIRST_PASS
@ -354,7 +355,6 @@ float2 blendUV = (UV - 0.5) * (16.0 / (16.0+1.0)) + 0.5;
#endif #endif
#endif #endif
thisLayerUV = layerUV;
#if @shPropertyBool(use_parallax_@shIterator) #if @shPropertyBool(use_parallax_@shIterator)
thisLayerUV += TSeyeDir.xy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS ); thisLayerUV += TSeyeDir.xy * ( normalTex.a * PARALLAX_SCALE + PARALLAX_BIAS );
#endif #endif

@ -11,6 +11,8 @@
#include <extern/sdl4ogre/sdlwindowhelper.hpp> #include <extern/sdl4ogre/sdlwindowhelper.hpp>
#include <components/ogreinit/ogreinit.hpp>
#include <cassert> #include <cassert>
#include <stdexcept> #include <stdexcept>
@ -23,6 +25,12 @@ void OgreRenderer::cleanup()
delete mFader; delete mFader;
mFader = NULL; mFader = NULL;
Ogre::Root::getSingleton().destroyRenderTarget(mWindow);
mWindow = NULL;
delete mOgreInit;
mOgreInit = NULL;
// If we don't do this, the desktop resolution is not restored on exit // If we don't do this, the desktop resolution is not restored on exit
SDL_SetWindowFullscreen(mSDLWindow, 0); SDL_SetWindowFullscreen(mSDLWindow, 0);
@ -50,7 +58,8 @@ void OgreRenderer::configure(const std::string &logPath,
const std::string& rttMode const std::string& rttMode
) )
{ {
mRoot = mOgreInit.init(logPath + "/ogre.log"); mOgreInit = new OgreInit::OgreInit();
mRoot = mOgreInit->init(logPath + "/ogre.log");
RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem); RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem);
if (rs == 0) if (rs == 0)

@ -9,8 +9,6 @@
#include <OgreTexture.h> #include <OgreTexture.h>
#include <components/ogreinit/ogreinit.hpp>
struct SDL_Window; struct SDL_Window;
struct SDL_Surface; struct SDL_Surface;
@ -26,6 +24,11 @@ namespace Ogre
class ParticleAffectorFactory; class ParticleAffectorFactory;
} }
namespace OgreInit
{
class OgreInit;
}
namespace OEngine namespace OEngine
{ {
namespace Render namespace Render
@ -57,7 +60,7 @@ namespace OEngine
Ogre::Camera *mCamera; Ogre::Camera *mCamera;
Ogre::Viewport *mView; Ogre::Viewport *mView;
OgreInit::OgreInit mOgreInit; OgreInit::OgreInit* mOgreInit;
Fader* mFader; Fader* mFader;

@ -36,49 +36,58 @@ https://wiki.openmw.org/index.php?title=Development_Environment_Setup
THE DATA PATH THE DATA PATH
The data path tells OpenMW where to find your Morrowind files. From 0.12.0 on OpenMW should be able to The data path tells OpenMW where to find your Morrowind files. If you run the launcher, OpenMW should be able to
pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly pick up the location of these files on its own, if both Morrowind and OpenMW are installed properly
(installing Morrowind under WINE is considered a proper install). (installing Morrowind under WINE is considered a proper install).
If that does not work for you, please check if you have any leftover openmw.cfg files from versions earlier than 0.12.0. These can interfere with the configuration process, so try to remove then.
If you are running OpenMW without installing it, you still need to manually adjust the data path. Create a text file named openmw.cfg in the location of the binary and enter the following line:
data=path to your data directory
(where you replace "path to your data directory" with the actual location of your data directory)
COMMAND LINE OPTIONS COMMAND LINE OPTIONS
Syntax: openmw <options> Syntax: openmw <options>
Allowed options: Allowed options:
--help print help message --help print help message
--version print version information and quit --version print version information and quit
--data arg (=data) set data directories (later directories have higher priority) --data arg (=data) set data directories (later directories
--data-local arg set local data directory (highest priority) have higher priority)
--resources arg (=resources) set resources directory --data-local arg set local data directory (highest
--start arg (=Beshara) set initial cell priority)
--master arg master file(s) --fallback-archive arg (=fallback-archive)
--plugin arg plugin file(s) set fallback BSA archives (later
--anim-verbose [=arg(=1)] (=0) output animation indices files archives have higher priority)
--debug [=arg(=1)] (=0) debug mode --resources arg (=resources) set resources directory
--nosound [=arg(=1)] (=0) disable all sounds --start arg (=Beshara) set initial cell
--script-verbose [=arg(=1)] (=0) verbose script output --content arg content file(s): esm/esp, or
--script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scripts) at startup omwgame/omwaddon
--script-console [=arg(=1)] (=0) enable console-only script functionality --anim-verbose [=arg(=1)] (=0) output animation indices files
--script-run arg select a file containing a list of console commands that is executed on startup --no-sound [=arg(=1)] (=0) disable all sounds
--new-game [=arg(=1)] (=0) activate char gen/new game mechanics --script-verbose [=arg(=1)] (=0) verbose script output
--fs-strict [=arg(=1)] (=0) strict file system handling (no case folding) --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue
--encoding arg (=win1252) Character encoding used in OpenMW game messages: scripts) at startup
--script-console [=arg(=1)] (=0) enable console-only script
win1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages functionality
--script-run arg select a file containing a list of
win1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages console commands that is executed on
startup
win1252 - Western European (Latin) alphabet, used by default --new-game [=arg(=1)] (=0) activate char gen/new game mechanics
--fs-strict [=arg(=1)] (=0) strict file system handling (no case
--fallback arg fallback values folding)
--encoding arg (=win1252) Character encoding used in OpenMW game
messages:
win1250 - Central and Eastern European
such as Polish, Czech, Slovak,
Hungarian, Slovene, Bosnian, Croatian,
Serbian (Latin script), Romanian and
Albanian languages
win1251 - Cyrillic alphabet such as
Russian, Bulgarian, Serbian Cyrillic
and other languages
win1252 - Western European (Latin)
alphabet, used by default
--fallback arg fallback values
--no-grab Don't grab mouse cursor
--activate-dist arg (=-1) activation distance override
CHANGELOG CHANGELOG

Loading…
Cancel
Save