Merge remote-tracking branch 'upstream/master' into static-deps-build

This commit is contained in:
slothlife 2015-07-17 01:03:19 -05:00
commit 43b1f15af9
18 changed files with 278 additions and 234 deletions

View file

@ -18,8 +18,8 @@ addons:
name: "OpenMW/openmw" name: "OpenMW/openmw"
description: "<Your project description here>" description: "<Your project description here>"
notification_email: scrawl@baseoftrash.de notification_email: scrawl@baseoftrash.de
build_command_prepend: "cmake ." build_command_prepend: "cmake . -DBUILD_UNITTESTS=FALSE -DBUILD_OPENCS=FALSE"
build_command: "make -j3" build_command: "make"
branch_pattern: coverity_scan branch_pattern: coverity_scan
matrix: matrix:
include: include:

View file

@ -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) [![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 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.
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 * Version: 0.36.0
* License: GPL (see docs/license/GPL3.txt for more information) * 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: Font Licenses:
* DejaVuLGCSansMono.ttf: custom (see docs/license/DejaVu Font License.txt for more information) * 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 Getting Started
--------------- ---------------

View file

@ -166,7 +166,9 @@ namespace ESSImport
if (i >= file2.mRecords.size()) 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 << "Record in file1 not present in file2: (1) 0x" << std::hex << rec.mFileOffset << std::endl;
std::cout.flags(f);
return; return;
} }
@ -174,7 +176,9 @@ namespace ESSImport
if (rec.mName != rec2.mName) 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 << "Different record name at (2) 0x" << std::hex << rec2.mFileOffset << std::endl;
std::cout.flags(f);
return; // TODO: try to recover return; // TODO: try to recover
} }
@ -185,7 +189,9 @@ namespace ESSImport
if (j >= rec2.mSubrecords.size()) 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 << "Subrecord in file1 not present in file2: (1) 0x" << std::hex << sub.mFileOffset << std::endl;
std::cout.flags(f);
return; return;
} }
@ -193,8 +199,10 @@ namespace ESSImport
if (sub.mName != sub2.mName) 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 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; << " (2) 0x" << sub2.mFileOffset << std::endl;
std::cout.flags(f);
break; // TODO: try to recover break; // TODO: try to recover
} }
@ -203,6 +211,8 @@ namespace ESSImport
if (blacklist.find(std::make_pair(rec.mName, sub.mName)) != blacklist.end()) if (blacklist.find(std::make_pair(rec.mName, sub.mName)) != blacklist.end())
continue; 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 std::cout << "Different subrecord data for " << rec.mName << "." << sub.mName << " at (1) 0x" << std::hex << sub.mFileOffset
<< " (2) 0x" << sub2.mFileOffset << std::endl; << " (2) 0x" << sub2.mFileOffset << std::endl;
@ -235,6 +245,7 @@ namespace ESSImport
std::cout << "\033[0m"; std::cout << "\033[0m";
} }
std::cout << std::endl; std::cout << std::endl;
std::cout.flags(f);
} }
} }
} }
@ -319,7 +330,11 @@ namespace ESSImport
else else
{ {
if (unknownRecords.insert(n.val).second) 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 << "unknown record " << n.toString() << " (0x" << std::hex << esm.getFileOffset() << ")" << std::endl;
std::cerr.flags(f);
}
esm.skipRecord(); esm.skipRecord();
} }

View file

