diff --git a/.travis.yml b/.travis.yml index d3b54b1797..80277f8ba0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,8 +18,8 @@ addons: name: "OpenMW/openmw" description: "" notification_email: scrawl@baseoftrash.de - build_command_prepend: "cmake ." - build_command: "make -j3" + build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE" + build_command: "make" branch_pattern: coverity_scan matrix: include: diff --git a/README.md b/README.md index f62800e1fd..5b986e0075 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,9 @@ OpenMW [![Build Status](https://img.shields.io/travis/OpenMW/openmw.svg)](https://travis-ci.org/OpenMW/openmw) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3740/badge.svg)](https://scan.coverity.com/projects/3740) -OpenMW is an attempt at recreating the engine for the popular role-playing game -Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. +OpenMW is a recreation of the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. + +OpenMW also comes with OpenMW-CS, a replacement for Morrowind's TES Construction Set. * Version: 0.36.0 * License: GPL (see docs/license/GPL3.txt for more information) @@ -14,6 +15,13 @@ Morrowind by Bethesda Softworks. You need to own and install the original game f Font Licenses: * DejaVuLGCSansMono.ttf: custom (see docs/license/DejaVu Font License.txt for more information) +Current Status +-------------- + +The main quests in Morrowind, Tribunal and Bloodmoon are all completable. Some issues with side quests are to be expected (but rare). Check the [bug tracker](https://bugs.openmw.org/versions/21) for a list of issues we need to resolve before the "1.0" release. Even before the "1.0" release however, OpenMW boasts some new [features](https://wiki.openmw.org/index.php?title=Features), such as improved graphics and user interfaces. + +Pre-existing modifications created for the original Morrowind engine can be hit-and-miss. The OpenMW script compiler performs more thorough error-checking than Morrowind does, meaning that a mod created for Morrowind may not necessarily run in OpenMW. Some mods also rely on quirky behaviour or engine bugs in order to work. We are considering such compatibility issues on a case-by-case basis - in some cases adding a workaround to OpenMW may be feasible, in other cases fixing the mod will be the only option. If you know of any mods that work or don't work, feel free to add them to the [Mod status](https://wiki.openmw.org/index.php?title=Mod_status) wiki page. + Getting Started --------------- diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 32ad1816cf..624241039d 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -166,7 +166,9 @@ namespace ESSImport if (i >= file2.mRecords.size()) { + std::ios::fmtflags f(std::cout.flags()); std::cout << "Record in file1 not present in file2: (1) 0x" << std::hex << rec.mFileOffset << std::endl; + std::cout.flags(f); return; } @@ -174,7 +176,9 @@ namespace ESSImport if (rec.mName != rec2.mName) { + std::ios::fmtflags f(std::cout.flags()); std::cout << "Different record name at (2) 0x" << std::hex << rec2.mFileOffset << std::endl; + std::cout.flags(f); return; // TODO: try to recover } @@ -185,7 +189,9 @@ namespace ESSImport if (j >= rec2.mSubrecords.size()) { + std::ios::fmtflags f(std::cout.flags()); std::cout << "Subrecord in file1 not present in file2: (1) 0x" << std::hex << sub.mFileOffset << std::endl; + std::cout.flags(f); return; } @@ -193,8 +199,10 @@ namespace ESSImport if (sub.mName != sub2.mName) { + std::ios::fmtflags f(std::cout.flags()); std::cout << "Different subrecord name (" << rec.mName << "." << sub.mName << " vs. " << sub2.mName << ") at (1) 0x" << std::hex << sub.mFileOffset << " (2) 0x" << sub2.mFileOffset << std::endl; + std::cout.flags(f); break; // TODO: try to recover } @@ -203,6 +211,8 @@ namespace ESSImport if (blacklist.find(std::make_pair(rec.mName, sub.mName)) != blacklist.end()) continue; + std::ios::fmtflags f(std::cout.flags()); + std::cout << "Different subrecord data for " << rec.mName << "." << sub.mName << " at (1) 0x" << std::hex << sub.mFileOffset << " (2) 0x" << sub2.mFileOffset << std::endl; @@ -235,6 +245,7 @@ namespace ESSImport std::cout << "\033[0m"; } std::cout << std::endl; + std::cout.flags(f); } } } @@ -319,7 +330,11 @@ namespace ESSImport else { if (unknownRecords.insert(n.val).second) + { + std::ios::fmtflags f(std::cerr.flags()); std::cerr << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl; + std::cerr.flags(f); + } esm.skipRecord(); } diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index 3b010cb8ff..c93dff269d 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -49,6 +49,10 @@ namespace ESSImport std::map mNpcs; Context() + : mDay(0) + , mMonth(0) + , mYear(0) + , mHour(0.f) { mPlayer.mAutoMove = 0; ESM::CellId playerCellId; diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index adb7d38923..29fac1c673 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -203,6 +203,8 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c return false; // script does not have a variable of this name. int index = localDefs.getIndex (name); + if (index < 0) + return false; // shouldn't happen, we checked that variable has a type above, so must exist const MWScript::Locals& locals = mActor.getRefData().getLocals(); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 9cf9575227..a2a8261617 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -235,8 +235,9 @@ namespace MWGui MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged); + // Create all cursors in advance + createCursors(); onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer()); - mCursorManager->setEnabled(true); // hide mygui's pointer @@ -896,6 +897,9 @@ namespace MWGui void WindowManager::updateMap() { + if (!mLocalMapRender) + return; + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); osg::Vec3f playerPosition = player.getRefData().getPosition().asVec3(); @@ -1178,31 +1182,7 @@ namespace MWGui void WindowManager::onCursorChange(const std::string &name) { - if(!mCursorManager->cursorChanged(name)) - return; //the cursor manager doesn't want any more info about this cursor - //See if we can get the information we need out of the cursor resource - ResourceImageSetPointerFix* imgSetPtr = dynamic_cast(MyGUI::PointerManager::getInstance().getByName(name)); - if(imgSetPtr != NULL) - { - MyGUI::ResourceImageSet* imgSet = imgSetPtr->getImageSet(); - - std::string tex_name = imgSet->getIndexInfo(0,0).texture; - - osg::ref_ptr tex = mResourceSystem->getTextureManager()->getTexture2D(tex_name, osg::Texture::CLAMP, osg::Texture::CLAMP); - tex->setUnRefImageDataAfterApply(false); // FIXME? - - //everything looks good, send it to the cursor manager - if(tex.valid()) - { - Uint8 size_x = imgSetPtr->getSize().width; - Uint8 size_y = imgSetPtr->getSize().height; - Uint8 hotspot_x = imgSetPtr->getHotSpot().left; - Uint8 hotspot_y = imgSetPtr->getHotSpot().top; - int rotation = imgSetPtr->getRotation(); - - mCursorManager->receiveCursorInfo(name, rotation, tex->getImage(), size_x, size_y, hotspot_x, hotspot_y); - } - } + mCursorManager->cursorChanged(name); } void WindowManager::popGuiMode() @@ -1960,6 +1940,33 @@ namespace MWGui return Misc::ResourceHelpers::correctTexturePath(path, mResourceSystem->getVFS()); } + void WindowManager::createCursors() + { + MyGUI::ResourceManager::EnumeratorPtr enumerator = MyGUI::ResourceManager::getInstance().getEnumerator(); + while (enumerator.next()) + { + MyGUI::IResource* resource = enumerator.current().second; + ResourceImageSetPointerFix* imgSetPointer = dynamic_cast(resource); + if (!imgSetPointer) + continue; + std::string tex_name = imgSetPointer->getImageSet()->getIndexInfo(0,0).texture; + + osg::ref_ptr tex = mResourceSystem->getTextureManager()->getTexture2D(tex_name, osg::Texture::CLAMP, osg::Texture::CLAMP); + + if(tex.valid()) + { + //everything looks good, send it to the cursor manager + Uint8 size_x = imgSetPointer->getSize().width; + Uint8 size_y = imgSetPointer->getSize().height; + Uint8 hotspot_x = imgSetPointer->getHotSpot().left; + Uint8 hotspot_y = imgSetPointer->getHotSpot().top; + int rotation = imgSetPointer->getRotation(); + + mCursorManager->createCursor(imgSetPointer->getResourceName(), rotation, tex->getImage(), size_x, size_y, hotspot_x, hotspot_y); + } + } + } + void WindowManager::createTextures() { { diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index c275a9f626..e6c8d0a817 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -511,6 +511,7 @@ namespace MWGui void onClipboardRequested(const std::string& _type, std::string& _data); void createTextures(); + void createCursors(); void setMenuTransparency(float value); }; } diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index 4aef8f8ba4..1375b5807a 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -117,7 +117,7 @@ namespace MWMechanics mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); - mStoredAvailableNodes = false; + mPopulateAvailableNodes = true; } @@ -191,7 +191,7 @@ namespace MWMechanics if(!currentCell || cellChange) { currentCell = actor.getCell(); - mStoredAvailableNodes = false; // prob. not needed since mDistance = 0 + mPopulateAvailableNodes = true; } cStats.setDrawState(DrawState_Nothing); @@ -225,8 +225,6 @@ namespace MWMechanics storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], DESTINATION_TOLERANCE)) { stopWalking(actor, storage); - moveNow = false; - walking = false; chooseAction = true; mHasReturnPosition = false; } @@ -239,45 +237,7 @@ namespace MWMechanics zTurn(actor, osg::DegreesToRadians(storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); actor.getClass().getMovementSettings(actor).mPosition[1] = 1; - // Returns true if evasive action needs to be taken - if(mObstacleCheck.check(actor, duration)) - { - // first check if we're walking into a door - if(proximityToDoor(actor)) // NOTE: checks interior cells only - { - // remove allowed points then select another random destination - mTrimCurrentNode = true; - trimAllowedNodes(mAllowedNodes, storage.mPathFinder); - mObstacleCheck.clear(); - storage.mPathFinder.clearPath(); - walking = false; - moveNow = true; - } - else // probably walking into another NPC - { - // TODO: diagonal should have same animation as walk forward - // but doesn't seem to do that? - actor.getClass().getMovementSettings(actor).mPosition[0] = 1; - actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f; - // change the angle a bit, too - zTurn(actor, osg::DegreesToRadians(storage.mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1]))); - } - mStuckCount++; // TODO: maybe no longer needed - } -//#if 0 - // TODO: maybe no longer needed - if(mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset - { - //std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl; - mObstacleCheck.clear(); - - stopWalking(actor, storage); - moveNow = false; - walking = false; - chooseAction = true; - mStuckCount = 0; - } -//#endif + evadeObstacles(actor, storage, duration); } @@ -325,32 +285,7 @@ namespace MWMechanics } } - // Play idle voiced dialogue entries randomly - int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); - if (hello > 0 && !MWBase::Environment::get().getWorld()->isSwimming(actor) - && MWBase::Environment::get().getSoundManager()->sayDone(actor)) - { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - - static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore() - .get().find("fVoiceIdleOdds")->getFloat(); - - float roll = Misc::Rng::rollProbability() * 10000.0f; - - // In vanilla MW the chance was FPS dependent, and did not allow proper changing of fVoiceIdleOdds - // due to the roll being an integer. - // Our implementation does not have these issues, so needs to be recalibrated. We chose to - // use the chance MW would have when run at 60 FPS with the default value of the GMST for calibration. - float x = fVoiceIdleOdds * 0.6f * (MWBase::Environment::get().getFrameDuration() / 0.1f); - - // Only say Idle voices when player is in LOS - // A bit counterintuitive, likely vanilla did this to reduce the appearance of - // voices going through walls? - if (roll < x && (player.getRefData().getPosition().asVec3() - pos.asVec3()).length2() - < 3000*3000 // maybe should be fAudioVoiceDefaultMaxDistance*fAudioMaxDistanceMult instead - && MWBase::Environment::get().getWorld()->getLOS(player, actor)) - MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); - } + playIdleDialogueRandomly(actor); float& lastReaction = storage.mReaction; lastReaction += duration; @@ -367,17 +302,8 @@ namespace MWMechanics { // End package if duration is complete or mid-night hits: MWWorld::TimeStamp currentTime = world->getTimeStamp(); - if(currentTime.getHour() >= mStartTime.getHour() + mDuration) - { - if(!mRepeat) - { - stopWalking(actor, storage); - return true; - } - else - mStartTime = currentTime; - } - else if(int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay()) + if((currentTime.getHour() >= mStartTime.getHour() + mDuration) || + (int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay())) { if(!mRepeat) { @@ -390,7 +316,7 @@ namespace MWMechanics } // Initialization to discover & store allowed node points for this actor. - if(!mStoredAvailableNodes) + if (mPopulateAvailableNodes) { getAllowedNodes(actor, currentCell->getCell()); } @@ -432,71 +358,7 @@ namespace MWMechanics // Allow interrupting a walking actor to trigger a greeting if(idleNow || walking) { - // Play a random voice greeting if the player gets too close - int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified(); - float helloDistance = static_cast(hello); - static int iGreetDistanceMultiplier =MWBase::Environment::get().getWorld()->getStore() - .get().find("iGreetDistanceMultiplier")->getInt(); - - helloDistance *= iGreetDistanceMultiplier; - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - osg::Vec3f playerPos(player.getRefData().getPosition().asVec3()); - osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); - float playerDistSqr = (playerPos - actorPos).length2(); - - int& greetingTimer = storage.mGreetingTimer; - if (greetingState == Greet_None) - { - if ((playerDistSqr <= helloDistance*helloDistance) && - !player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor) - && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor)) - greetingTimer++; - - if (greetingTimer >= GREETING_SHOULD_START) - { - greetingState = Greet_InProgress; - MWBase::Environment::get().getDialogueManager()->say(actor, "hello"); - greetingTimer = 0; - } - } - - if(greetingState == Greet_InProgress) - { - greetingTimer++; - - if(walking) - { - stopWalking(actor, storage); - moveNow = false; - walking = false; - mObstacleCheck.clear(); - idleNow = true; - getRandomIdle(playedIdle); - } - - if(!rotate) - { - osg::Vec3f dir = playerPos - actorPos; - - float faceAngleRadians = std::atan2(dir.x(), dir.y()); - targetAngleRadians = faceAngleRadians; - rotate = true; - } - - if (greetingTimer >= GREETING_SHOULD_END) - { - greetingState = Greet_Done; - greetingTimer = 0; - } - } - - if (greetingState == MWMechanics::AiWander::Greet_Done) - { - float resetDist = 2*helloDistance; - if (playerDistSqr >= resetDist*resetDist) - greetingState = Greet_None; - } + playGreetingIfPlayerGetsTooClose(actor, storage); } if(moveNow && mDistance) @@ -504,41 +366,187 @@ namespace MWMechanics // Construct a new path if there isn't one if(!storage.mPathFinder.isPathConstructed()) { - assert(mAllowedNodes.size()); - unsigned int randNode = Misc::Rng::rollDice(mAllowedNodes.size()); - ESM::Pathgrid::Point dest(mAllowedNodes[randNode]); - ToWorldCoordinates(dest, currentCell->getCell()); - - // actor position is already in world co-ordinates - ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(pos)); - - // don't take shortcuts for wandering - storage.mPathFinder.buildSyncedPath(start, dest, actor.getCell(), false); - - if(storage.mPathFinder.isPathConstructed()) + if (mAllowedNodes.size()) { - // Remove this node as an option and add back the previously used node (stops NPC from picking the same node): - ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; - mAllowedNodes.erase(mAllowedNodes.begin() + randNode); - // check if mCurrentNode was taken out of mAllowedNodes - if(mTrimCurrentNode && mAllowedNodes.size() > 1) - mTrimCurrentNode = false; - else - mAllowedNodes.push_back(mCurrentNode); - mCurrentNode = temp; - - moveNow = false; - walking = true; + setPathToAnAllowedNode(actor, storage, pos); } - // Choose a different node and delete this one from possible nodes because it is uncreachable: - else - mAllowedNodes.erase(mAllowedNodes.begin() + randNode); } } return false; // AiWander package not yet completed } + void AiWander::evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration) + { + if (mObstacleCheck.check(actor, duration)) + { + // first check if we're walking into a door + if (proximityToDoor(actor)) // NOTE: checks interior cells only + { + // remove allowed points then select another random destination + mTrimCurrentNode = true; + trimAllowedNodes(mAllowedNodes, storage.mPathFinder); + mObstacleCheck.clear(); + storage.mPathFinder.clearPath(); + storage.mWalking = false; + storage.mMoveNow = true; + } + else // probably walking into another NPC + { + // TODO: diagonal should have same animation as walk forward + // but doesn't seem to do that? + actor.getClass().getMovementSettings(actor).mPosition[0] = 1; + actor.getClass().getMovementSettings(actor).mPosition[1] = 0.1f; + // change the angle a bit, too + const ESM::Position& pos = actor.getRefData().getPosition(); + zTurn(actor, osg::DegreesToRadians(storage.mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1]))); + } + mStuckCount++; // TODO: maybe no longer needed + } +//#if 0 + // TODO: maybe no longer needed + if (mStuckCount >= COUNT_BEFORE_RESET) // something has gone wrong, reset + { + //std::cout << "Reset \""<< cls.getName(actor) << "\"" << std::endl; + mObstacleCheck.clear(); + + stopWalking(actor, storage); + storage.mChooseAction = true; + mStuckCount = 0; + } +//#endif + } + + void AiWander::playIdleDialogueRandomly(const MWWorld::Ptr& actor) + { + int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); + if (hello > 0 && !MWBase::Environment::get().getWorld()->isSwimming(actor) + && MWBase::Environment::get().getSoundManager()->sayDone(actor)) + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + + static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore() + .get().find("fVoiceIdleOdds")->getFloat(); + + float roll = Misc::Rng::rollProbability() * 10000.0f; + + // In vanilla MW the chance was FPS dependent, and did not allow proper changing of fVoiceIdleOdds + // due to the roll being an integer. + // Our implementation does not have these issues, so needs to be recalibrated. We chose to + // use the chance MW would have when run at 60 FPS with the default value of the GMST for calibration. + float x = fVoiceIdleOdds * 0.6f * (MWBase::Environment::get().getFrameDuration() / 0.1f); + + // Only say Idle voices when player is in LOS + // A bit counterintuitive, likely vanilla did this to reduce the appearance of + // voices going through walls? + const ESM::Position& pos = actor.getRefData().getPosition(); + if (roll < x && (player.getRefData().getPosition().asVec3() - pos.asVec3()).length2() + < 3000 * 3000 // maybe should be fAudioVoiceDefaultMaxDistance*fAudioMaxDistanceMult instead + && MWBase::Environment::get().getWorld()->getLOS(player, actor)) + MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); + } + } + + void AiWander::playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage) + { + // Play a random voice greeting if the player gets too close + int hello = actor.getClass().getCreatureStats(actor).getAiSetting(CreatureStats::AI_Hello).getModified(); + float helloDistance = static_cast(hello); + static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore() + .get().find("iGreetDistanceMultiplier")->getInt(); + + helloDistance *= iGreetDistanceMultiplier; + + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); + osg::Vec3f playerPos(player.getRefData().getPosition().asVec3()); + osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3()); + float playerDistSqr = (playerPos - actorPos).length2(); + + int& greetingTimer = storage.mGreetingTimer; + GreetingState& greetingState = storage.mSaidGreeting; + if (greetingState == Greet_None) + { + if ((playerDistSqr <= helloDistance*helloDistance) && + !player.getClass().getCreatureStats(player).isDead() && MWBase::Environment::get().getWorld()->getLOS(player, actor) + && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, actor)) + greetingTimer++; + + if (greetingTimer >= GREETING_SHOULD_START) + { + greetingState = Greet_InProgress; + MWBase::Environment::get().getDialogueManager()->say(actor, "hello"); + greetingTimer = 0; + } + } + + if (greetingState == Greet_InProgress) + { + greetingTimer++; + + if (storage.mWalking) + { + stopWalking(actor, storage); + mObstacleCheck.clear(); + storage.mIdleNow = true; + getRandomIdle(storage.mPlayedIdle); + } + + if (!storage.mRotate) + { + osg::Vec3f dir = playerPos - actorPos; + + float faceAngleRadians = std::atan2(dir.x(), dir.y()); + storage.mTargetAngleRadians = faceAngleRadians; + storage.mRotate = true; + } + + if (greetingTimer >= GREETING_SHOULD_END) + { + greetingState = Greet_Done; + greetingTimer = 0; + } + } + + if (greetingState == MWMechanics::AiWander::Greet_Done) + { + float resetDist = 2 * helloDistance; + if (playerDistSqr >= resetDist*resetDist) + greetingState = Greet_None; + } + } + + void AiWander::setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos) + { + unsigned int randNode = Misc::Rng::rollDice(mAllowedNodes.size()); + ESM::Pathgrid::Point dest(mAllowedNodes[randNode]); + ToWorldCoordinates(dest, storage.mCell->getCell()); + + // actor position is already in world co-ordinates + ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(actorPos)); + + // don't take shortcuts for wandering + storage.mPathFinder.buildSyncedPath(start, dest, actor.getCell(), false); + + if (storage.mPathFinder.isPathConstructed()) + { + // Remove this node as an option and add back the previously used node (stops NPC from picking the same node): + ESM::Pathgrid::Point temp = mAllowedNodes[randNode]; + mAllowedNodes.erase(mAllowedNodes.begin() + randNode); + // check if mCurrentNode was taken out of mAllowedNodes + if (mTrimCurrentNode && mAllowedNodes.size() > 1) + mTrimCurrentNode = false; + else + mAllowedNodes.push_back(mCurrentNode); + mCurrentNode = temp; + + storage.mMoveNow = false; + storage.mWalking = true; + } + // Choose a different node and delete this one from possible nodes because it is uncreachable: + else + mAllowedNodes.erase(mAllowedNodes.begin() + randNode); + } + void AiWander::ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell) { if (cell->isExterior()) @@ -583,6 +591,8 @@ namespace MWMechanics { storage.mPathFinder.clearPath(); actor.getClass().getMovementSettings(actor).mPosition[1] = 0; + storage.mMoveNow = false; + storage.mWalking = false; } void AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) @@ -640,7 +650,7 @@ namespace MWMechanics if (mDistance == 0) return; - if (!mStoredAvailableNodes) + if (mPopulateAvailableNodes) getAllowedNodes(actor, actor.getCell()->getCell()); if (mAllowedNodes.empty()) @@ -660,7 +670,7 @@ namespace MWMechanics actor.getClass().adjustPosition(actor, false); // may have changed cell - mStoredAvailableNodes = false; + mPopulateAvailableNodes = true; } int AiWander::OffsetToPreventOvercrowding() @@ -722,8 +732,9 @@ namespace MWMechanics { SetCurrentNodeToClosestAllowedNode(npcPos); } - mStoredAvailableNodes = true; // set only if successful in finding allowed nodes } + + mPopulateAvailableNodes = false; } // When only one path grid point in wander distance, diff --git a/apps/openmw/mwmechanics/aiwander.hpp b/apps/openmw/mwmechanics/aiwander.hpp index 926017b46a..82baeedf36 100644 --- a/apps/openmw/mwmechanics/aiwander.hpp +++ b/apps/openmw/mwmechanics/aiwander.hpp @@ -71,6 +71,10 @@ namespace MWMechanics void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); void getRandomIdle(unsigned short& playedIdle); + void setPathToAnAllowedNode(const MWWorld::Ptr& actor, AiWanderStorage& storage, const ESM::Position& actorPos); + void playGreetingIfPlayerGetsTooClose(const MWWorld::Ptr& actor, AiWanderStorage& storage); + void evadeObstacles(const MWWorld::Ptr& actor, AiWanderStorage& storage, float duration); + void playIdleDialogueRandomly(const MWWorld::Ptr& actor); int mDistance; // how far the actor can wander from the spawn point int mDuration; @@ -88,8 +92,8 @@ namespace MWMechanics - // if false triggers calculating allowed nodes based on mDistance - bool mStoredAvailableNodes; + // do we need to calculate allowed nodes based on mDistance + bool mPopulateAvailableNodes; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 6252d392bf..6417bc812c 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -459,7 +459,7 @@ void NpcAnimation::updateParts() }; static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); - bool wasArrowAttached = 0;//(mAmmunition.get() != NULL); + bool wasArrowAttached = (mAmmunition.get() != NULL); MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr); for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) @@ -622,7 +622,7 @@ void NpcAnimation::updateParts() continue; } - if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) + if ((!mNpc->isMale()) != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female)) { // Allow opposite gender's parts as fallback if parts for our gender are missing BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); diff --git a/apps/openmw/mwrender/ripplesimulation.hpp b/apps/openmw/mwrender/ripplesimulation.hpp index 8e591a5dbb..1717cca573 100644 --- a/apps/openmw/mwrender/ripplesimulation.hpp +++ b/apps/openmw/mwrender/ripplesimulation.hpp @@ -59,7 +59,6 @@ namespace MWRender private: osg::ref_ptr mParent; - Resource::ResourceSystem* mResourceSystem; osg::ref_ptr mParticleSystem; osg::ref_ptr mParticleNode; diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 96c7118964..d873ab4686 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1064,11 +1064,11 @@ namespace MWWorld template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; -template class MWWorld::Store; +//template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; -template class MWWorld::Store; +//template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; @@ -1082,21 +1082,21 @@ template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; -template class MWWorld::Store; +//template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; -template class MWWorld::Store; +//template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; -template class MWWorld::Store; +//template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; -template class MWWorld::Store; +//template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 02fb983cda..3cbd8d3ac9 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -319,7 +319,7 @@ namespace MWWorld public: - Store(); + Store(); void setCells(Store& cells); void load(ESM::ESMReader &esm, const std::string &id); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 3224d09896..d91bb5c30d 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -180,6 +180,7 @@ target_link_libraries(components ${SDL2_LIBRARY} # For MyGUI platform ${OPENGL_gl_LIBRARY} + ${MYGUI_LIBRARIES} ) if (WIN32) diff --git a/components/myguiplatform/myguirendermanager.cpp b/components/myguiplatform/myguirendermanager.cpp index 89d55f5c15..160d659bd6 100644 --- a/components/myguiplatform/myguirendermanager.cpp +++ b/components/myguiplatform/myguirendermanager.cpp @@ -119,7 +119,7 @@ class Drawable : public osg::Drawable { // VBOs disabled due to crash in OSG: http://forum.openscenegraph.org/viewtopic.php?t=14909 osg::GLBufferObject* bufferobject = 0;//state->isVertexBufferObjectSupported() ? vbo->getOrCreateGLBufferObject(state->getContextID()) : 0; - if (bufferobject) + if (0)//bufferobject) { state->bindVertexBufferObject(bufferobject); diff --git a/components/nifosg/controller.cpp b/components/nifosg/controller.cpp index 5e7e55004e..83ecc0fa97 100644 --- a/components/nifosg/controller.cpp +++ b/components/nifosg/controller.cpp @@ -407,7 +407,8 @@ FlipController::FlipController(int texSlot, float delta, std::vectorsecond); } - void SDLCursorManager::receiveCursorInfo(const std::string& name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y) + void SDLCursorManager::createCursor(const std::string& name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y) { _createCursorFromResource(name, rotDegrees, image, size_x, size_y, hotspot_x, hotspot_y); } diff --git a/components/sdlutil/sdlcursormanager.hpp b/components/sdlutil/sdlcursormanager.hpp index 646f548e3c..0db578039c 100644 --- a/components/sdlutil/sdlcursormanager.hpp +++ b/components/sdlutil/sdlcursormanager.hpp @@ -27,11 +27,9 @@ namespace SDLUtil /// \brief Tell the manager that the cursor has changed, giving the /// name of the cursor we changed to ("arrow", "ibeam", etc) - /// \return Whether the manager is interested in more information about the cursor - virtual bool cursorChanged(const std::string &name); + virtual void cursorChanged(const std::string &name); - /// \brief Follow up a cursorChanged() call with enough info to create an cursor. - virtual void receiveCursorInfo(const std::string &name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y); + virtual void createCursor(const std::string &name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y); private: void _createCursorFromResource(const std::string &name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y);