Feature #1030 - partial fix to stop creatures unable to walk/fly to come out of water. Does not necessarily handle situations where they are already out of water, however.

actorid
cc9cii 11 years ago
parent cd0c283795
commit 43757efdc4

@ -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<ESM::Creature>().insert(*ref), &cell);
}
bool Creature::isFlying(const MWWorld::Ptr &ptr) const
bool Creature::isBipedal(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
return ref->mBase->mFlags & ESM::Creature::Bipedal;
}
bool Creature::canFly(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
@ -686,6 +694,22 @@ namespace MWClass
return ref->mBase->mFlags & ESM::Creature::Flies;
}
bool Creature::canSwim(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
return ref->mBase->mFlags & ESM::Creature::Swims;
}
bool Creature::canWalk(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
return ref->mBase->mFlags & ESM::Creature::Walks;
}
int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name)
{
if(name == "left")

@ -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;

@ -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;
}

@ -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;

@ -116,8 +116,9 @@ namespace MWWorld
const ESM::Position &refpos = ptr.getRefData().getPosition();
Ogre::Vector3 position(refpos.pos);
/* Anything to collide with? */
OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle());
// If no need to check for collision simply return the new position.
if(!physicActor || !physicActor->getCollisionMode())
{
return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
@ -125,36 +126,67 @@ namespace MWWorld
* movement * time;
}
/* Anything to collide with? */
btCollisionObject *colobj = physicActor->getCollisionBody();
Ogre::Vector3 halfExtents = physicActor->getHalfExtents();
position.z += halfExtents.z;
waterlevel -= halfExtents.z * 0.5;
Ogre::Vector3 newPosition = position; // FIXME: not sure if a copy is needed
newPosition.z += halfExtents.z; // NOTE: remember to restore before returning
float actorWaterlevel = waterlevel - halfExtents.z * 0.5;
/*
* A 3/4 submerged example
*
* +---+
* | |
* | | <- waterlevel
* | |
* | | <- newPosition <- actorWaterlevel
* | |
* | |
* | |
* +---+ <- 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(!canWalk && !isBipedal && !isNpc && (position.z >= waterlevel))
//{
//std::cout << "Swim Creature \""<<ptr.getClass().getName(ptr)<<"\""
//<< " above waterline, z pos = "<<std::to_string(position.z ) << std::endl;
//}
// FIXME: Choose one
//if((position.z < waterlevel) || isFlying) // under water by any amount or can fly
if((newPosition.z < actorWaterlevel) || isFlying) // 3/4 under water 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;
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 +195,36 @@ 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;
if(newPosition.z < waterlevel && !isFlying &&
nextpos.z > waterlevel && newPosition.z <= waterlevel)
Ogre::Vector3 nextpos = newPosition + velocity * remainingTime;
// If not able to fly, walk or bipedal don't allow to move out of water
// FIXME: this if condition may not work for large creatures or situations
// where the creature gets above the waterline for some reason
if(//(newPosition.z + halfExtents.z) <= waterlevel && // started fully 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 + halfExtents.z) > waterlevel)) // but about to go above water
{
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;
// FIXME: 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,14 +233,15 @@ 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); // TODO: 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))
isOnGround = !(newPosition.z < waterlevel || isFlying); // Only on the ground if there's gravity
// NOTE: May need to stop slaughterfish step out of the water.
if((canWalk || isBipedal || isNpc) && stepMove(colobj, newPosition, velocity, remainingTime, engine))
isOnGround = !((newPosition.z < actorWaterlevel) || isFlying); // Only on the ground if there's gravity
else
{
// Can't move this way, try to find another spot along the plane
@ -207,14 +252,14 @@ namespace MWWorld
// Do not allow sliding upward if there is gravity. Stepping will have taken
// care of that.
if(!(newPosition.z < waterlevel || isFlying))
if(!(newPosition.z < actorWaterlevel) || isFlying)
velocity.z = std::min(velocity.z, 0.0f);
}
}
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;
@ -224,7 +269,7 @@ namespace MWWorld
isOnGround = false;
}
if(isOnGround || newPosition.z < waterlevel || isFlying)
if(isOnGround || ((newPosition.z) < actorWaterlevel) || isFlying)
physicActor->setInertialForce(Ogre::Vector3(0.0f));
else
{
@ -236,7 +281,7 @@ namespace MWWorld
}
physicActor->setOnGround(isOnGround);
newPosition.z -= halfExtents.z;
newPosition.z -= halfExtents.z; // remove what was added at the beggining
return newPosition;
}
};
@ -574,7 +619,7 @@ namespace MWWorld
{
float waterlevel = -std::numeric_limits<float>::max();
const ESM::Cell *cell = iter->first.getCell()->getCell();
if(cell->hasWater())
if(cell->hasWater()) // should also check cell->mHasWaterLevelRecord
waterlevel = cell->mWater;
float oldHeight = iter->first.getRefData().getPosition().pos[2];

@ -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);

Loading…
Cancel
Save