@ -49,6 +49,10 @@ namespace ESSImport
std::map<std::string, ESM::NPC> mNpcs; std::map<std::string, ESM::NPC> mNpcs;
Context() Context()
: mDay(0)
, mMonth(0)
, mYear(0)
, mHour(0.f)
{ {
mPlayer.mAutoMove = 0; mPlayer.mAutoMove = 0;
ESM::CellId playerCellId; ESM::CellId playerCellId;

View file

@ -203,6 +203,8 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c
return false; // script does not have a variable of this name. return false; // script does not have a variable of this name.
int index = localDefs.getIndex (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(); const MWScript::Locals& locals = mActor.getRefData().getLocals();

View file

@ -235,8 +235,9 @@ namespace MWGui
MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged); MyGUI::InputManager::getInstance().eventChangeKeyFocus += MyGUI::newDelegate(this, &WindowManager::onKeyFocusChanged);
// Create all cursors in advance
createCursors();
onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer()); onCursorChange(MyGUI::PointerManager::getInstance().getDefaultPointer());
mCursorManager->setEnabled(true); mCursorManager->setEnabled(true);
// hide mygui's pointer // hide mygui's pointer
@ -896,6 +897,9 @@ namespace MWGui
void WindowManager::updateMap() void WindowManager::updateMap()
{ {
if (!mLocalMapRender)
return;
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
osg::Vec3f playerPosition = player.getRefData().getPosition().asVec3(); osg::Vec3f playerPosition = player.getRefData().getPosition().asVec3();
@ -1178,31 +1182,7 @@ namespace MWGui
void WindowManager::onCursorChange(const std::string &name) void WindowManager::onCursorChange(const std::string &name)
{ {
if(!mCursorManager->cursorChanged(name)) 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<ResourceImageSetPointerFix*>(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<osg::Texture2D> 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);
}
}
} }
void WindowManager::popGuiMode() void WindowManager::popGuiMode()
@ -1960,6 +1940,33 @@ namespace MWGui
return Misc::ResourceHelpers::correctTexturePath(path, mResourceSystem->getVFS()); 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<ResourceImageSetPointerFix*>(resource);
if (!imgSetPointer)
continue;
std::string tex_name = imgSetPointer->getImageSet()->getIndexInfo(0,0).texture;
osg::ref_ptr<osg::Texture2D> 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() void WindowManager::createTextures()
{ {
{ {

View file

@ -511,6 +511,7 @@ namespace MWGui
void onClipboardRequested(const std::string& _type, std::string& _data); void onClipboardRequested(const std::string& _type, std::string& _data);
void createTextures(); void createTextures();
void createCursors();
void setMenuTransparency(float value); void setMenuTransparency(float value);
}; };
} }

View file

@ -117,7 +117,7 @@ namespace MWMechanics
mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp(); mStartTime = MWBase::Environment::get().getWorld()->getTimeStamp();
mStoredAvailableNodes = false; mPopulateAvailableNodes = true;
} }
@ -191,7 +191,7 @@ namespace MWMechanics
if(!currentCell || cellChange) if(!currentCell || cellChange)
{ {
currentCell = actor.getCell(); currentCell = actor.getCell();
mStoredAvailableNodes = false; // prob. not needed since mDistance = 0 mPopulateAvailableNodes = true;
} }
cStats.setDrawState(DrawState_Nothing); cStats.setDrawState(DrawState_Nothing);
@ -225,8 +225,6 @@ namespace MWMechanics
storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], DESTINATION_TOLERANCE)) storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], DESTINATION_TOLERANCE))
{ {
stopWalking(actor, storage); stopWalking(actor, storage);
moveNow = false;
walking = false;
chooseAction = true; chooseAction = true;
mHasReturnPosition = false; mHasReturnPosition = false;
} }
@ -239,45 +237,7 @@ namespace MWMechanics
zTurn(actor, osg::DegreesToRadians(storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); zTurn(actor, osg::DegreesToRadians(storage.mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1])));
actor.getClass().getMovementSettings(actor).mPosition[1] = 1; actor.getClass().getMovementSettings(actor).mPosition[1] = 1;
// Returns true if evasive action needs to be taken evadeObstacles(actor, storage, 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();
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
} }
@ -325,32 +285,7 @@ namespace MWMechanics
} }
} }
// Play idle voiced dialogue entries randomly playIdleDialogueRandomly(actor);
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<ESM::GameSetting>().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");
}
float& lastReaction = storage.mReaction; float& lastReaction = storage.mReaction;
lastReaction += duration; lastReaction += duration;
@ -367,17 +302,8 @@ namespace MWMechanics
{ {
// End package if duration is complete or mid-night hits: // End package if duration is complete or mid-night hits:
MWWorld::TimeStamp currentTime = world->getTimeStamp(); MWWorld::TimeStamp currentTime = world->getTimeStamp();
if(currentTime.getHour() >= mStartTime.getHour() + mDuration) if((currentTime.getHour() >= mStartTime.getHour() + mDuration) ||
{ (int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay()))
if(!mRepeat)
{
stopWalking(actor, storage);
return true;
}
else
mStartTime = currentTime;
}
else if(int(currentTime.getHour()) == 0 && currentTime.getDay() != mStartTime.getDay())
{ {
if(!mRepeat) if(!mRepeat)
{ {
@ -390,7 +316,7 @@ namespace MWMechanics
} }
// Initialization to discover & store allowed node points for this actor. // Initialization to discover & store allowed node points for this actor.
if(!mStoredAvailableNodes) if (mPopulateAvailableNodes)
{ {
getAllowedNodes(actor, currentCell->getCell()); getAllowedNodes(actor, currentCell->getCell());
} }
@ -432,71 +358,7 @@ namespace MWMechanics
// Allow interrupting a walking actor to trigger a greeting // Allow interrupting a walking actor to trigger a greeting
if(idleNow || walking) if(idleNow || walking)
{ {
// Play a random voice greeting if the player gets too close playGreetingIfPlayerGetsTooClose(actor, storage);
int hello = cStats.getAiSetting(CreatureStats::AI_Hello).getModified();
float helloDistance = static_cast<float>(hello);
static int iGreetDistanceMultiplier =MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().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;
}
} }
if(moveNow && mDistance) if(moveNow && mDistance)
@ -504,41 +366,187 @@ namespace MWMechanics
// Construct a new path if there isn't one // Construct a new path if there isn't one
if(!storage.mPathFinder.isPathConstructed()) if(!storage.mPathFinder.isPathConstructed())
{ {
assert(mAllowedNodes.size()); if (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())
{ {
// Remove this node as an option and add back the previously used node (stops NPC from picking the same node): setPathToAnAllowedNode(actor, storage, pos);
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;
} }
// 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 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<ESM::GameSetting>().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<float>(hello);
static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
.get<ESM::GameSetting>().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) void AiWander::ToWorldCoordinates(ESM::Pathgrid::Point& point, const ESM::Cell * cell)
{ {
if (cell->isExterior()) if (cell->isExterior())
@ -583,6 +591,8 @@ namespace MWMechanics
{ {
storage.mPathFinder.clearPath(); storage.mPathFinder.clearPath();
actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
storage.mMoveNow = false;
storage.mWalking = false;
} }
void AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect) void AiWander::playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect)
@ -640,7 +650,7 @@ namespace MWMechanics
if (mDistance == 0) if (mDistance == 0)
return; return;
if (!mStoredAvailableNodes) if (mPopulateAvailableNodes)
getAllowedNodes(actor, actor.getCell()->getCell()); getAllowedNodes(actor, actor.getCell()->getCell());
if (mAllowedNodes.empty()) if (mAllowedNodes.empty())
@ -660,7 +670,7 @@ namespace MWMechanics
actor.getClass().adjustPosition(actor, false); actor.getClass().adjustPosition(actor, false);
// may have changed cell // may have changed cell
mStoredAvailableNodes = false; mPopulateAvailableNodes = true;
} }
int AiWander::OffsetToPreventOvercrowding() int AiWander::OffsetToPreventOvercrowding()
@ -722,8 +732,9 @@ namespace MWMechanics
{ {
SetCurrentNodeToClosestAllowedNode(npcPos); SetCurrentNodeToClosestAllowedNode(npcPos);
} }
mStoredAvailableNodes = true; // set only if successful in finding allowed nodes
} }
mPopulateAvailableNodes = false;
} }
// When only one path grid point in wander distance, // When only one path grid point in wander distance,

View file

@ -71,6 +71,10 @@ namespace MWMechanics
void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); void playIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect); bool checkIdle(const MWWorld::Ptr& actor, unsigned short idleSelect);
void getRandomIdle(unsigned short& playedIdle); 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 mDistance; // how far the actor can wander from the spawn point
int mDuration; int mDuration;
@ -88,8 +92,8 @@ namespace MWMechanics
// if false triggers calculating allowed nodes based on mDistance // do we need to calculate allowed nodes based on mDistance
bool mStoredAvailableNodes; bool mPopulateAvailableNodes;

View file

@ -459,7 +459,7 @@ void NpcAnimation::updateParts()
}; };
static const size_t slotlistsize = sizeof(slotlist)/sizeof(slotlist[0]); 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); MWWorld::InventoryStore& inv = mPtr.getClass().getInventoryStore(mPtr);
for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++) for(size_t i = 0;i < slotlistsize && mViewMode != VM_HeadOnly;i++)
@ -622,7 +622,7 @@ void NpcAnimation::updateParts()
continue; 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 // 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)); BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));

View file

@ -59,7 +59,6 @@ namespace MWRender
private: private:
osg::ref_ptr<osg::Group> mParent; osg::ref_ptr<osg::Group> mParent;
Resource::ResourceSystem* mResourceSystem;
osg::ref_ptr<osgParticle::ParticleSystem> mParticleSystem; osg::ref_ptr<osgParticle::ParticleSystem> mParticleSystem;
osg::ref_ptr<osg::PositionAttitudeTransform> mParticleNode; osg::ref_ptr<osg::PositionAttitudeTransform> mParticleNode;

