int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name) { if(name == "left") { MWBase::World *world = MWBase::Environment::get().getWorld(); Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); if(world->isUnderwater(ptr.getCell(), pos)) return 2; if(world->isOnGround(ptr)) return 0; return -1; } if(name == "right") { MWBase::World *world = MWBase::Environment::get().getWorld(); Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); if(world->isUnderwater(ptr.getCell(), pos)) return 3; if(world->isOnGround(ptr)) return 1; return -1; } if(name == "moan") return 4; if(name == "roar") return 5; if(name == "scream") return 6; if(name == "land") return 7; throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); }
MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minSqr, bool closed) { MWWorld::CellStore *cell = actor.getCell(); if(cell->getCell()->isExterior()) return MWWorld::Ptr(); // check interior cells only // Check all the doors in this cell MWWorld::CellRefList<ESM::Door>& doors = cell->get<ESM::Door>(); MWWorld::CellRefList<ESM::Door>::List& refList = doors.mList; MWWorld::CellRefList<ESM::Door>::List::iterator it = refList.begin(); Ogre::Vector3 pos(actor.getRefData().getPosition().pos); /// TODO: How to check whether the actor is facing a door? Below code is for /// the player, perhaps it can be adapted. //MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject(); //if(!ptr.isEmpty()) //std::cout << "faced door " << ptr.getClass().getName(ptr) << std::endl; /// TODO: The in-game observation of rot[2] value seems to be the /// opposite of the code in World::activateDoor() ::confused:: for (; it != refList.end(); ++it) { MWWorld::LiveCellRef<ESM::Door>& ref = *it; if(pos.squaredDistance(Ogre::Vector3(ref.mData.getPosition().pos)) < minSqr) if((closed && ref.mData.getLocalRotation().rot[2] == 0) || (!closed && ref.mData.getLocalRotation().rot[2] >= 1)) { return MWWorld::Ptr(&ref, actor.getCell()); // found, stop searching } } return MWWorld::Ptr(); // none found }
void Actors::insertBegin(const MWWorld::Ptr &ptr) { Ogre::SceneNode* cellnode; CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(ptr.getCell()); if(celliter != mCellSceneNodes.end()) cellnode = celliter->second; else { //Create the scenenode and put it in the map cellnode = mRootNode->createChildSceneNode(); mCellSceneNodes[ptr.getCell()] = cellnode; } Ogre::SceneNode* insert = cellnode->createChildSceneNode(); const float *f = ptr.getRefData().getPosition().pos; insert->setPosition(f[0], f[1], f[2]); insert->setScale(ptr.getCellRef().mScale, ptr.getCellRef().mScale, ptr.getCellRef().mScale); // Convert MW rotation to a quaternion: f = ptr.getCellRef().mPos.rot; // Rotate around X axis Ogre::Quaternion xr(Ogre::Radian(-f[0]), Ogre::Vector3::UNIT_X); // Rotate around Y axis Ogre::Quaternion yr(Ogre::Radian(-f[1]), Ogre::Vector3::UNIT_Y); // Rotate around Z axis Ogre::Quaternion zr(Ogre::Radian(-f[2]), Ogre::Vector3::UNIT_Z); // Rotates first around z, then y, then x insert->setOrientation(xr*yr*zr); ptr.getRefData().setBaseNode(insert); }
void Objects::insertBegin(const MWWorld::Ptr& ptr) { osg::ref_ptr<osg::Group> cellnode; CellMap::iterator found = mCellSceneNodes.find(ptr.getCell()); if (found == mCellSceneNodes.end()) { cellnode = new osg::Group; mRootNode->addChild(cellnode); mCellSceneNodes[ptr.getCell()] = cellnode; } else cellnode = found->second; osg::ref_ptr<SceneUtil::PositionAttitudeTransform> insert (new SceneUtil::PositionAttitudeTransform); cellnode->addChild(insert); insert->getOrCreateUserDataContainer()->addUserObject(new PtrHolder(ptr)); const float *f = ptr.getRefData().getPosition().pos; insert->setPosition(osg::Vec3(f[0], f[1], f[2])); const float scale = ptr.getCellRef().getScale(); osg::Vec3f scaleVec(scale, scale, scale); ptr.getClass().adjustScale(ptr, scaleVec, true); insert->setScale(scaleVec); ptr.getRefData().setBaseNode(insert); }
bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration) { //Update various Timers mTimer += duration; //Update timer ESM::Position pos = actor.getRefData().getPosition(); //position of the actor /// Stops the actor when it gets too close to a unloaded cell //... At current time, this test is unnecessary. AI shuts down when actor is more than 7168 //... units from player, and exterior cells are 8192 units long and wide. //... But AI processing distance may increase in the future. if (isNearInactiveCell(pos)) { actor.getClass().getMovementSettings(actor).mPosition[1] = 0; return false; } //*********************** /// Checks if you can't get to the end position at all, adds end position to end of path /// Rebuilds path every quarter of a second, in case the target has moved //*********************** if(mTimer > 0.25) { const ESM::Cell *cell = actor.getCell()->getCell(); if (doesPathNeedRecalc(dest, cell)) { //Only rebuild path if it's moved mPathFinder.buildSyncedPath(pos.pos, dest, actor.getCell(), true); //Rebuild path, in case the target has moved mPrevDest = dest; } if(!mPathFinder.getPath().empty()) //Path has points in it { ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path if(distance(dest, lastPos) > 100) //End of the path is far from the destination mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go } mTimer = 0; } //************************ /// Checks if you aren't moving; attempts to unstick you //************************ if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1])) //Path finished? { // Reset mTimer so that path will be built right away when a package is repeated mTimer = 0.26f; return true; } else { evadeObstacles(actor, duration, pos); } return false; }
void AiCombat::buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target) { ESM::Pathgrid::Point newPathTarget = PathFinder::MakePathgridPoint(target.getRefData().getPosition()); //construct new path only if target has moved away more than on [targetPosThreshold] if (doesPathNeedRecalc(newPathTarget, actor.getCell()->getCell())) { ESM::Pathgrid::Point start(PathFinder::MakePathgridPoint(actor.getRefData().getPosition())); mPathFinder.buildSyncedPath(start, newPathTarget, actor.getCell(), false); } }
void AiCombat::buildNewPath(const MWWorld::Ptr& actor) { //Construct path to target ESM::Pathgrid::Point dest; dest.mX = mTarget.getRefData().getPosition().pos[0]; dest.mY = mTarget.getRefData().getPosition().pos[1]; dest.mZ = mTarget.getRefData().getPosition().pos[2]; Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); float dist = -1; //hack to indicate first time, to construct a new path if(!mPathFinder.getPath().empty()) { ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); } float targetPosThreshold; bool isOutside = actor.getCell()->getCell()->isExterior(); if (isOutside) targetPosThreshold = 300; else targetPosThreshold = 100; if((dist < 0) || (dist > targetPosThreshold)) { //construct new path only if target has moved away more than on <targetPosThreshold> ESM::Position pos = actor.getRefData().getPosition(); ESM::Pathgrid::Point start; start.mX = pos.pos[0]; start.mY = pos.pos[1]; start.mZ = pos.pos[2]; if(!mPathFinder.isPathConstructed()) mPathFinder.buildPath(start, dest, actor.getCell(), isOutside); else { PathFinder newPathFinder; newPathFinder.buildPath(start, dest, actor.getCell(), isOutside); //TO EXPLORE: //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, //not the actual path length. Here we should know if the new path is actually more effective. //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) if(!mPathFinder.getPath().empty()) { newPathFinder.syncStart(mPathFinder.getPath()); mPathFinder = newPathFinder; } } } }
bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const { const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); // check player faction if (!info.mPcFaction.empty()) { MWMechanics::NpcStats& stats = MWWorld::Class::get (player).getNpcStats (player); std::map<std::string,int>::iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (info.mPcFaction)); if(iter==stats.getFactionRanks().end()) return false; // check rank if (iter->second < info.mData.mPCrank) return false; } // check cell if (!info.mCell.empty()) if (Misc::StringUtils::lowerCase (player.getCell()->mCell->mName) != Misc::StringUtils::lowerCase (info.mCell)) return false; return true; }
bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const { const MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); // check player faction if (!info.mPcFaction.empty()) { MWMechanics::NpcStats& stats = MWWorld::Class::get (player).getNpcStats (player); std::map<std::string,int>::iterator iter = stats.getFactionRanks().find (Misc::StringUtils::lowerCase (info.mPcFaction)); if(iter==stats.getFactionRanks().end()) return false; // check rank if (iter->second < info.mData.mPCrank) return false; } // check cell if (!info.mCell.empty()) { // supports partial matches, just like getPcCell const std::string& playerCell = player.getCell()->getCell()->mName; bool match = playerCell.length()>=info.mCell.length() && Misc::StringUtils::ciEqual(playerCell.substr (0, info.mCell.length()), info.mCell); if (!match) return false; } return true; }
void Actors::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { Ogre::SceneNode *node; MWWorld::CellStore *newCell = cur.getCell(); CellSceneNodeMap::const_iterator celliter = mCellSceneNodes.find(newCell); if(celliter != mCellSceneNodes.end()) node = celliter->second; else { node = mRootNode->createChildSceneNode(); mCellSceneNodes[newCell] = node; } node->addChild(cur.getRefData().getBaseNode()); PtrAnimationMap::iterator iter = mAllActors.find(old); if(iter != mAllActors.end()) { Animation *anim = iter->second; mAllActors.erase(iter); anim->updatePtr(cur); mAllActors[cur] = anim; } mRendering->updateWaterRippleEmitterPtr (old, cur); }
bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mActorId, false); //The target to follow if(target == MWWorld::Ptr()) return true; //Target doesn't exist ESM::Position pos = actor.getRefData().getPosition(); //position of the actor if(!mAlwaysFollow) //Update if you only follow for a bit { if(mTotalTime > mDuration && mDuration != 0) //Check if we've run out of time return true; if((pos.pos[0]-mX)*(pos.pos[0]-mX) + (pos.pos[1]-mY)*(pos.pos[1]-mY) + (pos.pos[2]-mZ)*(pos.pos[2]-mZ) < 100*100) //Close-ish to final position { if(actor.getCell()->isExterior()) //Outside? { if(mCellId == "") //No cell to travel to return true; } else { if(mCellId == actor.getCell()->getCell()->mName) //Cell to travel to return true; } } } //Set the target desition from the actor ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) //Stop when you get close actor.getClass().getMovementSettings(actor).mPosition[1] = 0; else { pathTo(actor, dest, duration); //Go to the destination } //Check if you're far away if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) > 1000) actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run else if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 800) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk return false; }
void SoundManager::updateRegionSound(float duration) { static float sTimeToNextEnvSound = 0.0f; static int total = 0; static std::string regionName = ""; static float sTimePassed = 0.0; MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Ptr player = world->getPlayerPtr(); const ESM::Cell *cell = player.getCell()->getCell(); sTimePassed += duration; if(!cell->isExterior() || sTimePassed < sTimeToNextEnvSound) return; float a = Misc::Rng::rollClosedProbability(); // NOTE: We should use the "Minimum Time Between Environmental Sounds" and // "Maximum Time Between Environmental Sounds" fallback settings here. sTimeToNextEnvSound = 5.0f*a + 15.0f*(1.0f-a); sTimePassed = 0; if(regionName != cell->mRegion) { regionName = cell->mRegion; total = 0; } const ESM::Region *regn = world->getStore().get<ESM::Region>().search(regionName); if(regn == NULL) return; std::vector<ESM::Region::SoundRef>::const_iterator soundIter; if(total == 0) { soundIter = regn->mSoundList.begin(); while(soundIter != regn->mSoundList.end()) { total += (int)soundIter->mChance; ++soundIter; } if(total == 0) return; } int r = Misc::Rng::rollDice(total); int pos = 0; soundIter = regn->mSoundList.begin(); while(soundIter != regn->mSoundList.end()) { if(r - pos < soundIter->mChance) { playSound(soundIter->mSound.toString(), 1.0f, 1.0f); break; } pos += soundIter->mChance; ++soundIter; } }
void AiCombat::buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target) { Ogre::Vector3 newPathTarget = Ogre::Vector3(target.getRefData().getPosition().pos); float dist; if(!mPathFinder.getPath().empty()) { ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); dist = (newPathTarget - currPathTarget).length(); } else dist = 1e+38F; // necessarily construct a new path float targetPosThreshold = (actor.getCell()->getCell()->isExterior())? 300 : 100; //construct new path only if target has moved away more than on [targetPosThreshold] if(dist > targetPosThreshold) { ESM::Position pos = actor.getRefData().getPosition(); ESM::Pathgrid::Point start; start.mX = pos.pos[0]; start.mY = pos.pos[1]; start.mZ = pos.pos[2]; ESM::Pathgrid::Point dest; dest.mX = newPathTarget.x; dest.mY = newPathTarget.y; dest.mZ = newPathTarget.z; if(!mPathFinder.isPathConstructed()) mPathFinder.buildPath(start, dest, actor.getCell(), false); else { PathFinder newPathFinder; newPathFinder.buildPath(start, dest, actor.getCell(), false); if(!mPathFinder.getPath().empty()) { newPathFinder.syncStart(mPathFinder.getPath()); mPathFinder = newPathFinder; } } } }
void WaitDialog::startWaiting(int hoursToWait) { if(Settings::Manager::getBool("autosave","Saves")) //autosaves when enabled MWBase::Environment::get().getStateManager()->quickSave("Autosave"); MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.2f); mFadeTimeRemaining = 0.4f; setVisible(false); mHours = hoursToWait; // FIXME: move this somewhere else? mInterruptAt = -1; MWWorld::Ptr player = world->getPlayerPtr(); if (mSleeping && player.getCell()->isExterior()) { std::string regionstr = player.getCell()->getCell()->mRegion; if (!regionstr.empty()) { const ESM::Region *region = world->getStore().get<ESM::Region>().find (regionstr); if (!region->mSleepList.empty()) { // figure out if player will be woken while sleeping int x = Misc::Rng::rollDice(hoursToWait); float fSleepRandMod = world->getStore().get<ESM::GameSetting>().find("fSleepRandMod")->getFloat(); if (x < fSleepRandMod * hoursToWait) { float fSleepRestMod = world->getStore().get<ESM::GameSetting>().find("fSleepRestMod")->getFloat(); int interruptAtHoursRemaining = int(fSleepRestMod * hoursToWait); if (interruptAtHoursRemaining != 0) { mInterruptAt = hoursToWait - interruptAtHoursRemaining; mInterruptCreatureList = region->mSleepList; } } } } } mProgressBar.setProgress (0, hoursToWait); }
void Objects::insertBegin (const MWWorld::Ptr& ptr, bool enabled, bool static_) { Ogre::SceneNode* root = mMwRoot; Ogre::SceneNode* cellnode; if(mCellSceneNodes.find(ptr.getCell()) == mCellSceneNodes.end()) { //Create the scenenode and put it in the map cellnode = root->createChildSceneNode(); mCellSceneNodes[ptr.getCell()] = cellnode; } else { cellnode = mCellSceneNodes[ptr.getCell()]; } Ogre::SceneNode* insert = cellnode->createChildSceneNode(); const float *f = ptr.getRefData().getPosition().pos; insert->setPosition(f[0], f[1], f[2]); insert->setScale(ptr.getCellRef().scale, ptr.getCellRef().scale, ptr.getCellRef().scale); // Convert MW rotation to a quaternion: f = ptr.getCellRef().pos.rot; // Rotate around X axis Ogre::Quaternion xr(Ogre::Radian(-f[0]), Ogre::Vector3::UNIT_X); // Rotate around Y axis Ogre::Quaternion yr(Ogre::Radian(-f[1]), Ogre::Vector3::UNIT_Y); // Rotate around Z axis Ogre::Quaternion zr(Ogre::Radian(-f[2]), Ogre::Vector3::UNIT_Z); // Rotates first around z, then y, then x insert->setOrientation(xr*yr*zr); if (!enabled) insert->setVisible (false); ptr.getRefData().setBaseNode(insert); mIsStatic = static_; }
void WaitDialog::startWaiting(int hoursToWait) { if(Settings::Manager::getBool("autosave","Saves") && mSleeping) //autosaves when enabled and sleeping MWBase::Environment::get().getStateManager()->quickSave("Autosave"); MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.2); setVisible(false); mProgressBar.setVisible (true); mWaiting = true; mCurHour = 0; mHours = hoursToWait; // FIXME: move this somewhere else? mInterruptAt = -1; MWWorld::Ptr player = world->getPlayerPtr(); if (mSleeping && player.getCell()->isExterior()) { std::string regionstr = player.getCell()->getCell()->mRegion; if (!regionstr.empty()) { const ESM::Region *region = world->getStore().get<ESM::Region>().find (regionstr); if (!region->mSleepList.empty()) { float fSleepRandMod = world->getStore().get<ESM::GameSetting>().find("fSleepRandMod")->getFloat(); int x = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * hoursToWait; // [0, hoursRested] float y = fSleepRandMod * hoursToWait; if (x > y) { float fSleepRestMod = world->getStore().get<ESM::GameSetting>().find("fSleepRestMod")->getFloat(); mInterruptAt = hoursToWait - int(fSleepRestMod * hoursToWait); mInterruptCreatureList = region->mSleepList; } } } } mRemainingTime = 0.05; mProgressBar.setProgress (0, mHours); }
void SoundManager::setListenerPosDir(const osg::Vec3f &pos, const osg::Vec3f &dir, const osg::Vec3f &up) { mListenerPos = pos; mListenerDir = dir; mListenerUp = up; MWWorld::Ptr player = MWMechanics::getPlayer(); const MWWorld::CellStore *cell = player.getCell(); mListenerUnderwater = ((cell->getCell()->mData.mFlags&ESM::Cell::HasWater) && mListenerPos.z() < cell->getWaterLevel()); }
void SoundManager::setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) { mListenerPos = pos; mListenerDir = dir; mListenerUp = up; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const ESM::Cell *cell = player.getCell()->getCell(); mListenerUnderwater = ((cell->mData.mFlags&cell->HasWater) && mListenerPos.z < cell->mWater); }
bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { // If AiEscort has ran for as long or longer then the duration specified // and the duration is not infinite, the package is complete. if(mRemainingDuration != 0) { mRemainingDuration -= duration; if (duration <= 0) return true; } if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3())) return false; if (!mCellId.empty() && mCellId != actor.getCell()->getCell()->getCellId().mWorldspace) return false; // Not in the correct cell, pause and rely on the player to go back through a teleport door actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false); const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); const float* const leaderPos = actor.getRefData().getPosition().pos; const float* const followerPos = follower.getRefData().getPosition().pos; double differenceBetween[3]; for (short counter = 0; counter < 3; counter++) differenceBetween[counter] = (leaderPos[counter] - followerPos[counter]); double distanceBetweenResult = (differenceBetween[0] * differenceBetween[0]) + (differenceBetween[1] * differenceBetween[1]) + (differenceBetween[2] * differenceBetween[2]); if(distanceBetweenResult <= mMaxDist * mMaxDist) { ESM::Pathgrid::Point point(static_cast<int>(mX), static_cast<int>(mY), static_cast<int>(mZ)); point.mAutogenerated = 0; point.mConnectionNum = 0; point.mUnknown = 0; if(pathTo(actor,point,duration)) //Returns true on path complete return true; mMaxDist = 450; } else { // Stop moving if the player is to far away MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); actor.getClass().getMovementSettings(actor).mPosition[1] = 0; mMaxDist = 250; } return false; }
void Objects::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { Ogre::SceneNode *node; MWWorld::CellStore *newCell = cur.getCell(); if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) { node = mRootNode->createChildSceneNode(); mCellSceneNodes[newCell] = node; } else { node = mCellSceneNodes[newCell]; } node->addChild(cur.getRefData().getBaseNode()); }
const MWWorld::Ptr getNearbyDoor(const MWWorld::Ptr& actor, float minDist) { MWWorld::CellStore *cell = actor.getCell(); // Check all the doors in this cell const MWWorld::CellRefList<ESM::Door>& doors = cell->getReadOnlyDoors(); const MWWorld::CellRefList<ESM::Door>::List& refList = doors.mList; MWWorld::CellRefList<ESM::Door>::List::const_iterator it = refList.begin(); osg::Vec3f pos(actor.getRefData().getPosition().asVec3()); pos.z() = 0; osg::Vec3f actorDir = (actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,1,0)); for (; it != refList.end(); ++it) { const MWWorld::LiveCellRef<ESM::Door>& ref = *it; osg::Vec3f doorPos(ref.mData.getPosition().asVec3()); // FIXME: cast const MWWorld::Ptr doorPtr = MWWorld::Ptr(&const_cast<MWWorld::LiveCellRef<ESM::Door> &>(ref), actor.getCell()); int doorState = doorPtr.getClass().getDoorState(doorPtr); float doorRot = ref.mData.getPosition().rot[2] - doorPtr.getCellRef().getPosition().rot[2]; if (doorState != 0 || doorRot != 0) continue; // the door is already opened/opening doorPos.z() = 0; float angle = std::acos(actorDir * (doorPos - pos) / (actorDir.length() * (doorPos - pos).length())); // Allow 60 degrees angle between actor and door if (angle < -osg::PI / 3 || angle > osg::PI / 3) continue; // Door is not close enough if ((pos - doorPos).length2() > minDist*minDist) continue; return doorPtr; // found, stop searching } return MWWorld::Ptr(); // none found }
void Objects::updateObjectCell(const MWWorld::Ptr &ptr) { Ogre::SceneNode *node; MWWorld::CellStore *newCell = ptr.getCell(); if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) { node = mMwRoot->createChildSceneNode(); mCellSceneNodes[newCell] = node; } else { node = mCellSceneNodes[newCell]; } node->addChild(ptr.getRefData().getBaseNode()); /// \note Still unaware how to move aabb and static w/o full rebuild, /// moving static objects may cause problems insertMesh(ptr, MWWorld::Class::get(ptr).getModel(ptr)); }
bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { // If AiEscort has ran for as long or longer then the duration specified // and the duration is not infinite, the package is complete. if (mDuration > 0) { mRemainingDuration -= ((duration*MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600); if (mRemainingDuration <= 0) { mRemainingDuration = mDuration; return true; } } if (!mCellId.empty() && mCellId != actor.getCell()->getCell()->getCellId().mWorldspace) return false; // Not in the correct cell, pause and rely on the player to go back through a teleport door actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false); const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mTargetActorRefId, false); const osg::Vec3f leaderPos = actor.getRefData().getPosition().asVec3(); const osg::Vec3f followerPos = follower.getRefData().getPosition().asVec3(); if ((leaderPos - followerPos).length2() <= mMaxDist * mMaxDist) { const osg::Vec3f dest(mX, mY, mZ); if (pathTo(actor, dest, duration)) //Returns true on path complete { mRemainingDuration = mDuration; return true; } mMaxDist = 450; } else { // Stop moving if the player is too far away MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1); actor.getClass().getMovementSettings(actor).mPosition[1] = 0; mMaxDist = 250; } return false; }
void SoundManager::updateSounds(float duration) { static float timePassed = 0.0; timePassed += duration; if(timePassed < (1.0f/30.0f)) return; timePassed = 0.0f; // Make sure music is still playing if(!isMusicPlaying()) startRandomTitle(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); const ESM::Cell *cell = player.getCell()->cell; Environment env = Env_Normal; if((cell->mData.mFlags&cell->HasWater) && mListenerPos.z < cell->mWater) env = Env_Underwater; mOutput->updateListener( mListenerPos, mListenerDir, mListenerUp, env ); // Check if any sounds are finished playing, and trash them SoundMap::iterator snditer = mActiveSounds.begin(); while(snditer != mActiveSounds.end()) { if(!snditer->first->isPlaying()) mActiveSounds.erase(snditer++); else { snditer->first->update(); snditer++; } } }
void Objects::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &cur) { osg::Node* objectNode = cur.getRefData().getBaseNode(); if (!objectNode) return; MWWorld::CellStore *newCell = cur.getCell(); osg::Group* cellnode; if(mCellSceneNodes.find(newCell) == mCellSceneNodes.end()) { cellnode = new osg::Group; mRootNode->addChild(cellnode); mCellSceneNodes[newCell] = cellnode; } else { cellnode = mCellSceneNodes[newCell]; } osg::UserDataContainer* userDataContainer = objectNode->getUserDataContainer(); if (userDataContainer) for (unsigned int i=0; i<userDataContainer->getNumUserObjects(); ++i) { if (dynamic_cast<PtrHolder*>(userDataContainer->getUserObject(i))) userDataContainer->setUserObject(i, new PtrHolder(cur)); } if (objectNode->getNumParents()) objectNode->getParent(0)->removeChild(objectNode); cellnode->addChild(objectNode); PtrAnimationMap::iterator iter = mObjects.find(old); if(iter != mObjects.end()) { Animation *anim = iter->second; mObjects.erase(iter); anim->updatePtr(cur); mObjects[cur] = anim; } }
virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr me = R()(runtime); std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); Interpreter::Type_Integer count = runtime[0].mInteger; runtime.pop(); Interpreter::Type_Float distance = runtime[0].mFloat; runtime.pop(); Interpreter::Type_Integer direction = runtime[0].mInteger; runtime.pop(); ESM::Position ipos = me.getRefData().getPosition(); Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); if(direction == 0) pos = pos + distance*rot.yAxis(); else if(direction == 1) pos = pos - distance*rot.yAxis(); else if(direction == 2) pos = pos - distance*rot.xAxis(); else if(direction == 3) pos = pos + distance*rot.xAxis(); else throw std::runtime_error ("direction must be 0,1,2 or 3"); ipos.pos[0] = pos.x; ipos.pos[1] = pos.y; ipos.pos[2] = pos.z; ipos.rot[0] = 0; ipos.rot[1] = 0; ipos.rot[2] = 0; MWWorld::CellStore* store = me.getCell(); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); ref.getPtr().getCellRef().mPos = ipos; ref.getPtr().getRefData().setCount(count); MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); }
float InterpreterContext::getDistance (const std::string& name, const std::string& id) const { // NOTE: id may be empty, indicating an implicit reference MWWorld::Ptr ref2; if (id.empty()) ref2 = getReferenceImp(); else ref2 = MWBase::Environment::get().getWorld()->getPtr(id, false); if (ref2.getContainerStore()) // is the object contained? { MWWorld::Ptr container = MWBase::Environment::get().getWorld()->findContainer(ref2); if (!container.isEmpty()) ref2 = container; else throw std::runtime_error("failed to find container ptr"); } const MWWorld::Ptr ref = MWBase::Environment::get().getWorld()->getPtr(name, false); // If the objects are in different worldspaces, return a large value (just like vanilla) if (ref.getCell()->getCell()->getCellId().mWorldspace != ref2.getCell()->getCell()->getCellId().mWorldspace) return std::numeric_limits<float>::max(); double diff[3]; const float* const pos1 = ref.getRefData().getPosition().pos; const float* const pos2 = ref2.getRefData().getPosition().pos; for (int i=0; i<3; ++i) diff[i] = pos1[i] - pos2[i]; return static_cast<float>(std::sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2])); }
bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration) { //Update various Timers mTimer += duration; //Update timer mStuckTimer += duration; //Update stuck timer mTotalTime += duration; //Update total time following ESM::Position pos = actor.getRefData().getPosition(); //position of the actor /// Stops the actor when it gets too close to a unloaded cell { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); const ESM::Cell *cell = actor.getCell()->getCell(); Movement &movement = actor.getClass().getMovementSettings(actor); //Ensure pursuer doesn't leave loaded cells if(cell->mData.mX != player.getCell()->getCell()->mData.mX) { int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX); //check if actor is near the border of an inactive cell. If so, stop walking. if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) { movement.mPosition[1] = 0; return false; } } if(cell->mData.mY != player.getCell()->getCell()->mData.mY) { int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY); //check if actor is near the border of an inactive cell. If so, stop walking. if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) { movement.mPosition[1] = 0; return false; } } } //Start position ESM::Pathgrid::Point start = pos.pos; //*********************** /// Checks if you can't get to the end position at all, adds end position to end of path /// Rebuilds path every quarter of a second, in case the target has moved //*********************** if(mTimer > 0.25) { if(distance(mPrevDest, dest) > 10) { //Only rebuild path if it's moved mPathFinder.buildPath(start, dest, actor.getCell(), true); //Rebuild path, in case the target has moved mPrevDest = dest; } if(!mPathFinder.getPath().empty()) //Path has points in it { ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path if(distance(dest, lastPos) > 100) //End of the path is far from the destination mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go } mTimer = 0; } //************************ /// Checks if you aren't moving; attempts to unstick you //************************ if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2])) //Path finished? return true; else if(mStuckTimer>0.5) //Every half second see if we need to take action to avoid something { /// TODO (tluppi#1#): Use ObstacleCheck here. Not working for some reason //if(mObstacleCheck.check(actor, duration)) { if(distance(start, mStuckPos.pos[0], mStuckPos.pos[1], mStuckPos.pos[2]) < 10 && distance(dest, start) > 20) { //Actually stuck, and far enough away from destination to care // first check if we're walking into a door MWWorld::Ptr door = getNearbyDoor(actor); if(door != MWWorld::Ptr()) // NOTE: checks interior cells only { if(door.getCellRef().mTrap.empty() && mLastDoorChecked != door) { //Open the door if untrapped door.getClass().activate(door, actor).get()->execute(actor); mLastDoorChecked = door; } } 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, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0] + 1, pos.pos[1]))); } } else { //Not stuck, so reset things mStuckTimer = 0; mStuckPos = pos; mLastDoorChecked = MWWorld::Ptr(); //Resets it, in case he gets stuck behind the door again actor.getClass().getMovementSettings(actor).mPosition[1] = 1; //Just run forward } } else { actor.getClass().getMovementSettings(actor).mPosition[1] = 1; //Just run forward the rest of the time } zTurn(actor, Ogre::Degree(mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]))); return false; }
bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) { MWWorld::Ptr target = getTarget(); if (target.isEmpty() || !target.getRefData().getCount() || !target.getRefData().isEnabled() // Really we should be checking whether the target is currently registered // with the MechanicsManager ) return false; // Target is not here right now, wait for it to return actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing); AiFollowStorage& storage = state.get<AiFollowStorage>(); // AiFollow requires the target to be in range and within sight for the initial activation if (!mActive) { storage.mTimer -= duration; if (storage.mTimer < 0) { if ((actor.getRefData().getPosition().asVec3() - target.getRefData().getPosition().asVec3()).length2() < 500*500 && MWBase::Environment::get().getWorld()->getLOS(actor, target)) mActive = true; storage.mTimer = 0.5f; } } if (!mActive) return false; ESM::Position pos = actor.getRefData().getPosition(); //position of the actor // The distances below are approximations based on observations of the original engine. // If only one actor is following the target, it uses 186. // If there are multiple actors following the same target, they form a group with each group member at 313 + (130 * i) distance to the target. short followDistance = 186; std::list<int> followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowingIndices(target); if (followers.size() >= 2) { followDistance = 313; short i = 0; followers.sort(); for (std::list<int>::iterator it = followers.begin(); it != followers.end(); ++it) { if (*it == mFollowIndex) followDistance += 130 * i; ++i; } } if (!mAlwaysFollow) //Update if you only follow for a bit { //Check if we've run out of time if (mDuration > 0) { mRemainingDuration -= ((duration*MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600); if (mRemainingDuration <= 0) { mRemainingDuration = mDuration; return true; } } if ((pos.pos[0]-mX)*(pos.pos[0]-mX) + (pos.pos[1]-mY)*(pos.pos[1]-mY) + (pos.pos[2]-mZ)*(pos.pos[2]-mZ) < followDistance*followDistance) //Close-ish to final position { if (actor.getCell()->isExterior()) //Outside? { if (mCellId == "") //No cell to travel to return true; } else { if (mCellId == actor.getCell()->getCell()->mName) //Cell to travel to return true; } } } //Set the target destination from the actor ESM::Pathgrid::Point dest = target.getRefData().getPosition().pos; if (!storage.mMoving) { const short threshold = 10; // to avoid constant switching between moving/stopping followDistance += threshold; } storage.mMoving = !pathTo(actor, dest, duration, followDistance); // Go to the destination if (storage.mMoving) { //Check if you're far away float dist = distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]); if (dist > 450) actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, true); //Make NPC run else if (dist < 325) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshold actor.getClass().getCreatureStats(actor).setMovementFlag(MWMechanics::CreatureStats::Flag_Run, false); //make NPC walk } return false; }
bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, ESM::Pathgrid::Point dest, float duration) { //Update various Timers mTimer += duration; //Update timer ESM::Position pos = actor.getRefData().getPosition(); //position of the actor /// Stops the actor when it gets too close to a unloaded cell const ESM::Cell *cell = actor.getCell()->getCell(); { MWWorld::Ptr player = getPlayer(); Movement &movement = actor.getClass().getMovementSettings(actor); //Ensure pursuer doesn't leave loaded cells if(cell->mData.mX != player.getCell()->getCell()->mData.mX) { int sideX = PathFinder::sgn(cell->mData.mX - player.getCell()->getCell()->mData.mX); //check if actor is near the border of an inactive cell. If so, stop walking. if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) > sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) { movement.mPosition[1] = 0; return false; } } if(cell->mData.mY != player.getCell()->getCell()->mData.mY) { int sideY = PathFinder::sgn(cell->mData.mY - player.getCell()->getCell()->mData.mY); //check if actor is near the border of an inactive cell. If so, stop walking. if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) > sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f)) { movement.mPosition[1] = 0; return false; } } } //Start position ESM::Pathgrid::Point start = pos.pos; //*********************** /// Checks if you can't get to the end position at all, adds end position to end of path /// Rebuilds path every quarter of a second, in case the target has moved //*********************** if(mTimer > 0.25) { if (doesPathNeedRecalc(dest, cell)) { //Only rebuild path if it's moved mPathFinder.buildSyncedPath(start, dest, actor.getCell(), true); //Rebuild path, in case the target has moved mPrevDest = dest; } if(!mPathFinder.getPath().empty()) //Path has points in it { ESM::Pathgrid::Point lastPos = mPathFinder.getPath().back(); //Get the end of the proposed path if(distance(dest, lastPos) > 100) //End of the path is far from the destination mPathFinder.addPointToPath(dest); //Adds the final destination to the path, to try to get to where you want to go } mTimer = 0; } //************************ /// Checks if you aren't moving; attempts to unstick you //************************ if(mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1])) //Path finished? return true; else { evadeObstacles(actor, duration, pos); } return false; }