diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 33c4390a0a..c66bda09fa 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -525,7 +525,7 @@ namespace MWClass float moveSpeed; if(normalizedEncumbrance >= 1.0f) moveSpeed = 0.0f; - else if(isFlying(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && + else if(canFly(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 && world->isLevitationEnabled())) { float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + @@ -678,7 +678,15 @@ namespace MWClass return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } - bool Creature::isFlying(const MWWorld::Ptr &ptr) const + bool Creature::isBipedal(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mFlags & ESM::Creature::Bipedal; + } + + bool Creature::canFly(const MWWorld::Ptr &ptr) const { MWWorld::LiveCellRef *ref = ptr.get(); @@ -686,6 +694,22 @@ namespace MWClass return ref->mBase->mFlags & ESM::Creature::Flies; } + bool Creature::canSwim(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mFlags & ESM::Creature::Swims; + } + + bool Creature::canWalk(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = + ptr.get(); + + return ref->mBase->mFlags & ESM::Creature::Walks; + } + int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name) { if(name == "left") diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 6df6db297f..c1bcb8739e 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -124,7 +124,10 @@ namespace MWClass return true; } - virtual bool isFlying (const MWWorld::Ptr &ptr) const; + virtual bool isBipedal (const MWWorld::Ptr &ptr) const; + virtual bool canFly (const MWWorld::Ptr &ptr) const; + virtual bool canSwim (const MWWorld::Ptr &ptr) const; + virtual bool canWalk (const MWWorld::Ptr &ptr) const; virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 2110086d37..39d48f95b8 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -363,7 +363,22 @@ namespace MWWorld return newPtr; } - bool Class::isFlying(const Ptr &ptr) const + bool Class::isBipedal(const Ptr &ptr) const + { + return false; + } + + bool Class::canFly(const Ptr &ptr) const + { + return false; + } + + bool Class::canSwim(const Ptr &ptr) const + { + return false; + } + + bool Class::canWalk(const Ptr &ptr) const { return false; } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index ad2cc3af47..739fd5942e 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -307,7 +307,10 @@ namespace MWWorld return false; } - virtual bool isFlying(const MWWorld::Ptr& ptr) const; + virtual bool isBipedal(const MWWorld::Ptr& ptr) const; + virtual bool canFly(const MWWorld::Ptr& ptr) const; + virtual bool canSwim(const MWWorld::Ptr& ptr) const; + virtual bool canWalk(const MWWorld::Ptr& ptr) const; virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 1657cf2670..3c0c3ffaa3 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -130,31 +130,52 @@ namespace MWWorld position.z += halfExtents.z; waterlevel -= halfExtents.z * 0.5; + /* + * A 3/4 submerged example + * + * +---+ + * | | + * | | <- (original waterlevel) + * | | + * | | <- position <- waterlevel + * | | + * | | + * | | + * +---+ <- (original position) + */ OEngine::Physic::ActorTracer tracer; bool wasOnGround = false; bool isOnGround = false; Ogre::Vector3 inertia(0.0f); Ogre::Vector3 velocity; - if(position.z < waterlevel || isFlying) + + bool canWalk = ptr.getClass().canWalk(ptr); + bool isBipedal = ptr.getClass().isBipedal(ptr); + bool isNpc = ptr.getClass().isNpc(); + + if(position.z < waterlevel || isFlying) // under water by 3/4 or can fly { + // TODO: Shouldn't water have higher drag in calculating velocity? velocity = (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z)* Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)) * movement; } else { velocity = Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) * movement; + // not in water nor can fly, so need to deal with gravity if(!physicActor->getOnGround()) { // If falling, add part of the incoming velocity with the current inertia - velocity = velocity*time + physicActor->getInertialForce(); + velocity = velocity * time + physicActor->getInertialForce(); } - inertia = velocity; + inertia = velocity; // REM velocity is for z axis only in this code block if(!(movement.z > 0.0f)) { wasOnGround = physicActor->getOnGround(); - tracer.doTrace(colobj, position, position-Ogre::Vector3(0,0,2), engine); + // TODO: Find out if there is a significance with the value 2 used here + tracer.doTrace(colobj, position, position - Ogre::Vector3(0,0,2), engine); if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope) isOnGround = true; } @@ -163,24 +184,38 @@ namespace MWWorld if(isOnGround) { // if we're on the ground, don't try to fall - velocity.z = std::max(0.0f, velocity.z); + velocity.z = std::max(0.0f, velocity.z); // NOTE: two different velocity assignments above } Ogre::Vector3 newPosition = position; + /* + * A loop to find newPosition using tracer, if successful different from the starting position. + * nextpos is the local variable used to find potential newPosition, using velocity and remainingTime + * The initial velocity was set earlier (see above). + */ float remainingTime = time; - for(int iterations = 0;iterations < sMaxIterations && remainingTime > 0.01f;++iterations) + for(int iterations = 0; iterations < sMaxIterations && remainingTime > 0.01f; ++iterations) { - Ogre::Vector3 nextpos = newPosition + velocity*remainingTime; + Ogre::Vector3 nextpos = newPosition + velocity * remainingTime; - if(newPosition.z < waterlevel && !isFlying && - nextpos.z > waterlevel && newPosition.z <= waterlevel) + // If not able to fly, walk or bipedal don't allow to move out of water + // TODO: this if condition may not work for large creatures or situations + // where the creature gets above the waterline for some reason + if(newPosition.z < waterlevel && // started 3/4 under water + !isFlying && // can't fly + !canWalk && // can't walk + !isBipedal && // not bipedal (assume bipedals can walk) + !isNpc && // FIXME: shouldn't really need this + nextpos.z > waterlevel && // but about to go above water + newPosition.z <= waterlevel) { const Ogre::Vector3 down(0,0,-1); Ogre::Real movelen = velocity.normalise(); Ogre::Vector3 reflectdir = velocity.reflect(down); reflectdir.normalise(); velocity = slide(reflectdir, down)*movelen; - continue; + // NOTE: remainingTime is unchanged before the loop continues + continue; // velocity updated, calculate nextpos again } // trace to where character would go if there were no obstructions @@ -189,13 +224,14 @@ namespace MWWorld // check for obstructions if(tracer.mFraction >= 1.0f) { - newPosition = tracer.mEndPos; - remainingTime *= (1.0f-tracer.mFraction); + newPosition = tracer.mEndPos; // ok to move, so set newPosition + remainingTime *= (1.0f-tracer.mFraction); // FIXME: remainingTime is no longer used so don't set it? break; } // We hit something. Try to step up onto it. - if(stepMove(colobj, newPosition, velocity, remainingTime, engine)) + // NOTE: May need to stop slaughterfish step out of the water. + if((canWalk || isBipedal || isNpc) && stepMove(colobj, newPosition, velocity, remainingTime, engine)) isOnGround = !(newPosition.z < waterlevel || isFlying); // Only on the ground if there's gravity else { @@ -214,7 +250,7 @@ namespace MWWorld if(isOnGround || wasOnGround) { - tracer.doTrace(colobj, newPosition, newPosition-Ogre::Vector3(0,0,sStepSize+2.0f), engine); + tracer.doTrace(colobj, newPosition, newPosition - Ogre::Vector3(0,0,sStepSize+2.0f), engine); if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope) { newPosition.z = tracer.mEndPos.z + 1.0f; @@ -236,7 +272,7 @@ namespace MWWorld } physicActor->setOnGround(isOnGround); - newPosition.z -= halfExtents.z; + newPosition.z -= halfExtents.z; // remove what was added at the beggining return newPosition; } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 992ecc9607..62bdd38ea1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1649,7 +1649,7 @@ namespace MWWorld if (ptr.getClass().getCreatureStats(ptr).isDead()) return false; - if (ptr.getClass().isFlying(ptr)) + if (ptr.getClass().canFly(ptr)) return true; const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr);