diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 826619b5d..77644665f 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -15,7 +15,7 @@ #include #include - +#include #include #include "model/doc/document.hpp" @@ -35,6 +35,8 @@ CS::Editor::Editor (OgreInit::OgreInit& ogreInit) ogreInit.init ((mCfgMgr.getUserConfigPath() / "opencsOgre.log").string()); + NifOgre::Loader::setShowMarkers(true); + mOverlaySystem.reset (new CSVRender::OverlaySystem); Bsa::registerResources (Files::Collections (config.first, !mFsStrict), config.second, true, diff --git a/apps/opencs/view/world/physicssystem.cpp b/apps/opencs/view/world/physicssystem.cpp index 57909f4d3..2cbe17dcf 100644 --- a/apps/opencs/view/world/physicssystem.cpp +++ b/apps/opencs/view/world/physicssystem.cpp @@ -16,7 +16,7 @@ namespace CSVWorld PhysicsSystem::PhysicsSystem() { // Create physics. shapeLoader is deleted by the physic engine - NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(); + NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader(true); mEngine = new OEngine::Physic::PhysicEngine(shapeLoader); } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index a454d816a..64434a304 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -42,6 +42,12 @@ namespace { +bool isConscious(const MWWorld::Ptr& ptr) +{ + const MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); + return !stats.isDead() && !stats.getKnockedDown(); +} + void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& actor) { if (bound) @@ -1101,12 +1107,12 @@ namespace MWMechanics updateCrimePersuit(iter->first, duration); if (iter->first != player) - iter->first.getClass().getCreatureStats(iter->first).getAiSequence().execute(iter->first,iter->second->getAiState(), duration); - - CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); - if(!stats.isDead()) { - if (stats.getAiSequence().isInCombat()) hostilesCount++; + CreatureStats &stats = iter->first.getClass().getCreatureStats(iter->first); + if (isConscious(iter->first)) + stats.getAiSequence().execute(iter->first,iter->second->getAiState(), duration); + + if (stats.getAiSequence().isInCombat() && !stats.isDead()) hostilesCount++; } } @@ -1515,7 +1521,9 @@ namespace MWMechanics for (PtrActorMap::iterator it = map.begin(); it != map.end(); ++it) { MWWorld::Ptr ptr = it->first; - if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) + if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr() + || !isConscious(ptr) + || ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0) continue; MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); seq.fastForward(ptr, it->second->getAiState()); diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 364e6effe..533bcd17c 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -152,8 +152,7 @@ bool AiSequence::isPackageDone() const void AiSequence::execute (const MWWorld::Ptr& actor, AiState& state,float duration) { - if(actor != MWBase::Environment::get().getWorld()->getPlayerPtr() - && !actor.getClass().getCreatureStats(actor).getKnockedDown()) + if(actor != MWBase::Environment::get().getWorld()->getPlayerPtr()) { if (!mPackages.empty()) { diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index c3278a5ad..fbb147b34 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -308,22 +308,26 @@ 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) - && actor.getRefData().getPosition().pos[2] < 3000 && - MWBase::Environment::get().getSoundManager()->sayDone(actor)) + && MWBase::Environment::get().getSoundManager()->sayDone(actor)) { - float roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 10000; // [0, 9999] MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); - // Don't bother if the player is out of hearing range static float fVoiceIdleOdds = MWBase::Environment::get().getWorld()->getStore() .get().find("fVoiceIdleOdds")->getFloat(); - float x = fVoiceIdleOdds * MWBase::Environment::get().getFrameDuration(); + float roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 10000; + + // 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.6 * (MWBase::Environment::get().getFrameDuration() / 0.1); // 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 && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) < 1500*1500 + if (roll < x && Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(pos.pos)) + < 3000*3000 // maybe should be fAudioVoiceDefaultMaxDistance*fAudioMaxDistanceMult instead && MWBase::Environment::get().getWorld()->getLOS(player, actor)) MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); } diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 77c5b4460..0090ca010 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -70,13 +70,15 @@ void animateCollisionShapes (std::map(instance.mCompound); + btCompoundShape* compound = static_cast(instance.mCompound); btTransform trans; - trans.setOrigin(BtOgre::Convert::toBullet(bone->_getDerivedPosition())); + trans.setOrigin(BtOgre::Convert::toBullet(bone->_getDerivedPosition()) * compound->getLocalScaling()); trans.setRotation(BtOgre::Convert::toBullet(bone->_getDerivedOrientation())); - compound->getChildShape(shapeIt->second)->setLocalScaling(BtOgre::Convert::toBullet(bone->_getDerivedScale())); + compound->getChildShape(shapeIt->second)->setLocalScaling( + compound->getLocalScaling() * + BtOgre::Convert::toBullet(bone->_getDerivedScale())); compound->updateChildTransform(shapeIt->second, trans); } @@ -348,6 +350,8 @@ namespace MWWorld velocity *= 1.f-(fStromWalkMult * (angle.valueDegrees()/180.f)); } + Ogre::Vector3 origVelocity = velocity; + Ogre::Vector3 newPosition = position; /* * A loop to find newPosition using tracer, if successful different from the starting position. @@ -425,10 +429,18 @@ namespace MWWorld else { // Can't move this way, try to find another spot along the plane - Ogre::Real movelen = velocity.normalise(); + Ogre::Vector3 direction = velocity; + Ogre::Real movelen = direction.normalise(); Ogre::Vector3 reflectdir = velocity.reflect(tracer.mPlaneNormal); reflectdir.normalise(); - velocity = slide(reflectdir, tracer.mPlaneNormal)*movelen; + + Ogre::Vector3 newVelocity = slide(reflectdir, tracer.mPlaneNormal)*movelen; + if ((newVelocity-velocity).squaredLength() < 0.01) + break; + if (velocity.dotProduct(origVelocity) <= 0.f) + break; + + velocity = newVelocity; // Do not allow sliding upward if there is gravity. Stepping will have taken // care of that. diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index d7548018f..286721df4 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -21,6 +21,18 @@ namespace { + + void addObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics, + MWRender::RenderingManager& rendering) + { + std::string model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr)); + std::string id = ptr.getClass().getId(ptr); + if (id == "prisonmarker" || id == "divinemarker" || id == "templemarker" || id == "northmarker") + model = ""; // marker objects that have a hardcoded function in the game logic, should be hidden from the player + rendering.addObject(ptr, model); + ptr.getClass().insertObject (ptr, model, physics); + } + void updateObjectLocalRotation (const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics, MWRender::RenderingManager& rendering) { @@ -80,10 +92,7 @@ namespace { try { - const std::string& model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr)); - mRendering.addObject(ptr, model); - ptr.getClass().insertObject (ptr, model, mPhysics); - + addObject(ptr, mPhysics, mRendering); updateObjectLocalRotation(ptr, mPhysics, mRendering); if (ptr.getRefData().getBaseNode()) { @@ -530,9 +539,7 @@ namespace MWWorld void Scene::addObjectToScene (const Ptr& ptr) { - const std::string& model = Misc::ResourceHelpers::correctActorModelPath(ptr.getClass().getModel(ptr)); - mRendering.addObject(ptr, model); - ptr.getClass().insertObject (ptr, model, *mPhysics); + addObject(ptr, *mPhysics, mRendering); MWBase::Environment::get().getWorld()->rotateObject(ptr, 0, 0, 0, true); MWBase::Environment::get().getWorld()->scaleObject(ptr, ptr.getCellRef().getScale()); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 011f6c330..104605e65 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2107,6 +2107,9 @@ namespace MWWorld Ogre::Vector3 playerPos(refdata.getPosition().pos); const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); + if (!physactor) + throw std::runtime_error("can't find player"); + if((!physactor->getOnGround()&&physactor->getCollisionMode()) || isUnderwater(currentCell, playerPos) || isWalkingOnWater(player)) return 2; if((currentCell->getCell()->mData.mFlags&ESM::Cell::NoSleep) || @@ -2326,9 +2329,15 @@ namespace MWWorld if (!targetActor.getRefData().getBaseNode() || !targetActor.getRefData().getBaseNode()) return false; // not in active cell - Ogre::Vector3 halfExt1 = mPhysEngine->getCharacter(actor.getRefData().getHandle())->getHalfExtents(); + OEngine::Physic::PhysicActor* actor1 = mPhysEngine->getCharacter(actor.getRefData().getHandle()); + OEngine::Physic::PhysicActor* actor2 = mPhysEngine->getCharacter(targetActor.getRefData().getHandle()); + + if (!actor1 || !actor2) + return false; + + Ogre::Vector3 halfExt1 = actor1->getHalfExtents(); const float* pos1 = actor.getRefData().getPosition().pos; - Ogre::Vector3 halfExt2 = mPhysEngine->getCharacter(targetActor.getRefData().getHandle())->getHalfExtents(); + Ogre::Vector3 halfExt2 = actor2->getHalfExtents(); const float* pos2 = targetActor.getRefData().getPosition().pos; btVector3 from(pos1[0],pos1[1],pos1[2]+halfExt1.z*2*0.9); // eye level diff --git a/components/nifbullet/bulletnifloader.cpp b/components/nifbullet/bulletnifloader.cpp index 130787797..35d552726 100644 --- a/components/nifbullet/bulletnifloader.cpp +++ b/components/nifbullet/bulletnifloader.cpp @@ -158,7 +158,7 @@ void ManualBulletShapeLoader::loadResource(Ogre::Resource *resource) mShape->mAutogenerated = hasAutoGeneratedCollision(node); //do a first pass - handleNode(node,0,false,false,false); + handleNode(node,0,false,false); if(mBoundingBox != NULL) { @@ -232,7 +232,7 @@ bool ManualBulletShapeLoader::hasAutoGeneratedCollision(Nif::Node const * rootNo void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, bool isCollisionNode, - bool raycasting, bool isMarker, bool isAnimated) + bool raycasting, bool isAnimated) { // Accumulate the flags from all the child nodes. This works for all // the flags we currently use, at least. @@ -254,13 +254,6 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, if(node->recType == Nif::RC_AvoidNode) flags |= 0x800; - // Marker objects - /// \todo don't do this in the editor - std::string nodename = node->name; - Misc::StringUtils::toLower(nodename); - if (nodename.find("marker") != std::string::npos) - isMarker = true; - // Check for extra data Nif::Extra const *e = node; while (!e->extra.empty()) @@ -281,16 +274,14 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, // No collision. Use an internal flag setting to mark this. flags |= 0x800; } - else if (sd->string == "MRK") + else if (sd->string == "MRK" && !mShowMarkers) // Marker objects. These are only visible in the - // editor. Until and unless we add an editor component to - // the engine, just skip this entire node. - isMarker = true; + // editor. + return; } } - if ( (isCollisionNode || (mShape->mAutogenerated && !raycasting)) - && (!isMarker || (!mShape->mAutogenerated && !raycasting))) + if (isCollisionNode || (mShape->mAutogenerated && !raycasting)) { // NOTE: a trishape with hasBounds=true, but no BBoxCollision flag should NOT go through handleNiTriShape! // It must be ignored completely. @@ -319,7 +310,7 @@ void ManualBulletShapeLoader::handleNode(const Nif::Node *node, int flags, for(size_t i = 0;i < list.length();i++) { if(!list[i].empty()) - handleNode(list[i].getPtr(), flags, isCollisionNode, raycasting, isMarker, isAnimated); + handleNode(list[i].getPtr(), flags, isCollisionNode, raycasting, isAnimated); } } } @@ -374,10 +365,17 @@ void ManualBulletShapeLoader::handleNiTriShape(const Nif::NiTriShape *shape, int TriangleMeshShape* childShape = new TriangleMeshShape(childMesh,true); - childShape->setLocalScaling(btVector3(transform[0][0], transform[1][1], transform[2][2])); - + float scale = shape->trafo.scale; + const Nif::Node* parent = shape; + while (parent->parent) + { + parent = parent->parent; + scale *= parent->trafo.scale; + } Ogre::Quaternion q = transform.extractQuaternion(); Ogre::Vector3 v = transform.getTrans(); + childShape->setLocalScaling(btVector3(scale, scale, scale)); + btTransform trans(btQuaternion(q.x, q.y, q.z, q.w), btVector3(v.x, v.y, v.z)); if (raycasting) diff --git a/components/nifbullet/bulletnifloader.hpp b/components/nifbullet/bulletnifloader.hpp index f95bdfccf..56d98834d 100644 --- a/components/nifbullet/bulletnifloader.hpp +++ b/components/nifbullet/bulletnifloader.hpp @@ -67,11 +67,12 @@ struct TriangleMeshShape : public btBvhTriangleMeshShape class ManualBulletShapeLoader : public OEngine::Physic::BulletShapeLoader { public: - ManualBulletShapeLoader() + ManualBulletShapeLoader(bool showMarkers=false) : mShape(NULL) , mStaticMesh(NULL) , mCompoundShape(NULL) , mBoundingBox(NULL) + , mShowMarkers(showMarkers) { } @@ -107,7 +108,7 @@ private: *Parse a node. */ void handleNode(Nif::Node const *node, int flags, bool isCollisionNode, - bool raycasting, bool isMarker, bool isAnimated=false); + bool raycasting, bool isAnimated=false); /** *Helper function @@ -130,6 +131,8 @@ private: btBoxShape *mBoundingBox; std::set mControlledNodes; + + bool mShowMarkers; }; diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index 7ec2c2c24..17df7a3cd 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -689,6 +689,14 @@ public: */ class NIFObjectLoader { + static bool sShowMarkers; +public: + static void setShowMarkers(bool show) + { + sShowMarkers = show; + } +private: + static void warn(const std::string &msg) { std::cerr << "NIFObjectLoader: Warn: " << msg << std::endl; @@ -1176,11 +1184,6 @@ class NIFObjectLoader if(node->recType == Nif::RC_RootCollisionNode) isRootCollisionNode = true; - // Marker objects: just skip the entire node branch - /// \todo don't do this in the editor - if (node->name.find("marker") != std::string::npos) - return; - if(node->recType == Nif::RC_NiBSAnimationNode) animflags |= node->flags; else if(node->recType == Nif::RC_NiBSParticleNode) @@ -1213,7 +1216,7 @@ class NIFObjectLoader const Nif::NiStringExtraData *sd = static_cast(e.getPtr()); // String markers may contain important information // affecting the entire subtree of this obj - if(sd->string == "MRK") + if(sd->string == "MRK" && !sShowMarkers) { // Marker objects. These meshes are only visible in the // editor. @@ -1476,5 +1479,14 @@ void Loader::createKfControllers(Ogre::Entity *skelBase, NIFObjectLoader::loadKf(skelBase->getSkeleton(), name, textKeys, ctrls); } +bool Loader::sShowMarkers = false; +bool NIFObjectLoader::sShowMarkers = false; + +void Loader::setShowMarkers(bool show) +{ + sShowMarkers = show; + NIFObjectLoader::setShowMarkers(show); +} + } // namespace NifOgre diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index fd5ddca7d..c13532644 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -110,10 +110,18 @@ public: std::string name, const std::string &group="General"); + /// Set whether or not nodes marked as "MRK" should be shown. + /// These should be hidden ingame, but visible in the editior. + /// Default: false. + static void setShowMarkers(bool show); + static void createKfControllers(Ogre::Entity *skelBase, const std::string &name, TextKeyMap &textKeys, std::vector > &ctrls); + +private: + static bool sShowMarkers; }; // FIXME: Should be with other general Ogre extensions. diff --git a/extern/oics/ICSInputControlSystem.cpp b/extern/oics/ICSInputControlSystem.cpp index 7dc026c7e..e1bd58728 100644 --- a/extern/oics/ICSInputControlSystem.cpp +++ b/extern/oics/ICSInputControlSystem.cpp @@ -36,6 +36,9 @@ namespace ICS , mDetectingBindingControl(NULL) , mLog(log) , mXmouseAxisBinded(false), mYmouseAxisBinded(false) + , mClientHeight(1) + , mClientWidth(1) + , mDetectingBindingDirection(Control::STOP) { ICS_LOG(" - Creating InputControlSystem - ");