diff --git a/CMakeLists.txt b/CMakeLists.txt index b5b0884e4e..0a24998d15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,7 @@ set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/fader.cpp ${LIBDIR}/openengine/ogre/imagerotate.cpp ${LIBDIR}/openengine/ogre/atlas.cpp + ${LIBDIR}/openengine/ogre/selectionbuffer.cpp ) set(OENGINE_GUI ${LIBDIR}/openengine/gui/manager.cpp @@ -512,6 +513,7 @@ if (WIN32) 4309 # Variable overflow, trying to store 128 in a signed char for example 4355 # Using 'this' in member initialization list 4701 # Potentially uninitialized local variable used + 4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt ) foreach(d ${WARNINGS_DISABLE}) @@ -523,6 +525,7 @@ if (WIN32) set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS}) endif (BUILD_LAUNCHER) set_target_properties(openmw PROPERTIES COMPILE_FLAGS ${WARNINGS}) + set_target_properties(esmtool PROPERTIES COMPILE_FLAGS ${WARNINGS}) endif(MSVC) # Same for MinGW diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 64816fae56..d32a3b045c 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -189,73 +189,8 @@ void DataFilesPage::setupConfig() } -bool DataFilesPage::setupDataFiles() +void DataFilesPage::addDataFiles(Files::Collections &fileCollections, const QString &encoding) { - // We use the Configuration Manager to retrieve the configuration values - boost::program_options::variables_map variables; - boost::program_options::options_description desc; - - desc.add_options() - ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) - ("data-local", boost::program_options::value()->default_value("")) - ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) - ("encoding", boost::program_options::value()->default_value("win1252")); - - mCfgMgr.readConfiguration(variables, desc); - - // Put the paths in a boost::filesystem vector to use with Files::Collections - mDataDirs = Files::PathContainer(variables["data"].as()); - - std::string local = variables["data-local"].as(); - if (!local.empty()) { - mDataLocal.push_back(Files::PathContainer::value_type(local)); - } - - if (mDataDirs.size()>1) - mDataDirs.resize (1); - - mCfgMgr.processPaths(mDataDirs); - - while (mDataDirs.empty()) { - QMessageBox msgBox; - msgBox.setWindowTitle("Error detecting Morrowind installation"); - msgBox.setIcon(QMessageBox::Warning); - msgBox.setStandardButtons(QMessageBox::Cancel); - msgBox.setText(tr("
Could not find the Data Files location

\ - The directory containing the data files was not found.

\ - Press \"Browse...\" to specify the location manually.
")); - - QAbstractButton *dirSelectButton = - msgBox.addButton(tr("B&rowse..."), QMessageBox::ActionRole); - - msgBox.exec(); - - if (msgBox.clickedButton() == dirSelectButton) { - - // Show a custom dir selection dialog which only accepts valid dirs - QString selectedDir = FileDialog::getExistingDirectory( - this, tr("Select Data Files Directory"), - QDir::currentPath(), - QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); - - // Add the user selected data directory - if (!selectedDir.isEmpty()) { - mDataDirs.push_back(Files::PathContainer::value_type(selectedDir.toStdString())); - mCfgMgr.processPaths(mDataDirs); - } else { - // Cancel from within the dir selection dialog - return false; - } - - } else { - // Cancel - return false; - } - } - - // Create a file collection for the data dirs - Files::Collections fileCollections(mDataDirs, !variables["fs-strict"].as()); - // First we add all the master files const Files::MultiDirCollection &esm = fileCollections.getCollection(".esm"); unsigned int i = 0; // Row number @@ -283,7 +218,7 @@ bool DataFilesPage::setupDataFiles() ESMReader fileReader; QStringList availableMasters; // Will contain all found masters - fileReader.setEncoding(variables["encoding"].as()); + fileReader.setEncoding(encoding.toStdString()); fileReader.open(iter->second.string()); // First we fill the availableMasters and the mMastersWidget @@ -350,10 +285,86 @@ bool DataFilesPage::setupDataFiles() } catch(std::runtime_error &e) { // An error occurred while reading the .esp + std::cerr << "Error reading .esp: " << e.what() << std::endl; continue; } } + +} + + +bool DataFilesPage::setupDataFiles() +{ + // We use the Configuration Manager to retrieve the configuration values + boost::program_options::variables_map variables; + boost::program_options::options_description desc; + + desc.add_options() + ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) + ("data-local", boost::program_options::value()->default_value("")) + ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) + ("encoding", boost::program_options::value()->default_value("win1252")); + + mCfgMgr.readConfiguration(variables, desc); + + // Put the paths in a boost::filesystem vector to use with Files::Collections + mDataDirs = Files::PathContainer(variables["data"].as()); + + std::string local = variables["data-local"].as(); + if (!local.empty()) { + mDataLocal.push_back(Files::PathContainer::value_type(local)); + } + + mCfgMgr.processPaths(mDataDirs); + mCfgMgr.processPaths(mDataLocal); + + while (mDataDirs.empty()) { + QMessageBox msgBox; + msgBox.setWindowTitle("Error detecting Morrowind installation"); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setStandardButtons(QMessageBox::Cancel); + msgBox.setText(tr("
Could not find the Data Files location

\ + The directory containing the data files was not found.

\ + Press \"Browse...\" to specify the location manually.
")); + + QAbstractButton *dirSelectButton = + msgBox.addButton(tr("B&rowse..."), QMessageBox::ActionRole); + + msgBox.exec(); + + if (msgBox.clickedButton() == dirSelectButton) { + + // Show a custom dir selection dialog which only accepts valid dirs + QString selectedDir = FileDialog::getExistingDirectory( + this, tr("Select Data Files Directory"), + QDir::currentPath(), + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + // Add the user selected data directory + if (!selectedDir.isEmpty()) { + mDataDirs.push_back(Files::PathContainer::value_type(selectedDir.toStdString())); + mCfgMgr.processPaths(mDataDirs); + } else { + // Cancel from within the dir selection dialog + return false; + } + + } else { + // Cancel + return false; + } + } + + // Add the plugins in the data directories + Files::Collections dataCollections(mDataDirs, !variables["fs-strict"].as()); + Files::Collections dataLocalCollections(mDataLocal, !variables["fs-strict"].as()); + + addDataFiles(dataCollections, QString::fromStdString(variables["encoding"].as())); + addDataFiles(dataLocalCollections, QString::fromStdString(variables["encoding"].as())); + + mDataFilesModel->sort(0); + readConfig(); return true; } @@ -1139,11 +1150,13 @@ void DataFilesPage::writeConfig(QString profile) path = QString::fromStdString(it->string()); path.remove(QChar('\"')); + QDir dir(path); + // Make sure the string is quoted when it contains spaces if (path.contains(" ")) { - gameConfig << "data=\"" << path << "\"" << endl; + gameConfig << "data=\"" << dir.absolutePath() << "\"" << endl; } else { - gameConfig << "data=" << path << endl; + gameConfig << "data=" << dir.absolutePath() << endl; } } @@ -1152,10 +1165,12 @@ void DataFilesPage::writeConfig(QString profile) path = QString::fromStdString(mDataLocal.front().string()); path.remove(QChar('\"')); + QDir dir(path); + if (path.contains(" ")) { - gameConfig << "data-local=\"" << path << "\"" << endl; + gameConfig << "data-local=\"" << dir.absolutePath() << "\"" << endl; } else { - gameConfig << "data-local=" << path << endl; + gameConfig << "data-local=" << dir.absolutePath() << endl; } } diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 5078f64288..83b3186772 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -89,6 +89,7 @@ private: const QStringList checkedPlugins(); const QStringList selectedMasters(); + void addDataFiles(Files::Collections &fileCollections, const QString &encoding); void addPlugins(const QModelIndex &index); void removePlugins(const QModelIndex &index); void uncheckPlugins(); diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index b9ea8041d6..2542ef350e 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -16,7 +16,7 @@ source_group(game FILES ${GAME} ${GAME_HEADER}) add_openmw_dir (mwrender renderingmanager debugging sky player animation npcanimation creatureanimation actors objects renderinginterface localmap occlusionquery terrain terrainmaterial water shadows - compositors + compositors characterpreview externalrendering ) add_openmw_dir (mwinput @@ -29,7 +29,7 @@ add_openmw_dir (mwgui map_window window_pinnable_base cursorreplace tooltips scrollwindow bookwindow list formatting inventorywindow container hud countdialog tradewindow settingswindow confirmationdialog alchemywindow referenceinterface spellwindow mainmenu quickkeysmenu - itemselection spellbuyingwindow loadingscreen + itemselection spellbuyingwindow loadingscreen levelupdialog ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 58fe180e91..0fd78225ab 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -118,7 +118,7 @@ namespace MWBase /// Set value for the given ID. virtual void setValue (const std::string& id, const MWMechanics::Stat& value) = 0; virtual void setValue (int parSkill, const MWMechanics::Stat& value) = 0; - virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value) = 0; + virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value) = 0; virtual void setValue (const std::string& id, const std::string& value) = 0; virtual void setValue (const std::string& id, int value) = 0; @@ -219,6 +219,9 @@ namespace MWBase virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total) = 0; virtual void loadingDone() = 0; + + virtual void enableRest() = 0; + virtual bool getRestEnabled() = 0; }; } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 19405fb7a1..a83d6906f4 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -35,6 +35,11 @@ namespace ESMS struct ESMStore; } +namespace MWRender +{ + class ExternalRendering; +} + namespace MWWorld { class CellStore; @@ -271,6 +276,8 @@ namespace MWBase virtual void togglePlayerLooking(bool enable) = 0; virtual void renderPlayer() = 0; + + virtual void setupExternalRendering (MWRender::ExternalRendering& rendering) = 0; }; } diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 7d63f69220..0005b78ad4 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -151,7 +151,7 @@ void CharacterCreation::setValue (const std::string& id, const MWMechanics::Stat } } -void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat& value) +void CharacterCreation::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { if (mReviewDialog) { @@ -294,17 +294,17 @@ void CharacterCreation::spawnDialog(const char id) } } -void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) +void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) { mPlayerHealth = value; } -void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) +void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) { mPlayerMagicka = value; } -void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) +void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) { mPlayerFatigue = value; } diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index d65763d0cd..28ced2e70a 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -35,14 +35,14 @@ namespace MWGui //Show a dialog void spawnDialog(const char id); - void setPlayerHealth (const MWMechanics::DynamicStat& value); + void setPlayerHealth (const MWMechanics::DynamicStat& value); - void setPlayerMagicka (const MWMechanics::DynamicStat& value); + void setPlayerMagicka (const MWMechanics::DynamicStat& value); - void setPlayerFatigue (const MWMechanics::DynamicStat& value); + void setPlayerFatigue (const MWMechanics::DynamicStat& value); void setValue (const std::string& id, const MWMechanics::Stat& value); - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); + void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); void configureSkills (const SkillList& major, const SkillList& minor); @@ -65,9 +65,9 @@ namespace MWGui std::string mPlayerRaceId; std::string mPlayerBirthSignId; ESM::Class mPlayerClass; - MWMechanics::DynamicStat mPlayerHealth; - MWMechanics::DynamicStat mPlayerMagicka; - MWMechanics::DynamicStat mPlayerFatigue; + MWMechanics::DynamicStat mPlayerHealth; + MWMechanics::DynamicStat mPlayerMagicka; + MWMechanics::DynamicStat mPlayerFatigue; //Class generation vars unsigned mGenerateClassStep; // Keeps track of current step in Generate Class dialog diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index b148335537..72dc0c7fbe 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -161,7 +161,7 @@ void HUD::setEffect(const char *img) mEffect1->setImageTexture(img); } -void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) +void HUD::setValue(const std::string& id, const MWMechanics::DynamicStat& value) { static const char *ids[] = { diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index de4228e87c..013ad59f05 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -14,7 +14,7 @@ namespace MWGui public: HUD(int width, int height, int fpsLevel, DragAndDrop* dragAndDrop); void setEffect(const char *img); - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); + void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setFPS(float fps); void setTriangleCount(unsigned int count); void setBatchCount(unsigned int count); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 6dc19944f3..a8c0e2ac9d 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -44,10 +44,14 @@ namespace MWGui : ContainerBase(dragAndDrop) , WindowPinnableBase("openmw_inventory_window.layout", parWindowManager) , mTrading(false) + , mLastXSize(0) + , mLastYSize(0) + , mPreview(MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer ()) { static_cast(mMainWidget)->eventWindowChangeCoord += MyGUI::newDelegate(this, &InventoryWindow::onWindowResize); getWidget(mAvatar, "Avatar"); + getWidget(mAvatarImage, "AvatarImage"); getWidget(mEncumbranceBar, "EncumbranceBar"); getWidget(mEncumbranceText, "EncumbranceBarT"); getWidget(mFilterAll, "AllButton"); @@ -76,6 +80,8 @@ namespace MWGui setCoord(0, 342, 498, 258); + MWBase::Environment::get().getWorld ()->setupExternalRendering (mPreview); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); openContainer(player); } @@ -89,6 +95,7 @@ namespace MWGui mBoughtItems.clear(); onWindowResize(static_cast(mMainWidget)); + drawItems(); } void InventoryWindow::onWindowResize(MyGUI::Window* _sender) @@ -99,7 +106,13 @@ namespace MWGui mRightPane->getPosition().top, _sender->getSize().width - 12 - (_sender->getSize().height-44) * aspect - 15, _sender->getSize().height-44 ); - drawItems(); + + if (mMainWidget->getSize().width != mLastXSize || mMainWidget->getSize().height != mLastYSize) + { + drawItems(); + mLastXSize = mMainWidget->getSize().width; + mLastYSize = mMainWidget->getSize().height; + } } void InventoryWindow::onFilterChanged(MyGUI::Widget* _sender) @@ -171,15 +184,45 @@ namespace MWGui drawItems(); - // update selected weapon icon - MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - MWWorld::ContainerStoreIterator weaponSlot = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - if (weaponSlot == invStore.end()) - mWindowManager.unsetSelectedWeapon(); - else - mWindowManager.setSelectedWeapon(*weaponSlot, 100); /// \todo track weapon durability - + notifyContentChanged(); } + else + { + MyGUI::IntPoint mousePos = MyGUI::InputManager::getInstance ().getLastPressedPosition (MyGUI::MouseButton::Left); + MyGUI::IntPoint relPos = mousePos - mAvatar->getAbsolutePosition (); + int realX = int(float(relPos.left) / float(mAvatar->getSize().width) * 512.f ); + int realY = int(float(relPos.top) / float(mAvatar->getSize().height) * 1024.f ); + + MWWorld::Ptr itemSelected = getAvatarSelectedItem (realX, realY); + if (itemSelected.isEmpty ()) + return; + + for (unsigned int i=0; i < mContainerWidget->getChildCount (); ++i) + { + MyGUI::Widget* w = mContainerWidget->getChildAt (i); + + if (*w->getUserData() == itemSelected) + { + onSelectedItem(w); + return; + } + } + } + } + + MWWorld::Ptr InventoryWindow::getAvatarSelectedItem(int x, int y) + { + int slot = mPreview.getSlotSelected (x, y); + + if (slot == -1) + return MWWorld::Ptr(); + + MWWorld::Ptr player = mPtr; + MWWorld::InventoryStore& invStore = MWWorld::Class::get(player).getInventoryStore(player); + if (invStore.getSlot(slot) != invStore.end()) + return *invStore.getSlot (slot); + else + return MWWorld::Ptr(); } std::vector InventoryWindow::getEquippedItems() @@ -265,6 +308,12 @@ namespace MWGui mWindowManager.unsetSelectedWeapon(); else mWindowManager.setSelectedWeapon(*weaponSlot, 100); /// \todo track weapon durability + + MyGUI::IntSize size = mAvatar->getSize(); + + mPreview.update (size.width, size.height); + mAvatarImage->setSize(MyGUI::IntSize(std::max(mAvatar->getSize().width, 512), std::max(mAvatar->getSize().height, 1024))); + mAvatarImage->setImageTexture("CharacterPreview"); } void InventoryWindow::pickUpObject (MWWorld::Ptr object) @@ -329,4 +378,9 @@ namespace MWGui text->setCaption(getCountString(count)); mDragAndDrop->mDraggedFrom = this; } + + MyGUI::IntCoord InventoryWindow::getAvatarScreenCoord () + { + return mAvatar->getAbsoluteCoord (); + } } diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index fbdb79977a..84b576a58c 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -1,6 +1,8 @@ #ifndef MGUI_Inventory_H #define MGUI_Inventory_H +#include "../mwrender/characterpreview.hpp" + #include "container.hpp" #include "window_pinnable_base.hpp" @@ -22,8 +24,13 @@ namespace MWGui int getPlayerGold(); + MyGUI::IntCoord getAvatarScreenCoord(); + + MWWorld::Ptr getAvatarSelectedItem(int x, int y); + protected: MyGUI::Widget* mAvatar; + MyGUI::ImageBox* mAvatarImage; MyGUI::TextBox* mArmorRating; MyGUI::ProgressBar* mEncumbranceBar; MyGUI::TextBox* mEncumbranceText; @@ -37,6 +44,11 @@ namespace MWGui MyGUI::Button* mFilterMagic; MyGUI::Button* mFilterMisc; + int mLastXSize; + int mLastYSize; + + MWRender::InventoryPreview mPreview; + bool mTrading; void onWindowResize(MyGUI::Window* _sender); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp new file mode 100644 index 0000000000..3616283d97 --- /dev/null +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -0,0 +1,197 @@ +#include "levelupdialog.hpp" + +#include + +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/stat.hpp" + +#include +#include + +namespace MWGui +{ + + LevelupDialog::LevelupDialog(MWBase::WindowManager &parWindowManager) + : WindowBase("openmw_levelup_dialog.layout", parWindowManager) + { + getWidget(mOkButton, "OkButton"); + getWidget(mClassImage, "ClassImage"); + getWidget(mLevelText, "LevelText"); + + mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onOkButtonClicked); + + for (int i=1; i<9; ++i) + { + MyGUI::TextBox* t; + getWidget(t, "AttribVal" + boost::lexical_cast(i)); + + MyGUI::Button* b; + getWidget(b, "Attrib" + boost::lexical_cast(i)); + b->setUserData (i-1); + b->eventMouseButtonClick += MyGUI::newDelegate(this, &LevelupDialog::onAttributeClicked); + mAttributes.push_back(b); + + mAttributeValues.push_back(t); + + getWidget(t, "AttribMultiplier" + boost::lexical_cast(i)); + + mAttributeMultipliers.push_back(t); + } + + int curX = mMainWidget->getWidth()/2 - (16 + 2) * 1.5; + for (int i=0; i<3; ++i) + { + MyGUI::ImageBox* image = mMainWidget->createWidget("ImageBox", MyGUI::IntCoord(curX,250,16,16), MyGUI::Align::Default); + image->setImageTexture ("icons\\tx_goldicon.dds"); + curX += 24+2; + mCoins.push_back(image); + } + + center(); + } + + void LevelupDialog::setAttributeValues() + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); + MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); + + for (int i=0; i<8; ++i) + { + int val = creatureStats.getAttribute (i).getBase (); + if (std::find(mSpentAttributes.begin(), mSpentAttributes.end(), i) != mSpentAttributes.end()) + { + val += pcStats.getLevelupAttributeMultiplier (i); + } + + if (val >= 100) + val = 100; + + mAttributeValues[i]->setCaption(boost::lexical_cast(val)); + } + } + + + void LevelupDialog::resetCoins () + { + int curX = mMainWidget->getWidth()/2 - (16 + 2) * 1.5; + for (int i=0; i<3; ++i) + { + MyGUI::ImageBox* image = mCoins[i]; + image->setCoord(MyGUI::IntCoord(curX,250,16,16)); + curX += 24+2; + } + } + + void LevelupDialog::assignCoins () + { + resetCoins(); + for (unsigned int i=0; igetCaption() == "" ? 0 : 30; + + MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mMainWidget->getAbsolutePosition() - MyGUI::IntPoint(24+xdiff,-4); + image->setPosition(pos); + } + + setAttributeValues(); + } + + void LevelupDialog::open() + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); + MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); + + center(); + + mSpentAttributes.clear(); + resetCoins(); + + setAttributeValues(); + + // set class image + const ESM::Class& playerClass = MWBase::Environment::get().getWorld ()->getPlayer ().getClass (); + // retrieve the ID to this class + std::string classId; + std::map list = MWBase::Environment::get().getWorld()->getStore ().classes.list; + for (std::map::iterator it = list.begin(); it != list.end(); ++it) + { + if (playerClass.name == it->second.name) + classId = it->first; + } + mClassImage->setImageTexture ("textures\\levelup\\" + classId + ".dds"); + + /// \todo replace this with INI-imported texts + int level = creatureStats.getLevel ()+1; + mLevelText->setCaptionWithReplacing("#{sLevelUpMenu1} " + boost::lexical_cast(level)); + + for (int i=0; i<8; ++i) + { + MyGUI::TextBox* text = mAttributeMultipliers[i]; + int mult = pcStats.getLevelupAttributeMultiplier (i); + text->setCaption(mult <= 1 ? "" : "x" + boost::lexical_cast(mult)); + } + } + + void LevelupDialog::onOkButtonClicked (MyGUI::Widget* sender) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats (player); + MWMechanics::NpcStats& pcStats = MWWorld::Class::get(player).getNpcStats (player); + + if (mSpentAttributes.size() < 3) + MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage36}", std::vector()); + else + { + // increase attributes + for (int i=0; i<3; ++i) + { + MWMechanics::Stat& attribute = creatureStats.getAttribute(mSpentAttributes[i]); + attribute.setBase (attribute.getBase () + pcStats.getLevelupAttributeMultiplier (mSpentAttributes[i])); + + if (attribute.getBase() >= 100) + attribute.setBase(100); + } + + // "When you gain a level, in addition to increasing three primary attributes, your Health + // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level, + // the Health increase is calculated from the increased Endurance" + creatureStats.increaseLevelHealthBonus (creatureStats.getAttribute(ESM::Attribute::Endurance).getBase() * 0.1f); + + creatureStats.setLevel (creatureStats.getLevel()+1); + pcStats.levelUp (); + + mWindowManager.removeGuiMode (GM_Rest); + } + + } + + void LevelupDialog::onAttributeClicked (MyGUI::Widget *sender) + { + int attribute = *sender->getUserData(); + + std::vector::iterator found = std::find(mSpentAttributes.begin(), mSpentAttributes.end(), attribute); + if (found != mSpentAttributes.end()) + mSpentAttributes.erase (found); + else + { + if (mSpentAttributes.size() == 3) + mSpentAttributes[2] = attribute; + else + mSpentAttributes.push_back(attribute); + } + assignCoins(); + } +} diff --git a/apps/openmw/mwgui/levelupdialog.hpp b/apps/openmw/mwgui/levelupdialog.hpp new file mode 100644 index 0000000000..f5b24530d1 --- /dev/null +++ b/apps/openmw/mwgui/levelupdialog.hpp @@ -0,0 +1,39 @@ +#ifndef MWGUI_LEVELUPDIALOG_H +#define MWGUI_LEVELUPDIALOG_H + +#include "window_base.hpp" + +namespace MWGui +{ + + class LevelupDialog : public WindowBase + { + public: + LevelupDialog(MWBase::WindowManager& parWindowManager); + + virtual void open(); + + private: + MyGUI::Button* mOkButton; + MyGUI::ImageBox* mClassImage; + MyGUI::TextBox* mLevelText; + + std::vector mAttributes; + std::vector mAttributeValues; + std::vector mAttributeMultipliers; + std::vector mCoins; + + std::vector mSpentAttributes; + + void onOkButtonClicked (MyGUI::Widget* sender); + void onAttributeClicked (MyGUI::Widget* sender); + + void assignCoins(); + void resetCoins(); + + void setAttributeValues(); + }; + +} + +#endif diff --git a/apps/openmw/mwgui/mode.hpp b/apps/openmw/mwgui/mode.hpp index 7e1adcf8bd..eb2c52b26f 100644 --- a/apps/openmw/mwgui/mode.hpp +++ b/apps/openmw/mwgui/mode.hpp @@ -22,6 +22,8 @@ namespace MWGui GM_Rest, GM_SpellBuying, + GM_Levelup, + // Startup character creation dialogs GM_Name, GM_Race, diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index 370b30733a..3fe70d959b 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -25,15 +25,18 @@ RaceDialog::RaceDialog(MWBase::WindowManager& parWindowManager) , mHairIndex(0) , mFaceCount(10) , mHairCount(14) + , mCurrentAngle(0) { // Centre dialog center(); - // These are just demo values, you should replace these with - // real calls from outside the class later. - setText("AppearanceT", mWindowManager.getGameSettingString("sRaceMenu1", "Appearance")); - getWidget(mAppearanceBox, "AppearanceBox"); + getWidget(mPreviewImage, "PreviewImage"); + + MWBase::Environment::get().getWorld ()->setupExternalRendering (mPreview); + mPreview.update (0); + + mPreviewImage->setImageTexture ("CharacterHeadPreview"); getWidget(mHeadRotate, "HeadRotate"); mHeadRotate->setScrollRange(50); @@ -149,7 +152,10 @@ void RaceDialog::onBackClicked(MyGUI::Widget* _sender) void RaceDialog::onHeadRotate(MyGUI::ScrollBar*, size_t _position) { - // TODO: Rotate head + float angle = (float(_position) / 49.f - 0.5) * 3.14 * 2; + float diff = angle - mCurrentAngle; + mPreview.update (diff); + mCurrentAngle += diff; } void RaceDialog::onSelectPreviousGender(MyGUI::Widget*) diff --git a/apps/openmw/mwgui/race.hpp b/apps/openmw/mwgui/race.hpp index 4aded28c3a..0505e58e13 100644 --- a/apps/openmw/mwgui/race.hpp +++ b/apps/openmw/mwgui/race.hpp @@ -1,11 +1,15 @@ #ifndef MWGUI_RACE_H #define MWGUI_RACE_H + +#include + #include +#include "../mwrender/characterpreview.hpp" + #include "window_base.hpp" -#include namespace MWGui { @@ -73,7 +77,7 @@ namespace MWGui void updateSkills(); void updateSpellPowers(); - MyGUI::CanvasPtr mAppearanceBox; + MyGUI::ImageBox* mPreviewImage; MyGUI::ListBox* mRaceList; MyGUI::ScrollBar* mHeadRotate; @@ -87,6 +91,10 @@ namespace MWGui int mFaceCount, mHairCount; std::string mCurrentRaceId; + + float mCurrentAngle; + + MWRender::RaceSelectionPreview mPreview; }; } #endif diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 76b211b18e..411fead089 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -134,21 +134,21 @@ void ReviewDialog::setBirthSign(const std::string& signId) } } -void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) +void ReviewDialog::setHealth(const MWMechanics::DynamicStat& value) { mHealth->setValue(value.getCurrent(), value.getModified()); std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); mHealth->setUserString("Caption_HealthDescription", "#{sHealthDesc}\n" + valStr); } -void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) +void ReviewDialog::setMagicka(const MWMechanics::DynamicStat& value) { mMagicka->setValue(value.getCurrent(), value.getModified()); std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); mMagicka->setUserString("Caption_HealthDescription", "#{sIntDesc}\n" + valStr); } -void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) +void ReviewDialog::setFatigue(const MWMechanics::DynamicStat& value) { mFatigue->setValue(value.getCurrent(), value.getModified()); std::string valStr = boost::lexical_cast(value.getCurrent()) + "/" + boost::lexical_cast(value.getModified()); diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 058e3cff38..2b0740234e 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -35,9 +35,9 @@ namespace MWGui void setClass(const ESM::Class& class_); void setBirthSign (const std::string &signId); - void setHealth(const MWMechanics::DynamicStat& value); - void setMagicka(const MWMechanics::DynamicStat& value); - void setFatigue(const MWMechanics::DynamicStat& value); + void setHealth(const MWMechanics::DynamicStat& value); + void setMagicka(const MWMechanics::DynamicStat& value); + void setFatigue(const MWMechanics::DynamicStat& value); void setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::Stat& value); diff --git a/apps/openmw/mwgui/stats_window.cpp b/apps/openmw/mwgui/stats_window.cpp index ffbfd7d78e..701c39b0d6 100644 --- a/apps/openmw/mwgui/stats_window.cpp +++ b/apps/openmw/mwgui/stats_window.cpp @@ -137,7 +137,7 @@ void StatsWindow::setValue (const std::string& id, const MWMechanics::Stat& } } -void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat& value) +void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { static const char *ids[] = { @@ -150,7 +150,7 @@ void StatsWindow::setValue (const std::string& id, const MWMechanics::DynamicSta if (ids[i]==id) { std::string id (ids[i]); - setBar (id, id + "T", value.getCurrent(), value.getModified()); + setBar (id, id + "T", static_cast(value.getCurrent()), static_cast(value.getModified())); // health, magicka, fatigue tooltip MyGUI::Widget* w; @@ -236,12 +236,21 @@ void StatsWindow::configureSkills (const std::vector& major, const std::vec void StatsWindow::onFrame () { - if (mMainWidget->getVisible()) + if (!mMainWidget->getVisible()) return; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWMechanics::NpcStats PCstats = MWWorld::Class::get(player).getNpcStats(player); + // level progress + MyGUI::Widget* levelWidget; + for (int i=0; i<2; ++i) + { + getWidget(levelWidget, i==0 ? "Level_str" : "LevelText"); + levelWidget->setUserString("RangePosition_LevelProgress", boost::lexical_cast(PCstats.getLevelProgress())); + levelWidget->setUserString("Caption_LevelProgressText", boost::lexical_cast(PCstats.getLevelProgress()) + "/10"); + } + setFactions(PCstats.getFactionRanks()); setBirthSign(MWBase::Environment::get().getWorld()->getPlayer().getBirthsign()); diff --git a/apps/openmw/mwgui/stats_window.hpp b/apps/openmw/mwgui/stats_window.hpp index 73c4706dd9..75f8c568bf 100644 --- a/apps/openmw/mwgui/stats_window.hpp +++ b/apps/openmw/mwgui/stats_window.hpp @@ -32,7 +32,7 @@ namespace MWGui /// Set value for the given ID. void setValue (const std::string& id, const MWMechanics::Stat& value); - void setValue (const std::string& id, const MWMechanics::DynamicStat& value); + void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue (const std::string& id, const std::string& value); void setValue (const std::string& id, int value); void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 7aee26bcc3..8186279d18 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -14,6 +14,7 @@ #include "map_window.hpp" #include "widgets.hpp" +#include "inventorywindow.hpp" using namespace MWGui; using namespace MyGUI; @@ -162,12 +163,23 @@ void ToolTips::onFrame(float frameDuration) return; } - if (type == "ItemPtr") { mFocusObject = *focus->getUserData(); tooltipSize = getToolTipViaPtr(false); } + else if (type == "AvatarItemSelection") + { + MyGUI::IntCoord avatarPos = mWindowManager->getInventoryWindow ()->getAvatarScreenCoord (); + MyGUI::IntPoint relMousePos = MyGUI::InputManager::getInstance ().getMousePosition () - MyGUI::IntPoint(avatarPos.left, avatarPos.top); + int realX = int(float(relMousePos.left) / float(avatarPos.width) * 512.f ); + int realY = int(float(relMousePos.top) / float(avatarPos.height) * 1024.f ); + MWWorld::Ptr item = mWindowManager->getInventoryWindow ()->getAvatarSelectedItem (realX, realY); + + mFocusObject = item; + if (!mFocusObject.isEmpty ()) + tooltipSize = getToolTipViaPtr(false); + } else if (type == "Spell") { ToolTipInfo info; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 99f476574c..0241cc734a 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -44,6 +44,7 @@ #include "spellwindow.hpp" #include "quickkeysmenu.hpp" #include "loadingscreen.hpp" +#include "levelupdialog.hpp" using namespace MWGui; @@ -84,6 +85,7 @@ WindowManager::WindowManager( , mGarbageDialogs() , mShown(GW_ALL) , mAllowed(newGame ? GW_None : GW_ALL) + , mRestAllowed(newGame ? false : true) , mShowFPSLevel(fpsLevel) , mFPS(0.0f) , mTriangleCount(0) @@ -147,6 +149,7 @@ WindowManager::WindowManager( mAlchemyWindow = new AlchemyWindow(*this); mSpellWindow = new SpellWindow(*this); mQuickKeysMenu = new QuickKeysMenu(*this); + mLevelupDialog = new LevelupDialog(*this); mLoadingScreen = new LoadingScreen(mOgre->getScene (), mOgre->getWindow (), *this); mLoadingScreen->onResChange (w,h); @@ -200,6 +203,7 @@ WindowManager::~WindowManager() delete mAlchemyWindow; delete mSpellWindow; delete mLoadingScreen; + delete mLevelupDialog; cleanupGarbage(); @@ -247,6 +251,7 @@ void WindowManager::updateVisible() mAlchemyWindow->setVisible(false); mSpellWindow->setVisible(false); mQuickKeysMenu->setVisible(false); + mLevelupDialog->setVisible(false); mHud->setVisible(true); @@ -298,6 +303,9 @@ void WindowManager::updateVisible() case GM_Alchemy: mAlchemyWindow->setVisible(true); break; + case GM_Rest: + mLevelupDialog->setVisible(true); + break; case GM_Name: case GM_Race: case GM_Class: @@ -395,7 +403,7 @@ void WindowManager::setValue (int parSkill, const MWMechanics::Stat& valu mPlayerSkillValues[parSkill] = value; } -void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) +void WindowManager::setValue (const std::string& id, const MWMechanics::DynamicStat& value) { mStatsWindow->setValue (id, value); mHud->setValue (id, value); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index e16b03e43d..a02aa17b57 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -62,6 +62,7 @@ namespace MWGui class AlchemyWindow; class QuickKeysMenu; class LoadingScreen; + class LevelupDialog; class WindowManager : public MWBase::WindowManager { @@ -117,7 +118,7 @@ namespace MWGui ///< Set value for the given ID. virtual void setValue (const std::string& id, const MWMechanics::Stat& value); virtual void setValue (int parSkill, const MWMechanics::Stat& value); - virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value); + virtual void setValue (const std::string& id, const MWMechanics::DynamicStat& value); virtual void setValue (const std::string& id, const std::string& value); virtual void setValue (const std::string& id, int value); @@ -199,6 +200,9 @@ namespace MWGui virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total); virtual void loadingDone(); + virtual void enableRest() { mRestAllowed = true; } + virtual bool getRestEnabled() { return mRestAllowed; } + private: OEngine::GUI::MyGUIManager *mGuiManager; HUD *mHud; @@ -224,6 +228,7 @@ namespace MWGui SpellWindow* mSpellWindow; QuickKeysMenu* mQuickKeysMenu; LoadingScreen* mLoadingScreen; + LevelupDialog* mLevelupDialog; CharacterCreation* mCharGen; @@ -241,7 +246,7 @@ namespace MWGui std::map > mPlayerAttributes; SkillList mPlayerMajorSkills, mPlayerMinorSkills; std::map > mPlayerSkillValues; - MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; + MWMechanics::DynamicStat mPlayerHealth, mPlayerMagicka, mPlayerFatigue; MyGUI::Gui *mGui; // Gui @@ -258,6 +263,8 @@ namespace MWGui allow() and disableAll(). */ GuiWindow mAllowed; + // is the rest window allowed? + bool mRestAllowed; void updateVisible(); // Update visibility of all windows based on mode, shown and allowed settings diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index b198785d2f..c3e1314402 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -190,6 +190,9 @@ namespace MWInput case A_ToggleWeapon: toggleWeapon (); break; + case A_Rest: + rest(); + break; case A_ToggleSpell: toggleSpell (); break; @@ -543,6 +546,15 @@ namespace MWInput } } + void InputManager::rest() + { + if (!mWindows.getRestEnabled () || mWindows.isGuiMode ()) + return; + + /// \todo check if resting is currently allowed (enemies nearby?) + mWindows.pushGuiMode (MWGui::GM_Rest); + } + void InputManager::screenshot() { mEngine.screenshot(); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 7d03f1d5bf..5e6169f689 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -168,6 +168,7 @@ namespace MWInput void toggleWalking(); void toggleAutoMove(); void exitNow(); + void rest(); void quickKey (int index); void showQuickKeysMenu(); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index bd037df9f0..b4cc40fec2 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -68,7 +68,7 @@ namespace MWMechanics creatureStats.getMagicEffects().get (EffectKey (84)).mMagnitude * 0.1 + 0.5; creatureStats.getHealth().setBase( - static_cast (0.5 * (strength + endurance))); + static_cast (0.5 * (strength + endurance)) + creatureStats.getLevelHealthBonus ()); creatureStats.getMagicka().setBase( static_cast (intelligence + magickaFactor * intelligence)); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 91a9225fec..fc05231412 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -9,6 +9,21 @@ namespace MWMechanics { + CreatureStats::CreatureStats() + : mLevelHealthBonus(0.f) + { + } + + void CreatureStats::increaseLevelHealthBonus (float value) + { + mLevelHealthBonus += value; + } + + float CreatureStats::getLevelHealthBonus () const + { + return mLevelHealthBonus; + } + const AiSequence& CreatureStats::getAiSequence() const { return mAiSequence; @@ -40,17 +55,17 @@ namespace MWMechanics return mAttributes[index]; } - const DynamicStat &CreatureStats::getHealth() const + const DynamicStat &CreatureStats::getHealth() const { return mDynamic[0]; } - const DynamicStat &CreatureStats::getMagicka() const + const DynamicStat &CreatureStats::getMagicka() const { return mDynamic[1]; } - const DynamicStat &CreatureStats::getFatigue() const + const DynamicStat &CreatureStats::getFatigue() const { return mDynamic[2]; } @@ -103,22 +118,22 @@ namespace MWMechanics return mAttributes[index]; } - DynamicStat &CreatureStats::getHealth() + DynamicStat &CreatureStats::getHealth() { return mDynamic[0]; } - DynamicStat &CreatureStats::getMagicka() + DynamicStat &CreatureStats::getMagicka() { return mDynamic[1]; } - DynamicStat &CreatureStats::getFatigue() + DynamicStat &CreatureStats::getFatigue() { return mDynamic[2]; } - DynamicStat &CreatureStats::getDynamic(int index) + DynamicStat &CreatureStats::getDynamic(int index) { if (index < 0 || index > 2) { throw std::runtime_error("dynamic stat index is out of range"); @@ -154,17 +169,17 @@ namespace MWMechanics mAttributes[index] = value; } - void CreatureStats::setHealth(const DynamicStat &value) + void CreatureStats::setHealth(const DynamicStat &value) { mDynamic[0] = value; } - void CreatureStats::setMagicka(const DynamicStat &value) + void CreatureStats::setMagicka(const DynamicStat &value) { mDynamic[1] = value; } - void CreatureStats::setFatigue(const DynamicStat &value) + void CreatureStats::setFatigue(const DynamicStat &value) { mDynamic[2] = value; } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index a6fb6779af..7a1e46f56e 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -19,7 +19,7 @@ namespace MWMechanics class CreatureStats { Stat mAttributes[8]; - DynamicStat mDynamic[3]; // health, magicka, fatigue + DynamicStat mDynamic[3]; // health, magicka, fatigue int mLevel; Spells mSpells; ActiveSpells mActiveSpells; @@ -30,15 +30,18 @@ namespace MWMechanics int mAlarm; AiSequence mAiSequence; + float mLevelHealthBonus; + public: + CreatureStats(); const Stat & getAttribute(int index) const; - const DynamicStat & getHealth() const; + const DynamicStat & getHealth() const; - const DynamicStat & getMagicka() const; + const DynamicStat & getMagicka() const; - const DynamicStat & getFatigue() const; + const DynamicStat & getFatigue() const; const Spells & getSpells() const; @@ -59,13 +62,13 @@ namespace MWMechanics Stat & getAttribute(int index); - DynamicStat & getHealth(); + DynamicStat & getHealth(); - DynamicStat & getMagicka(); + DynamicStat & getMagicka(); - DynamicStat & getFatigue(); + DynamicStat & getFatigue(); - DynamicStat & getDynamic(int index); + DynamicStat & getDynamic(int index); Spells & getSpells(); @@ -76,11 +79,11 @@ namespace MWMechanics void setAttribute(int index, const Stat &value); - void setHealth(const DynamicStat &value); + void setHealth(const DynamicStat &value); - void setMagicka(const DynamicStat &value); + void setMagicka(const DynamicStat &value); - void setFatigue(const DynamicStat &value); + void setFatigue(const DynamicStat &value); void setSpells(const Spells &spells); @@ -104,6 +107,10 @@ namespace MWMechanics float getFatigueTerm() const; ///< Return effective fatigue + + // small hack to allow the fact that Health permanently increases by 10% of endurance on each level up + void increaseLevelHealthBonus(float value); + float getLevelHealthBonus() const; }; } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index d2908e26e2..5e3896af21 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include #include #include @@ -12,10 +14,17 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" MWMechanics::NpcStats::NpcStats() : mMovementFlags (0), mDrawState (DrawState_Nothing) -{} +, mLevelProgress(0) +{ + mSkillIncreases.resize (ESM::Attribute::Length); + for (int i=0; i (base)!=level) + { + // skill leveled up + increaseSkill(skillIndex, class_, false); + } + else + getSkill (skillIndex).setBase (base); +} + +void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress) +{ + float base = getSkill (skillIndex).getBase(); + + int level = static_cast (base); + + if (level >= 100) + return; + + if (preserveProgress) + base += 1; + else base = level+1; + // if this is a major or minor skill of the class, increase level progress + bool levelProgress = false; + for (int i=0; i<2; ++i) + for (int j=0; j<5; ++j) + { + int skill = class_.data.skills[j][i]; + if (skill == skillIndex) + levelProgress = true; + } + + mLevelProgress += levelProgress; + + // check the attribute this skill belongs to + const ESM::Skill* skill = MWBase::Environment::get().getWorld ()->getStore ().skills.find(skillIndex); + ++mSkillIncreases[skill->data.attribute]; + + // Play sound & skill progress notification + /// \todo check if character is the player, if levelling is ever implemented for NPCs + MWBase::Environment::get().getSoundManager ()->playSound ("skillraise", 1, 1); + + std::stringstream message; + message << boost::format(MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "")) + % std::string("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}") + % base; + MWBase::Environment::get().getWindowManager ()->messageBox(message.str(), std::vector()); + + if (mLevelProgress >= 10) + { + // levelup is possible now + MWBase::Environment::get().getWindowManager ()->messageBox ("#{sLevelUpMsg}", std::vector()); + } + getSkill (skillIndex).setBase (base); } + +int MWMechanics::NpcStats::getLevelProgress () const +{ + return mLevelProgress; +} + +void MWMechanics::NpcStats::levelUp() +{ + mLevelProgress -= 10; + for (int i=0; i #include #include +#include #include "stat.hpp" #include "drawstate.hpp" @@ -45,6 +46,10 @@ namespace MWMechanics unsigned int mMovementFlags; Stat mSkill[27]; + int mLevelProgress; // 0-10 + + std::vector mSkillIncreases; // number of skill increases for each attribute + public: NpcStats(); @@ -73,6 +78,14 @@ namespace MWMechanics void useSkill (int skillIndex, const ESM::Class& class_, int usageType = -1); ///< Increase skill by usage. + + void increaseSkill (int skillIndex, const ESM::Class& class_, bool preserveProgress); + + int getLevelProgress() const; + + int getLevelupAttributeMultiplier(int attribute) const; + + void levelUp(); }; } diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index 415d172414..41d02332e8 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -3,6 +3,8 @@ #include #include +#include "renderconst.hpp" + using namespace Ogre; using namespace MWRender; @@ -23,7 +25,7 @@ void Actors::setMwRoot(Ogre::SceneNode* root){ void Actors::insertNPC(const MWWorld::Ptr& ptr, MWWorld::InventoryStore& inv){ insertBegin(ptr, true, true); - NpcAnimation* anim = new MWRender::NpcAnimation(ptr, mRend, inv); + NpcAnimation* anim = new MWRender::NpcAnimation(ptr, ptr.getRefData ().getBaseNode (), inv, RV_Actors); mAllActors[ptr] = anim; } @@ -68,7 +70,7 @@ void Actors::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_){ void Actors::insertCreature (const MWWorld::Ptr& ptr){ insertBegin(ptr, true, true); - CreatureAnimation* anim = new MWRender::CreatureAnimation(ptr, mRend); + CreatureAnimation* anim = new MWRender::CreatureAnimation(ptr); //mAllActors.insert(std::pair(ptr,anim)); delete mAllActors[ptr]; mAllActors[ptr] = anim; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 46f3bdc0db..3d7629e5b5 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -11,9 +11,8 @@ namespace MWRender { -Animation::Animation(OEngine::Render::OgreRenderer& _rend) +Animation::Animation() : mInsert(NULL) - , mRend(_rend) , mTime(0.0f) , mSkipFrame(false) { diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 3611d35c0e..07583db788 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -31,7 +31,6 @@ class Animation { protected: Ogre::SceneNode* mInsert; - OEngine::Render::OgreRenderer &mRend; float mTime; GroupTimes mCurGroup; @@ -45,7 +44,7 @@ protected: bool findGroupTimes(const std::string &groupname, GroupTimes *times); public: - Animation(OEngine::Render::OgreRenderer& _rend); + Animation(); virtual ~Animation(); void playGroup(std::string groupname, int mode, int loops); diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp new file mode 100644 index 0000000000..01c5d594a7 --- /dev/null +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -0,0 +1,135 @@ +#include "characterpreview.hpp" + + +#include +#include + +#include + + +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" +#include "../mwworld/player.hpp" + +#include "renderconst.hpp" +#include "npcanimation.hpp" + +namespace MWRender +{ + + CharacterPreview::CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name, + Ogre::Vector3 position, Ogre::Vector3 lookAt) + : mSizeX(sizeX) + , mSizeY(sizeY) + , mName(name) + , mPosition(position) + , mLookAt(lookAt) + , mCharacter(character) + { + + } + + void CharacterPreview::onSetup() + { + + } + + void CharacterPreview::setup (Ogre::SceneManager *sceneManager) + { + mSceneMgr = sceneManager; + mCamera = mSceneMgr->createCamera (mName); + mCamera->setAspectRatio (float(mSizeX) / float(mSizeY)); + + mNode = static_cast(mSceneMgr->getRootSceneNode()->getChild("mwRoot"))->createChildSceneNode (); + + mAnimation = new NpcAnimation(mCharacter, mNode, + MWWorld::Class::get(mCharacter).getInventoryStore (mCharacter), RV_PlayerPreview); + + mNode->setVisible (false); + + mCamera->setPosition(mPosition); + mCamera->lookAt(mLookAt); + + mCamera->setNearClipDistance (0.01); + mCamera->setFarClipDistance (1000); + + mTexture = Ogre::TextureManager::getSingleton().createManual(mName, + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, mSizeX, mSizeY, 0, Ogre::PF_A8R8G8B8, Ogre::TU_RENDERTARGET); + + mRenderTarget = mTexture->getBuffer()->getRenderTarget(); + mViewport = mRenderTarget->addViewport(mCamera); + mViewport->setOverlaysEnabled(false); + mViewport->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0)); + mViewport->setShadowsEnabled(false); + mViewport->setMaterialScheme("local_map"); + mViewport->setVisibilityMask (RV_PlayerPreview); + mRenderTarget->setActive(true); + mRenderTarget->setAutoUpdated (false); + + onSetup (); + } + + CharacterPreview::~CharacterPreview () + { + Ogre::TextureManager::getSingleton().remove(mName); + } + + + // -------------------------------------------------------------------------------------------------- + + + InventoryPreview::InventoryPreview(MWWorld::Ptr character) + : CharacterPreview(character, 512, 1024, "CharacterPreview", Ogre::Vector3(0, 65, -180), Ogre::Vector3(0,65,0)) + { + } + + InventoryPreview::~InventoryPreview() + { + delete mSelectionBuffer; + } + + void InventoryPreview::update(int sizeX, int sizeY) + { + mAnimation->forceUpdate (); + + mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024))); + + mNode->setOrientation (Ogre::Quaternion::IDENTITY); + + mNode->setVisible (true); + + mRenderTarget->update(); + mSelectionBuffer->update(); + + mNode->setVisible (false); + } + + int InventoryPreview::getSlotSelected (int posX, int posY) + { + return mSelectionBuffer->getSelected (posX, posY); + } + + void InventoryPreview::onSetup () + { + mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, RV_PlayerPreview); + } + + // -------------------------------------------------------------------------------------------------- + + RaceSelectionPreview::RaceSelectionPreview() + : CharacterPreview(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), + 512, 512, "CharacterHeadPreview", Ogre::Vector3(0, 120, -35), Ogre::Vector3(0,125,0)) + { + + } + + void RaceSelectionPreview::update(float angle) + { + mNode->roll(Ogre::Radian(angle), Ogre::SceneNode::TS_LOCAL); + + mNode->setVisible (true); + mRenderTarget->update(); + mNode->setVisible (false); + } + +} diff --git a/apps/openmw/mwrender/characterpreview.hpp b/apps/openmw/mwrender/characterpreview.hpp new file mode 100644 index 0000000000..2a6b12b9ec --- /dev/null +++ b/apps/openmw/mwrender/characterpreview.hpp @@ -0,0 +1,88 @@ +#ifndef MWRENDER_CHARACTERPREVIEW_H +#define MWRENDER_CHARACTERPREVIEW_H + +#include +#include + + +#include "externalrendering.hpp" + +#include "../mwworld/ptr.hpp" + +namespace OEngine +{ +namespace Render +{ +class SelectionBuffer; +} +} + +namespace MWRender +{ + + class NpcAnimation; + + class CharacterPreview : public ExternalRendering + { + public: + CharacterPreview(MWWorld::Ptr character, int sizeX, int sizeY, const std::string& name, + Ogre::Vector3 position, Ogre::Vector3 lookAt); + virtual ~CharacterPreview(); + + virtual void setup (Ogre::SceneManager *sceneManager); + virtual void onSetup(); + + + protected: + Ogre::TexturePtr mTexture; + Ogre::RenderTarget* mRenderTarget; + Ogre::Viewport* mViewport; + + Ogre::Camera* mCamera; + + Ogre::SceneManager* mSceneMgr; + Ogre::SceneNode* mNode; + + Ogre::Vector3 mPosition; + Ogre::Vector3 mLookAt; + + MWWorld::Ptr mCharacter; + + MWRender::NpcAnimation* mAnimation; + + std::string mName; + + int mSizeX; + int mSizeY; + }; + + class InventoryPreview : public CharacterPreview + { + public: + + InventoryPreview(MWWorld::Ptr character); + virtual ~InventoryPreview(); + virtual void onSetup(); + + void update(int sizeX, int sizeY); + + int getSlotSelected(int posX, int posY); + + void setNpcAnimation (NpcAnimation* anim); + + private: + + OEngine::Render::SelectionBuffer* mSelectionBuffer; + }; + + class RaceSelectionPreview : public CharacterPreview + { + public: + RaceSelectionPreview(); + + void update(float angle); + }; + +} + +#endif diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 7fb153bd1e..5e3ec9a77c 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -16,7 +16,7 @@ CreatureAnimation::~CreatureAnimation() { } -CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend): Animation(_rend) +CreatureAnimation::CreatureAnimation(const MWWorld::Ptr& ptr): Animation() { mInsert = ptr.getRefData().getBaseNode(); MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwrender/creatureanimation.hpp b/apps/openmw/mwrender/creatureanimation.hpp index b3c9d3ddd7..e1a7bbb8f4 100644 --- a/apps/openmw/mwrender/creatureanimation.hpp +++ b/apps/openmw/mwrender/creatureanimation.hpp @@ -8,13 +8,13 @@ namespace MWRender{ -class CreatureAnimation: public Animation{ - + class CreatureAnimation: public Animation + { public: - virtual ~CreatureAnimation(); - CreatureAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend); - virtual void runAnimation(float timepassed); + virtual ~CreatureAnimation(); + CreatureAnimation(const MWWorld::Ptr& ptr); + virtual void runAnimation(float timepassed); -}; + }; } #endif diff --git a/apps/openmw/mwrender/externalrendering.hpp b/apps/openmw/mwrender/externalrendering.hpp new file mode 100644 index 0000000000..33c9afd875 --- /dev/null +++ b/apps/openmw/mwrender/externalrendering.hpp @@ -0,0 +1,23 @@ +#ifndef GAME_RENDERING_EXTERNALRENDERING_H +#define GAME_RENDERING_EXTERNALRENDERING_H + +namespace Ogre +{ + class SceneManager; +} + +namespace MWRender +{ + /// \brief Base class for out of world rendering + class ExternalRendering + { + public: + + virtual void setup (Ogre::SceneManager *sceneManager) = 0; + + virtual ~ExternalRendering() {} + }; +} + +#endif + diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 4f98aebc48..fdf4910f80 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -45,8 +45,8 @@ NpcAnimation::~NpcAnimation() } -NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv) - : Animation(_rend), mStateID(-1), mInv(_inv), timeToChange(0), +NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWorld::InventoryStore& _inv, int visibilityFlags) + : Animation(), mStateID(-1), mInv(_inv), timeToChange(0), mVisibilityFlags(visibilityFlags), robe(mInv.end()), helmet(mInv.end()), shirt(mInv.end()), cuirass(mInv.end()), greaves(mInv.end()), leftpauldron(mInv.end()), rightpauldron(mInv.end()), @@ -84,7 +84,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere std::cout << " Sex: Male" << " Height: " << race->data.height.male << "\n"; */ - mInsert = ptr.getRefData().getBaseNode(); + mInsert = node; assert(mInsert); std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); @@ -94,7 +94,9 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRendere { Ogre::Entity *base = mEntityList.mEntities[i]; - base->setVisibilityFlags(RV_Actors); + base->getUserObjectBindings ().setUserAny (Ogre::Any(-1)); + + base->setVisibilityFlags(mVisibilityFlags); bool transparent = false; for(unsigned int j=0;j < base->getNumSubEntities();++j) { @@ -351,13 +353,16 @@ void NpcAnimation::updateParts() } } -NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, const std::string &bonename) +NifOgre::EntityList NpcAnimation::insertBoundedPart(const std::string &mesh, int group, const std::string &bonename) { NifOgre::EntityList entities = NIFLoader::createEntities(mEntityList.mSkelBase, bonename, mInsert, mesh); std::vector &parts = entities.mEntities; for(size_t i = 0;i < parts.size();i++) - parts[i]->setVisibilityFlags(RV_Actors); + { + parts[i]->setVisibilityFlags(mVisibilityFlags); + parts[i]->getUserObjectBindings ().setUserAny (Ogre::Any(group)); + } return entities; } @@ -480,83 +485,83 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority, switch(type) { case ESM::PRT_Head: //0 - head = insertBoundedPart(mesh, "Head"); + head = insertBoundedPart(mesh, group, "Head"); break; case ESM::PRT_Hair: //1 - hair = insertBoundedPart(mesh, "Head"); + hair = insertBoundedPart(mesh, group, "Head"); break; case ESM::PRT_Neck: //2 - neck = insertBoundedPart(mesh, "Neck"); + neck = insertBoundedPart(mesh, group, "Neck"); break; case ESM::PRT_Cuirass: //3 - chest = insertBoundedPart(mesh, "Chest"); + chest = insertBoundedPart(mesh, group, "Chest"); break; case ESM::PRT_Groin: //4 - groin = insertBoundedPart(mesh, "Groin"); + groin = insertBoundedPart(mesh, group, "Groin"); break; case ESM::PRT_Skirt: //5 - skirt = insertBoundedPart(mesh, "Groin"); + skirt = insertBoundedPart(mesh, group, "Groin"); break; case ESM::PRT_RHand: //6 - rHand = insertBoundedPart(mesh, "Right Hand"); + rHand = insertBoundedPart(mesh, group, "Right Hand"); break; case ESM::PRT_LHand: //7 - lHand = insertBoundedPart(mesh, "Left Hand"); + lHand = insertBoundedPart(mesh, group, "Left Hand"); break; case ESM::PRT_RWrist: //8 - rWrist = insertBoundedPart(mesh, "Right Wrist"); + rWrist = insertBoundedPart(mesh, group, "Right Wrist"); break; case ESM::PRT_LWrist: //9 - lWrist = insertBoundedPart(mesh, "Left Wrist"); + lWrist = insertBoundedPart(mesh, group, "Left Wrist"); break; case ESM::PRT_Shield: //10 break; case ESM::PRT_RForearm: //11 - rForearm = insertBoundedPart(mesh, "Right Forearm"); + rForearm = insertBoundedPart(mesh, group, "Right Forearm"); break; case ESM::PRT_LForearm: //12 - lForearm = insertBoundedPart(mesh, "Left Forearm"); + lForearm = insertBoundedPart(mesh, group, "Left Forearm"); break; case ESM::PRT_RUpperarm: //13 - rupperArm = insertBoundedPart(mesh, "Right Upper Arm"); + rupperArm = insertBoundedPart(mesh, group, "Right Upper Arm"); break; case ESM::PRT_LUpperarm: //14 - lupperArm = insertBoundedPart(mesh, "Left Upper Arm"); + lupperArm = insertBoundedPart(mesh, group, "Left Upper Arm"); break; case ESM::PRT_RFoot: //15 - rfoot = insertBoundedPart(mesh, "Right Foot"); + rfoot = insertBoundedPart(mesh, group, "Right Foot"); break; case ESM::PRT_LFoot: //16 - lfoot = insertBoundedPart(mesh, "Left Foot"); + lfoot = insertBoundedPart(mesh, group, "Left Foot"); break; case ESM::PRT_RAnkle: //17 - rAnkle = insertBoundedPart(mesh, "Right Ankle"); + rAnkle = insertBoundedPart(mesh, group, "Right Ankle"); break; case ESM::PRT_LAnkle: //18 - lAnkle = insertBoundedPart(mesh, "Left Ankle"); + lAnkle = insertBoundedPart(mesh, group, "Left Ankle"); break; case ESM::PRT_RKnee: //19 - rKnee = insertBoundedPart(mesh, "Right Knee"); + rKnee = insertBoundedPart(mesh, group, "Right Knee"); break; case ESM::PRT_LKnee: //20 - lKnee = insertBoundedPart(mesh, "Left Knee"); + lKnee = insertBoundedPart(mesh, group, "Left Knee"); break; case ESM::PRT_RLeg: //21 - rUpperLeg = insertBoundedPart(mesh, "Right Upper Leg"); + rUpperLeg = insertBoundedPart(mesh, group, "Right Upper Leg"); break; case ESM::PRT_LLeg: //22 - lUpperLeg = insertBoundedPart(mesh, "Left Upper Leg"); + lUpperLeg = insertBoundedPart(mesh, group, "Left Upper Leg"); break; case ESM::PRT_RPauldron: //23 - rclavicle = insertBoundedPart(mesh , "Right Clavicle"); + rclavicle = insertBoundedPart(mesh , group, "Right Clavicle"); break; case ESM::PRT_LPauldron: //24 - lclavicle = insertBoundedPart(mesh, "Left Clavicle"); + lclavicle = insertBoundedPart(mesh, group, "Left Clavicle"); break; case ESM::PRT_Weapon: //25 break; case ESM::PRT_Tail: //26 - tail = insertBoundedPart(mesh, "Tail"); + tail = insertBoundedPart(mesh, group, "Tail"); break; } return true; @@ -575,10 +580,15 @@ void NpcAnimation::addPartGroup(int group, int priority, std::vectorgetStore().bodyParts.search(part.male); if(bodypart) - addOrReplaceIndividualPart(part.part, group,priority,"meshes\\" + bodypart->model); + addOrReplaceIndividualPart(part.part, group, priority,"meshes\\" + bodypart->model); else reserveIndividualPart(part.part, group, priority); } } +void NpcAnimation::forceUpdate () +{ + updateParts(); +} + } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index d4b2a5b9e3..21edb3be4f 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -65,10 +65,13 @@ private: MWWorld::ContainerStoreIterator rightglove; MWWorld::ContainerStoreIterator skirtiter; + int mVisibilityFlags; + public: - NpcAnimation(const MWWorld::Ptr& ptr, OEngine::Render::OgreRenderer& _rend, MWWorld::InventoryStore& _inv); + NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, + MWWorld::InventoryStore& _inv, int visibilityFlags); virtual ~NpcAnimation(); - NifOgre::EntityList insertBoundedPart(const std::string &mesh, const std::string &bonename); + NifOgre::EntityList insertBoundedPart(const std::string &mesh, int group, const std::string &bonename); virtual void runAnimation(float timepassed); void updateParts(); void removeEntities(NifOgre::EntityList &entities); @@ -78,6 +81,8 @@ public: bool addOrReplaceIndividualPart(int type, int group, int priority, const std::string &mesh); void removePartGroup(int group); void addPartGroup(int group, int priority, std::vector& parts); + + void forceUpdate(); }; } diff --git a/apps/openmw/mwrender/player.cpp b/apps/openmw/mwrender/player.cpp index 3ccc080221..297cb8d7a3 100644 --- a/apps/openmw/mwrender/player.cpp +++ b/apps/openmw/mwrender/player.cpp @@ -35,6 +35,11 @@ namespace MWRender mPreviewCam.yaw = 0.f; mPreviewCam.offset = 400.f; } + + Player::~Player() + { + delete mAnimation; + } bool Player::rotate(const Ogre::Vector3 &rot, bool adjust) { diff --git a/apps/openmw/mwrender/player.hpp b/apps/openmw/mwrender/player.hpp index 8dd313b7fa..b4d8983e4e 100644 --- a/apps/openmw/mwrender/player.hpp +++ b/apps/openmw/mwrender/player.hpp @@ -63,6 +63,7 @@ namespace MWRender public: Player (Ogre::Camera *camera, Ogre::SceneNode* mNode); + ~Player(); /// Set where the player is looking at. Uses Morrowind (euler) angles /// \param rot Rotation angles in radians diff --git a/apps/openmw/mwrender/renderconst.hpp b/apps/openmw/mwrender/renderconst.hpp index 9f57833bb7..e6ecb51504 100644 --- a/apps/openmw/mwrender/renderconst.hpp +++ b/apps/openmw/mwrender/renderconst.hpp @@ -41,7 +41,7 @@ enum VisibilityFlags // Water RV_Water = 8, - // Actors (player, npcs, creatures) + // Actors (npcs, creatures) RV_Actors = 16, // Misc objects (containers, dynamic objects) @@ -54,6 +54,8 @@ enum VisibilityFlags RV_OcclusionQuery = 256, + RV_PlayerPreview = 512, + RV_Map = RV_Terrain + RV_Statics + RV_StaticsSmall + RV_Misc + RV_Water /// \todo markers (normally hidden) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 70d7de5529..308a016888 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -34,6 +34,7 @@ #include "water.hpp" #include "compositors.hpp" #include "npcanimation.hpp" +#include "externalrendering.hpp" using namespace MWRender; using namespace Ogre; @@ -94,7 +95,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const if (filter == "anisotropic") tfo = TFO_ANISOTROPIC; else if (filter == "trilinear") tfo = TFO_TRILINEAR; else if (filter == "bilinear") tfo = TFO_BILINEAR; - else if (filter == "none") tfo = TFO_NONE; + else /*if (filter == "none")*/ tfo = TFO_NONE; MaterialManager::getSingleton().setDefaultTextureFiltering(tfo); MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 ); @@ -137,7 +138,7 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const // Morrowind uses, and it automagically makes everything work as it // should. SceneNode *rt = mRendering.getScene()->getRootSceneNode(); - mMwRoot = rt->createChildSceneNode(); + mMwRoot = rt->createChildSceneNode("mwRoot"); mMwRoot->pitch(Degree(-90)); mObjects.setMwRoot(mMwRoot); @@ -723,7 +724,7 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec if (filter == "anisotropic") tfo = TFO_ANISOTROPIC; else if (filter == "trilinear") tfo = TFO_TRILINEAR; else if (filter == "bilinear") tfo = TFO_BILINEAR; - else if (filter == "none") tfo = TFO_NONE; + else /*if (filter == "none")*/ tfo = TFO_NONE; MaterialManager::getSingleton().setDefaultTextureFiltering(tfo); MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 ); @@ -858,9 +859,8 @@ void RenderingManager::renderPlayer(const MWWorld::Ptr &ptr) { MWRender::NpcAnimation *anim = new MWRender::NpcAnimation( - ptr, - mRendering, - MWWorld::Class::get(ptr).getInventoryStore(ptr) + ptr, ptr.getRefData ().getBaseNode (), + MWWorld::Class::get(ptr).getInventoryStore(ptr), RV_Actors ); mPlayer->setAnimation(anim); } @@ -882,4 +882,9 @@ bool RenderingManager::isPositionExplored (float nX, float nY, int x, int y, boo return mLocalMap->isPositionExplored(nX, nY, x, y, interior); } +void RenderingManager::setupExternalRendering (MWRender::ExternalRendering& rendering) +{ + rendering.setup (mRendering.getScene()); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index e5be238bd4..e994e4909c 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -43,6 +43,7 @@ namespace MWRender class LocalMap; class Water; class Compositors; + class ExternalRendering; class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener { @@ -185,12 +186,14 @@ class RenderingManager: private RenderingInterface, public Ogre::WindowEventList static bool waterShaderSupported(); - virtual void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); + void getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y); ///< see MWRender::LocalMap::getInteriorMapPosition - virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior); + bool isPositionExplored (float nX, float nY, int x, int y, bool interior); ///< see MWRender::LocalMap::isPositionExplored + void setupExternalRendering (MWRender::ExternalRendering& rendering); + protected: virtual void windowResized(Ogre::RenderWindow* rw); virtual void windowClosed(Ogre::RenderWindow* rw); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 59b9bca35b..8052c6a082 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -183,4 +183,8 @@ op 0x2000172: GetStartingAngle op 0x2000173: GetStartingAngle, explicit reference op 0x2000174: ToggleVanityMode op 0x2000175-0x200018B: Get controls disabled -opcodes 0x200018C-0x3ffffff unused +op 0x200018C: GetLevel +op 0x200018D: GetLevel, explicit reference +op 0x200018E: SetLevel +op 0x200018F: SetLevel, explicit reference +opcodes 0x200018F-0x3ffffff unused diff --git a/apps/openmw/mwscript/guiextensions.cpp b/apps/openmw/mwscript/guiextensions.cpp index d740e5feba..7f3b9777b0 100644 --- a/apps/openmw/mwscript/guiextensions.cpp +++ b/apps/openmw/mwscript/guiextensions.cpp @@ -30,6 +30,16 @@ namespace MWScript } }; + class OpEnableRest : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::Environment::get().getWindowManager()->enableRest(); + } + }; + class OpShowDialogue : public Interpreter::Opcode0 { MWGui::GuiMode mDialogue; @@ -146,12 +156,8 @@ opcodeEnableStatsReviewMenu); interpreter.installSegment5 (opcodeEnableStatsMenu, new OpEnableWindow (MWGui::GW_Stats)); - /* Not done yet. Enabling rest mode is not really a gui - issue, it's a gameplay issue. - interpreter.installSegment5 (opcodeEnableRest, - new OpEnableDialogue (MWGui::GM_Rest)); - */ + new OpEnableRest ()); interpreter.installSegment5 (opcodeShowRestMenu, new OpShowDialogue (MWGui::GM_Rest)); diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 5113c9dbff..f49d684a5d 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -32,6 +32,42 @@ namespace MWScript { namespace Stats { + template + class OpGetLevel : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Integer value = + MWWorld::Class::get (ptr) + .getCreatureStats (ptr) + .getLevel(); + + runtime.push (value); + } + }; + + template + class OpSetLevel : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWWorld::Ptr ptr = R()(runtime); + + Interpreter::Type_Integer value = runtime[0].mInteger; + runtime.pop(); + + MWWorld::Class::get (ptr) + .getCreatureStats (ptr) + .setLevel(value); + } + }; + template class OpGetAttribute : public Interpreter::Opcode0 { @@ -592,6 +628,11 @@ namespace MWScript const int opcodeModDisposition = 0x200014d; const int opcodeModDispositionExplicit = 0x200014e; + const int opcodeGetLevel = 0x200018c; + const int opcodeGetLevelExplicit = 0x200018d; + const int opcodeSetLevel = 0x200018e; + const int opcodeSetLevelExplicit = 0x200018f; + void registerExtensions (Compiler::Extensions& extensions) { static const char *attributes[numberOfAttributes] = @@ -674,6 +715,9 @@ namespace MWScript extensions.registerInstruction("moddisposition","l",opcodeModDisposition, opcodeModDispositionExplicit); extensions.registerFunction("getpcrank",'l',"/S",opcodeGetPCRank,opcodeGetPCRankExplicit); + + extensions.registerInstruction("setlevel", "l", opcodeSetLevel, opcodeSetLevelExplicit); + extensions.registerFunction("getlevel", 'l', "", opcodeGetLevel, opcodeGetLevelExplicit); } void installOpcodes (Interpreter::Interpreter& interpreter) @@ -745,6 +789,12 @@ namespace MWScript interpreter.installSegment5(opcodeModDispositionExplicit,new OpModDisposition); interpreter.installSegment3(opcodeGetPCRank,new OpGetPCRank); interpreter.installSegment3(opcodeGetPCRankExplicit,new OpGetPCRank); + + interpreter.installSegment5 (opcodeGetLevel, new OpGetLevel); + interpreter.installSegment5 (opcodeGetLevelExplicit, new OpGetLevel); + interpreter.installSegment5 (opcodeSetLevel, new OpSetLevel); + interpreter.installSegment5 (opcodeSetLevelExplicit, new OpSetLevel); + } } } diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 7fb2b23e70..fe5e2d58fb 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -2,10 +2,18 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" + +#include "../mwworld/player.hpp" +#include "../mwworld/class.hpp" + +#include "../mwmechanics/npcstats.hpp" #include "../mwgui/bookwindow.hpp" #include "../mwgui/scrollwindow.hpp" +#include + namespace MWWorld { ActionRead::ActionRead (const MWWorld::Ptr& object) : Action (false, object) @@ -26,5 +34,23 @@ namespace MWWorld MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Book); MWBase::Environment::get().getWindowManager()->getBookWindow()->open(getTarget()); } + + /* + // Skill gain from books + if (ref->base->data.skillID >= 0 && ref->base->data.skillID < ESM::Skill::Length) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer(); + MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats (player); + MWWorld::LiveCellRef *playerRef = player.get(); + const ESM::Class *class_ = MWBase::Environment::get().getWorld()->getStore().classes.find ( + playerRef->base->cls); + + npcStats.increaseSkill (ref->base->data.skillID, *class_, true); + + /// \todo Remove skill from the book. Right now you can read as many times as you want + /// and the skill will still increase. + } + */ + } } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 869ce94d1d..6c92678365 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1222,4 +1222,9 @@ namespace MWWorld { mRendering->renderPlayer(mPlayer->getPlayer()); } + + void World::setupExternalRendering (MWRender::ExternalRendering& rendering) + { + mRendering->setupExternalRendering (rendering); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 6279343d41..96e6ae3f81 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -307,6 +307,8 @@ namespace MWWorld } virtual void renderPlayer(); + + virtual void setupExternalRendering (MWRender::ExternalRendering& rendering); }; } diff --git a/extern/shiny b/extern/shiny index 7168415233..4750676ac4 160000 --- a/extern/shiny +++ b/extern/shiny @@ -1 +1 @@ -Subproject commit 7168415233905de2864eec71ed4312cb8f83059b +Subproject commit 4750676ac46a7aaa86bca53dc68c5a1ba11f3bc1 diff --git a/files/CMakeLists.txt b/files/CMakeLists.txt index 3e99f57455..e8426afb70 100644 --- a/files/CMakeLists.txt +++ b/files/CMakeLists.txt @@ -40,7 +40,9 @@ set(MATERIAL_FILES water.mat water.shader water.shaderset - + selection.mat + selection.shader + selection.shaderset ) copy_all_files(${CMAKE_CURRENT_SOURCE_DIR}/water "${OpenMW_BINARY_DIR}/resources/water/" "${WATER_FILES}") diff --git a/files/materials/selection.mat b/files/materials/selection.mat new file mode 100644 index 0000000000..a76dd71792 --- /dev/null +++ b/files/materials/selection.mat @@ -0,0 +1,8 @@ +material SelectionColour +{ + pass + { + vertex_program selection_vertex + fragment_program selection_fragment + } +} diff --git a/files/materials/selection.shader b/files/materials/selection.shader new file mode 100644 index 0000000000..095a31259d --- /dev/null +++ b/files/materials/selection.shader @@ -0,0 +1,24 @@ +#include "core.h" + +#ifdef SH_VERTEX_SHADER + + SH_BEGIN_PROGRAM + shUniform(float4x4, wvp) @shAutoConstant(wvp, worldviewproj_matrix) + + SH_START_PROGRAM + { + shOutputPosition = shMatrixMult(wvp, shInputPosition); + } + +#else + + SH_BEGIN_PROGRAM + shUniform(float4, colour) @shAutoConstant(colour, custom, 1) + + SH_START_PROGRAM + { + shOutputColour(0) = colour; + //shOutputColour(0) = float4(1,0,0,1); + } + +#endif diff --git a/files/materials/selection.shaderset b/files/materials/selection.shaderset new file mode 100644 index 0000000000..c90826282e --- /dev/null +++ b/files/materials/selection.shaderset @@ -0,0 +1,15 @@ +shader_set selection_vertex +{ + source selection.shader + type vertex + profiles_cg vs_2_0 arbvp1 + profiles_hlsl vs_2_0 +} + +shader_set selection_fragment +{ + source selection.shader + type fragment + profiles_cg ps_2_x ps_2_0 ps arbfp1 + profiles_hlsl ps_2_0 +} diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index fdf447e690..55230fa76a 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -35,7 +35,7 @@ set(MYGUI_FILES openmw_dialogue_window.layout openmw_dialogue_window_skin.xml openmw_edit.skin.xml - openmw.font.xml + openmw_font.xml openmw_hud_box.skin.xml openmw_hud_energybar.skin.xml openmw_hud.layout @@ -51,7 +51,7 @@ set(MYGUI_FILES openmw_map_window.layout openmw_map_window_skin.xml openmw_messagebox.layout - openmw.pointer.xml + openmw_pointer.xml openmw_progress.skin.xml openmw_resources.xml openmw_scroll.layout @@ -72,6 +72,7 @@ set(MYGUI_FILES openmw_magicselection_dialog.layout openmw_spell_buying_window.layout openmw_loading_screen.layout + openmw_levelup_dialog.layout smallbars.png VeraMono.ttf markers.png diff --git a/files/mygui/core.xml b/files/mygui/core.xml index e1fb1b5e21..ea16278757 100644 --- a/files/mygui/core.xml +++ b/files/mygui/core.xml @@ -4,8 +4,8 @@ - - + + diff --git a/files/mygui/openmw_chargen_race.layout b/files/mygui/openmw_chargen_race.layout index 3d32ffd9a9..b690738992 100644 --- a/files/mygui/openmw_chargen_race.layout +++ b/files/mygui/openmw_chargen_race.layout @@ -8,11 +8,13 @@ - + + + - + diff --git a/files/mygui/openmw.font.xml b/files/mygui/openmw_font.xml similarity index 100% rename from files/mygui/openmw.font.xml rename to files/mygui/openmw_font.xml diff --git a/files/mygui/openmw_inventory_window.layout b/files/mygui/openmw_inventory_window.layout index 1ffabe3b6f..3a60916f7f 100644 --- a/files/mygui/openmw_inventory_window.layout +++ b/files/mygui/openmw_inventory_window.layout @@ -12,8 +12,13 @@ + + + + + diff --git a/files/mygui/openmw_levelup_dialog.layout b/files/mygui/openmw_levelup_dialog.layout new file mode 100644 index 0000000000..86e65e99a9 --- /dev/null +++ b/files/mygui/openmw_levelup_dialog.layout @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw.pointer.xml b/files/mygui/openmw_pointer.xml similarity index 100% rename from files/mygui/openmw.pointer.xml rename to files/mygui/openmw_pointer.xml diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 9251e5683e..8cff0f9e00 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -215,7 +215,7 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& mWindow = mRoot->createRenderWindow(title, settings.window_x, settings.window_y, settings.fullscreen, ¶ms); // create the semi-transparent black background texture used by the GUI. - // has to be created in code with TU_DYNAMIC_WRITE_ONLY_DISCARDABLE param + // has to be created in code with TU_DYNAMIC_WRITE_ONLY param // so that it can be modified at runtime. Ogre::TextureManager::getSingleton().createManual( "transparent.png", @@ -224,7 +224,7 @@ void OgreRenderer::createWindow(const std::string &title, const WindowSettings& 1, 1, 0, Ogre::PF_A8R8G8B8, - Ogre::TU_DYNAMIC_WRITE_ONLY_DISCARDABLE); + Ogre::TU_DYNAMIC_WRITE_ONLY); } void OgreRenderer::createScene(const std::string& camName, float fov, float nearClip) diff --git a/libs/openengine/ogre/selectionbuffer.cpp b/libs/openengine/ogre/selectionbuffer.cpp new file mode 100644 index 0000000000..c6b43a45de --- /dev/null +++ b/libs/openengine/ogre/selectionbuffer.cpp @@ -0,0 +1,123 @@ +#include "selectionbuffer.hpp" + +#include +#include +#include +#include +#include + +#include + +namespace OEngine +{ +namespace Render +{ + + SelectionBuffer::SelectionBuffer(Ogre::Camera *camera, int sizeX, int sizeY, int visibilityFlags) + { + mTexture = Ogre::TextureManager::getSingleton().createManual("SelectionBuffer", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, sizeX, sizeY, 0, Ogre::PF_R8G8B8, Ogre::TU_RENDERTARGET); + + mRenderTarget = mTexture->getBuffer()->getRenderTarget(); + Ogre::Viewport* vp = mRenderTarget->addViewport(camera); + vp->setOverlaysEnabled(false); + vp->setBackgroundColour(Ogre::ColourValue(0, 0, 0, 0)); + vp->setShadowsEnabled(false); + vp->setMaterialScheme("selectionbuffer"); + vp->setVisibilityMask (visibilityFlags); + mRenderTarget->setActive(true); + mRenderTarget->setAutoUpdated (false); + + mCurrentColour = Ogre::ColourValue(0.3, 0.3, 0.3); + } + + SelectionBuffer::~SelectionBuffer() + { + Ogre::TextureManager::getSingleton ().remove("SelectionBuffer"); + } + + void SelectionBuffer::update () + { + Ogre::MaterialManager::getSingleton ().addListener (this); + + mRenderTarget->update(); + + Ogre::MaterialManager::getSingleton ().removeListener (this); + + mTexture->convertToImage(mBuffer); + } + + int SelectionBuffer::getSelected(int xPos, int yPos) + { + Ogre::ColourValue clr = mBuffer.getColourAt (xPos, yPos, 0); + clr.a = 1; + if (mColourMap.find(clr) != mColourMap.end()) + return mColourMap[clr]; + else + return -1; // nothing selected + } + + Ogre::Technique* SelectionBuffer::handleSchemeNotFound ( + unsigned short schemeIndex, const Ogre::String &schemeName, Ogre::Material *originalMaterial, + unsigned short lodIndex, const Ogre::Renderable *rend) + { + if (schemeName == "selectionbuffer") + { + sh::Factory::getInstance ()._ensureMaterial ("SelectionColour", "Default"); + + Ogre::MaterialPtr m = Ogre::MaterialManager::getSingleton ().getByName("SelectionColour"); + + + if(typeid(*rend) == typeid(Ogre::SubEntity)) + { + const Ogre::SubEntity *subEntity = static_cast(rend); + int id = Ogre::any_cast(subEntity->getParent ()->getUserObjectBindings().getUserAny()); + bool found = false; + Ogre::ColourValue colour; + for (std::map::iterator it = mColourMap.begin(); it != mColourMap.end(); ++it) + { + if (it->second == id) + { + found = true; + colour = it->first; + } + } + + + if (!found) + { + getNextColour(); + const_cast(subEntity)->setCustomParameter(1, Ogre::Vector4(mCurrentColour.r, mCurrentColour.g, mCurrentColour.b, 1.0)); + mColourMap[mCurrentColour] = id; + } + else + { + const_cast(subEntity)->setCustomParameter(1, Ogre::Vector4(colour.r, colour.g, colour.b, 1.0)); + } + + assert(m->getTechnique(1)); + return m->getTechnique(1); + } + else + throw std::runtime_error("selectionbuffer only works with entities"); + } + return NULL; + } + + void SelectionBuffer::getNextColour () + { + Ogre::ARGB color = (float(rand()) / float(RAND_MAX)) * std::numeric_limits::max(); + + if (mCurrentColour.getAsARGB () == color) + { + getNextColour(); + return; + } + + mCurrentColour.setAsARGB(color); + mCurrentColour.a = 1; + } + + +} +} diff --git a/libs/openengine/ogre/selectionbuffer.hpp b/libs/openengine/ogre/selectionbuffer.hpp new file mode 100644 index 0000000000..c487b24b04 --- /dev/null +++ b/libs/openengine/ogre/selectionbuffer.hpp @@ -0,0 +1,54 @@ +#ifndef OENGINE_SELECTIONBUFFER_H +#define OENGINE_SELECTIONBUFFER_H + + +#include +#include +#include + +namespace OEngine +{ +namespace Render +{ + + struct cmp_ColourValue + { + bool operator()(const Ogre::ColourValue &a, const Ogre::ColourValue &b) const + { + return a.getAsBGRA() < b.getAsBGRA(); + } + }; + + class SelectionBuffer : public Ogre::MaterialManager::Listener + { + public: + SelectionBuffer(Ogre::Camera* camera, int sizeX, int sizeY, int visibilityFlags); + virtual ~SelectionBuffer(); + + int getSelected(int xPos, int yPos); + ///< @return ID of the selected object + + void update(); + + virtual Ogre::Technique* handleSchemeNotFound ( + unsigned short schemeIndex, const Ogre::String &schemeName, Ogre::Material *originalMaterial, + unsigned short lodIndex, const Ogre::Renderable *rend); + + + private: + Ogre::TexturePtr mTexture; + Ogre::RenderTexture* mRenderTarget; + + Ogre::Image mBuffer; + + std::map mColourMap; + + Ogre::ColourValue mCurrentColour; + + void getNextColour(); + }; + +} +} + +#endif