View file

@ -1064,11 +1064,11 @@ namespace MWWorld
template class MWWorld::Store<ESM::Activator>; template class MWWorld::Store<ESM::Activator>;
template class MWWorld::Store<ESM::Apparatus>; template class MWWorld::Store<ESM::Apparatus>;
template class MWWorld::Store<ESM::Armor>; template class MWWorld::Store<ESM::Armor>;
template class MWWorld::Store<ESM::Attribute>; //template class MWWorld::Store<ESM::Attribute>;
template class MWWorld::Store<ESM::BirthSign>; template class MWWorld::Store<ESM::BirthSign>;
template class MWWorld::Store<ESM::BodyPart>; template class MWWorld::Store<ESM::BodyPart>;
template class MWWorld::Store<ESM::Book>; template class MWWorld::Store<ESM::Book>;
template class MWWorld::Store<ESM::Cell>; //template class MWWorld::Store<ESM::Cell>;
template class MWWorld::Store<ESM::Class>; template class MWWorld::Store<ESM::Class>;
template class MWWorld::Store<ESM::Clothing>; template class MWWorld::Store<ESM::Clothing>;
template class MWWorld::Store<ESM::Container>; template class MWWorld::Store<ESM::Container>;
@ -1082,21 +1082,21 @@ template class MWWorld::Store<ESM::GameSetting>;
template class MWWorld::Store<ESM::Global>; template class MWWorld::Store<ESM::Global>;
template class MWWorld::Store<ESM::Ingredient>; template class MWWorld::Store<ESM::Ingredient>;
template class MWWorld::Store<ESM::ItemLevList>; template class MWWorld::Store<ESM::ItemLevList>;
template class MWWorld::Store<ESM::Land>; //template class MWWorld::Store<ESM::Land>;
template class MWWorld::Store<ESM::LandTexture>; template class MWWorld::Store<ESM::LandTexture>;
template class MWWorld::Store<ESM::Light>; template class MWWorld::Store<ESM::Light>;
template class MWWorld::Store<ESM::Lockpick>; template class MWWorld::Store<ESM::Lockpick>;
template class MWWorld::Store<ESM::MagicEffect>; //template class MWWorld::Store<ESM::MagicEffect>;
template class MWWorld::Store<ESM::Miscellaneous>; template class MWWorld::Store<ESM::Miscellaneous>;
template class MWWorld::Store<ESM::NPC>; template class MWWorld::Store<ESM::NPC>;
template class MWWorld::Store<ESM::Pathgrid>; //template class MWWorld::Store<ESM::Pathgrid>;
template class MWWorld::Store<ESM::Potion>; template class MWWorld::Store<ESM::Potion>;
template class MWWorld::Store<ESM::Probe>; template class MWWorld::Store<ESM::Probe>;
template class MWWorld::Store<ESM::Race>; template class MWWorld::Store<ESM::Race>;
template class MWWorld::Store<ESM::Region>; template class MWWorld::Store<ESM::Region>;
template class MWWorld::Store<ESM::Repair>; template class MWWorld::Store<ESM::Repair>;
template class MWWorld::Store<ESM::Script>; template class MWWorld::Store<ESM::Script>;
template class MWWorld::Store<ESM::Skill>; //template class MWWorld::Store<ESM::Skill>;
template class MWWorld::Store<ESM::Sound>; template class MWWorld::Store<ESM::Sound>;
template class MWWorld::Store<ESM::SoundGenerator>; template class MWWorld::Store<ESM::SoundGenerator>;
template class MWWorld::Store<ESM::Spell>; template class MWWorld::Store<ESM::Spell>;

View file

@ -319,7 +319,7 @@ namespace MWWorld
public: public:
Store<ESM::Pathgrid>(); Store();
void setCells(Store<ESM::Cell>& cells); void setCells(Store<ESM::Cell>& cells);
void load(ESM::ESMReader &esm, const std::string &id); void load(ESM::ESMReader &esm, const std::string &id);

View file

@ -180,6 +180,7 @@ target_link_libraries(components
${SDL2_LIBRARY} ${SDL2_LIBRARY}
# For MyGUI platform # For MyGUI platform
${OPENGL_gl_LIBRARY} ${OPENGL_gl_LIBRARY}
${MYGUI_LIBRARIES}
) )
if (WIN32) if (WIN32)

View file

@ -119,7 +119,7 @@ class Drawable : public osg::Drawable {
// VBOs disabled due to crash in OSG: http://forum.openscenegraph.org/viewtopic.php?t=14909 // 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; osg::GLBufferObject* bufferobject = 0;//state->isVertexBufferObjectSupported() ? vbo->getOrCreateGLBufferObject(state->getContextID()) : 0;
if (bufferobject) if (0)//bufferobject)
{ {
state->bindVertexBufferObject(bufferobject); state->bindVertexBufferObject(bufferobject);

View file

@ -407,7 +407,8 @@ FlipController::FlipController(int texSlot, float delta, std::vector<osg::ref_pt
} }
FlipController::FlipController() FlipController::FlipController()
: mDelta(0.f) : mTexSlot(0)
, mDelta(0.f)
{ {
} }

View file

@ -175,23 +175,16 @@ namespace SDLUtil
} }
} }
bool SDLCursorManager::cursorChanged(const std::string& name) void SDLCursorManager::cursorChanged(const std::string& name)
{ {
mCurrentCursor = name; mCurrentCursor = name;
CursorMap::const_iterator curs_iter = mCursorMap.find(name); CursorMap::const_iterator curs_iter = mCursorMap.find(name);
//we have this cursor
if(curs_iter != mCursorMap.end()) if(curs_iter != mCursorMap.end())
{ {
//we have this cursor
_setGUICursor(name); _setGUICursor(name);
return false;
}
else
{
//they should get back to us with more info
return true;
} }
} }
@ -200,7 +193,7 @@ namespace SDLUtil
SDL_SetCursor(mCursorMap.find(name)->second); SDL_SetCursor(mCursorMap.find(name)->second);
} }
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); _createCursorFromResource(name, rotDegrees, image, size_x, size_y, hotspot_x, hotspot_y);
} }

View file

@ -27,11 +27,9 @@ namespace SDLUtil
/// \brief Tell the manager that the cursor has changed, giving the /// \brief Tell the manager that the cursor has changed, giving the
/// name of the cursor we changed to ("arrow", "ibeam", etc) /// name of the cursor we changed to ("arrow", "ibeam", etc)
/// \return Whether the manager is interested in more information about the cursor virtual void cursorChanged(const std::string &name);
virtual bool cursorChanged(const std::string &name);
/// \brief Follow up a cursorChanged() call with enough info to create an cursor. virtual void createCursor(const std::string &name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y);
virtual void receiveCursorInfo(const std::string &name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y);
private: private:
void _createCursorFromResource(const std::string &name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y); void _createCursorFromResource(const std::string &name, int rotDegrees, osg::Image* image, Uint8 size_x, Uint8 size_y, Uint8 hotspot_x, Uint8 hotspot_y);