forked from mirror/openmw-tes3mp
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.
This commit is contained in:
parent
cd0c283795
commit
43757efdc4
6 changed files with 120 additions and 30 deletions
|
@ -525,7 +525,7 @@ namespace MWClass
|
||||||
float moveSpeed;
|
float moveSpeed;
|
||||||
if(normalizedEncumbrance >= 1.0f)
|
if(normalizedEncumbrance >= 1.0f)
|
||||||
moveSpeed = 0.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()))
|
world->isLevitationEnabled()))
|
||||||
{
|
{
|
||||||
float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +
|
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);
|
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 =
|
MWWorld::LiveCellRef<ESM::Creature> *ref =
|
||||||
ptr.get<ESM::Creature>();
|
ptr.get<ESM::Creature>();
|
||||||
|
@ -686,6 +694,22 @@ namespace MWClass
|
||||||
return ref->mBase->mFlags & ESM::Creature::Flies;
|
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)
|
int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name)
|
||||||
{
|
{
|
||||||
if(name == "left")
|
if(name == "left")
|
||||||
|
|
|
@ -124,7 +124,10 @@ namespace MWClass
|
||||||
return true;
|
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;
|
virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const;
|
||||||
|
|
||||||
|
|
|
@ -363,7 +363,22 @@ namespace MWWorld
|
||||||
return newPtr;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -307,7 +307,10 @@ namespace MWWorld
|
||||||
return false;
|
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;
|
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const;
|
||||||
|
|
||||||
|
|
|
@ -116,8 +116,9 @@ namespace MWWorld
|
||||||
const ESM::Position &refpos = ptr.getRefData().getPosition();
|
const ESM::Position &refpos = ptr.getRefData().getPosition();
|
||||||
Ogre::Vector3 position(refpos.pos);
|
Ogre::Vector3 position(refpos.pos);
|
||||||
|
|
||||||
/* Anything to collide with? */
|
|
||||||
OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle());
|
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())
|
if(!physicActor || !physicActor->getCollisionMode())
|
||||||
{
|
{
|
||||||
return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
return position + (Ogre::Quaternion(Ogre::Radian(refpos.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
||||||
|
@ -125,36 +126,67 @@ namespace MWWorld
|
||||||
* movement * time;
|
* movement * time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Anything to collide with? */
|
||||||
btCollisionObject *colobj = physicActor->getCollisionBody();
|
btCollisionObject *colobj = physicActor->getCollisionBody();
|
||||||
Ogre::Vector3 halfExtents = physicActor->getHalfExtents();
|
Ogre::Vector3 halfExtents = physicActor->getHalfExtents();
|
||||||
position.z += halfExtents.z;
|
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;
|
||||||
|
|
||||||
waterlevel -= halfExtents.z * 0.5;
|
/*
|
||||||
|
* A 3/4 submerged example
|
||||||
|
*
|
||||||
|
* +---+
|
||||||
|
* | |
|
||||||
|
* | | <- waterlevel
|
||||||
|
* | |
|
||||||
|
* | | <- newPosition <- actorWaterlevel
|
||||||
|
* | |
|
||||||
|
* | |
|
||||||
|
* | |
|
||||||
|
* +---+ <- position
|
||||||
|
*/
|
||||||
|
|
||||||
OEngine::Physic::ActorTracer tracer;
|
OEngine::Physic::ActorTracer tracer;
|
||||||
bool wasOnGround = false;
|
bool wasOnGround = false;
|
||||||
bool isOnGround = false;
|
bool isOnGround = false;
|
||||||
Ogre::Vector3 inertia(0.0f);
|
Ogre::Vector3 inertia(0.0f);
|
||||||
Ogre::Vector3 velocity;
|
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)*
|
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;
|
Ogre::Quaternion(Ogre::Radian(refpos.rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)) * movement;
|
||||||
}
|
}
|
||||||
else
|
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(!physicActor->getOnGround())
|
||||||
{
|
{
|
||||||
// If falling, add part of the incoming velocity with the current inertia
|
// 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))
|
if(!(movement.z > 0.0f))
|
||||||
{
|
{
|
||||||
wasOnGround = physicActor->getOnGround();
|
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)
|
if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope)
|
||||||
isOnGround = true;
|
isOnGround = true;
|
||||||
}
|
}
|
||||||
|
@ -163,24 +195,36 @@ namespace MWWorld
|
||||||
if(isOnGround)
|
if(isOnGround)
|
||||||
{
|
{
|
||||||
// if we're on the ground, don't try to fall
|
// 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;
|
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 &&
|
// If not able to fly, walk or bipedal don't allow to move out of water
|
||||||
nextpos.z > waterlevel && newPosition.z <= waterlevel)
|
// 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);
|
const Ogre::Vector3 down(0,0,-1);
|
||||||
Ogre::Real movelen = velocity.normalise();
|
Ogre::Real movelen = velocity.normalise();
|
||||||
Ogre::Vector3 reflectdir = velocity.reflect(down);
|
Ogre::Vector3 reflectdir = velocity.reflect(down);
|
||||||
reflectdir.normalise();
|
reflectdir.normalise();
|
||||||
velocity = slide(reflectdir, down)*movelen;
|
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
|
// trace to where character would go if there were no obstructions
|
||||||
|
@ -189,14 +233,15 @@ namespace MWWorld
|
||||||
// check for obstructions
|
// check for obstructions
|
||||||
if(tracer.mFraction >= 1.0f)
|
if(tracer.mFraction >= 1.0f)
|
||||||
{
|
{
|
||||||
newPosition = tracer.mEndPos;
|
newPosition = tracer.mEndPos; // ok to move, so set newPosition
|
||||||
remainingTime *= (1.0f-tracer.mFraction);
|
remainingTime *= (1.0f-tracer.mFraction); // TODO: remainingTime is no longer used so don't set it?
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We hit something. Try to step up onto it.
|
// 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.
|
||||||
isOnGround = !(newPosition.z < waterlevel || isFlying); // Only on the ground if there's gravity
|
if((canWalk || isBipedal || isNpc) && stepMove(colobj, newPosition, velocity, remainingTime, engine))
|
||||||
|
isOnGround = !((newPosition.z < actorWaterlevel) || isFlying); // Only on the ground if there's gravity
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Can't move this way, try to find another spot along the plane
|
// 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
|
// Do not allow sliding upward if there is gravity. Stepping will have taken
|
||||||
// care of that.
|
// care of that.
|
||||||
if(!(newPosition.z < waterlevel || isFlying))
|
if(!(newPosition.z < actorWaterlevel) || isFlying)
|
||||||
velocity.z = std::min(velocity.z, 0.0f);
|
velocity.z = std::min(velocity.z, 0.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isOnGround || wasOnGround)
|
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)
|
if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope)
|
||||||
{
|
{
|
||||||
newPosition.z = tracer.mEndPos.z + 1.0f;
|
newPosition.z = tracer.mEndPos.z + 1.0f;
|
||||||
|
@ -224,7 +269,7 @@ namespace MWWorld
|
||||||
isOnGround = false;
|
isOnGround = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isOnGround || newPosition.z < waterlevel || isFlying)
|
if(isOnGround || ((newPosition.z) < actorWaterlevel) || isFlying)
|
||||||
physicActor->setInertialForce(Ogre::Vector3(0.0f));
|
physicActor->setInertialForce(Ogre::Vector3(0.0f));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -236,7 +281,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
physicActor->setOnGround(isOnGround);
|
physicActor->setOnGround(isOnGround);
|
||||||
|
|
||||||
newPosition.z -= halfExtents.z;
|
newPosition.z -= halfExtents.z; // remove what was added at the beggining
|
||||||
return newPosition;
|
return newPosition;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -574,7 +619,7 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
float waterlevel = -std::numeric_limits<float>::max();
|
float waterlevel = -std::numeric_limits<float>::max();
|
||||||
const ESM::Cell *cell = iter->first.getCell()->getCell();
|
const ESM::Cell *cell = iter->first.getCell()->getCell();
|
||||||
if(cell->hasWater())
|
if(cell->hasWater()) // should also check cell->mHasWaterLevelRecord
|
||||||
waterlevel = cell->mWater;
|
waterlevel = cell->mWater;
|
||||||
|
|
||||||
float oldHeight = iter->first.getRefData().getPosition().pos[2];
|
float oldHeight = iter->first.getRefData().getPosition().pos[2];
|
||||||
|
|
|
@ -1649,7 +1649,7 @@ namespace MWWorld
|
||||||
if (ptr.getClass().getCreatureStats(ptr).isDead())
|
if (ptr.getClass().getCreatureStats(ptr).isDead())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (ptr.getClass().isFlying(ptr))
|
if (ptr.getClass().canFly(ptr))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr);
|
const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr);
|
||||||
|
|
Loading…
Reference in a new issue