Implement collision script instructions (Fixes #1111)

deque
scrawl 11 years ago
parent ccde462308
commit 543bb22e8f

@ -404,6 +404,15 @@ namespace MWBase
virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object
virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object
virtual bool getPlayerCollidingWith(const MWWorld::Ptr& object) = 0; ///< @return true if the player is colliding with \a object
virtual bool getActorCollidingWith (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is colliding with \a object
virtual void hurtStandingActors (const MWWorld::Ptr& object, float dmgPerSecond) = 0;
///< Apply a health difference to any actors standing on \a object.
/// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed.
virtual void hurtCollidingActors (const MWWorld::Ptr& object, float dmgPerSecond) = 0;
///< Apply a health difference to any actors colliding with \a object.
/// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed.
virtual float getWindSpeed() = 0; virtual float getWindSpeed() = 0;
virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out) = 0; virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out) = 0;

@ -405,5 +405,13 @@ op 0x200024c: Face
op 0x200024d: Face, explicit op 0x200024d: Face, explicit
op 0x200024e: GetStat (dummy function) op 0x200024e: GetStat (dummy function)
op 0x200024f: GetStat (dummy function), explicit op 0x200024f: GetStat (dummy function), explicit
op 0x2000250: GetCollidingPC
op 0x2000251: GetCollidingPC, explicit
op 0x2000252: GetCollidingActor
op 0x2000253: GetCollidingActor, explicit
op 0x2000254: HurtStandingActor
op 0x2000255: HurtStandingActor, explicit
op 0x2000256: HurtCollidingActor
op 0x2000257: HurtCollidingActor, explicit
opcodes 0x2000250-0x3ffffff unused opcodes 0x2000258-0x3ffffff unused

@ -611,6 +611,60 @@ namespace MWScript
} }
}; };
template <class R>
class OpGetCollidingPc : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
runtime.push (MWBase::Environment::get().getWorld()->getPlayerStandingOn(ptr));
}
};
template <class R>
class OpGetCollidingActor : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
runtime.push (MWBase::Environment::get().getWorld()->getActorStandingOn(ptr));
}
};
template <class R>
class OpHurtStandingActor : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
float healthDiffPerSecond = runtime[0].mFloat;
runtime.pop();
MWBase::Environment::get().getWorld()->hurtStandingActors(ptr, healthDiffPerSecond);
}
};
template <class R>
class OpHurtCollidingActor : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
float healthDiffPerSecond = runtime[0].mFloat;
runtime.pop();
MWBase::Environment::get().getWorld()->hurtCollidingActors(ptr, healthDiffPerSecond);
}
};
class OpGetWindSpeed : public Interpreter::Opcode0 class OpGetWindSpeed : public Interpreter::Opcode0
{ {
public: public:
@ -967,6 +1021,14 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingPcExplicit, new OpGetStandingPc<ExplicitRef>); interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingPcExplicit, new OpGetStandingPc<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingActor, new OpGetStandingActor<ImplicitRef>); interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingActor, new OpGetStandingActor<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingActorExplicit, new OpGetStandingActor<ExplicitRef>); interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingActorExplicit, new OpGetStandingActor<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingPc, new OpGetCollidingPc<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingPcExplicit, new OpGetCollidingPc<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingActor, new OpGetCollidingActor<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeGetCollidingActorExplicit, new OpGetCollidingActor<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeHurtStandingActor, new OpHurtStandingActor<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeHurtStandingActorExplicit, new OpHurtStandingActor<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeHurtCollidingActor, new OpHurtCollidingActor<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeHurtCollidingActorExplicit, new OpHurtCollidingActor<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeGetWindSpeed, new OpGetWindSpeed); interpreter.installSegment5 (Compiler::Misc::opcodeGetWindSpeed, new OpGetWindSpeed);
interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMe, new OpHitOnMe<ImplicitRef>); interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMe, new OpHitOnMe<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMeExplicit, new OpHitOnMe<ExplicitRef>); interpreter.installSegment5 (Compiler::Misc::opcodeHitOnMeExplicit, new OpHitOnMe<ExplicitRef>);

@ -241,7 +241,9 @@ namespace MWWorld
} }
static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time,
bool isFlying, float waterlevel, float slowFall, OEngine::Physic::PhysicEngine *engine) bool isFlying, float waterlevel, float slowFall, OEngine::Physic::PhysicEngine *engine
, std::map<std::string, std::string>& collisionTracker
, std::map<std::string, std::string>& standingCollisionTracker)
{ {
const ESM::Position &refpos = ptr.getRefData().getPosition(); const ESM::Position &refpos = ptr.getRefData().getPosition();
Ogre::Vector3 position(refpos.pos); Ogre::Vector3 position(refpos.pos);
@ -318,6 +320,11 @@ namespace MWWorld
tracer.doTrace(colobj, position, position - Ogre::Vector3(0,0,2), engine); // check if down 2 possible tracer.doTrace(colobj, position, position - Ogre::Vector3(0,0,2), engine); // check if down 2 possible
if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope) if(tracer.mFraction < 1.0f && getSlope(tracer.mPlaneNormal) <= sMaxSlope)
{ {
const btCollisionObject* standingOn = tracer.mHitObject;
if (const OEngine::Physic::RigidBody* body = dynamic_cast<const OEngine::Physic::RigidBody*>(standingOn))
{
standingCollisionTracker[ptr.getRefData().getHandle()] = body->mName;
}
isOnGround = true; isOnGround = true;
// if we're on the ground, don't try to fall any more // if we're on the ground, don't try to fall any more
velocity.z = std::max(0.0f, velocity.z); velocity.z = std::max(0.0f, velocity.z);
@ -376,6 +383,14 @@ namespace MWWorld
remainingTime *= (1.0f-tracer.mFraction); // FIXME: remainingTime is no longer used so don't set it? remainingTime *= (1.0f-tracer.mFraction); // FIXME: remainingTime is no longer used so don't set it?
break; break;
} }
else
{
const btCollisionObject* standingOn = tracer.mHitObject;
if (const OEngine::Physic::RigidBody* body = dynamic_cast<const OEngine::Physic::RigidBody*>(standingOn))
{
collisionTracker[ptr.getRefData().getHandle()] = body->mName;
}
}
} }
else else
{ {
@ -771,6 +786,10 @@ namespace MWWorld
const PtrVelocityList& PhysicsSystem::applyQueuedMovement(float dt) const PtrVelocityList& PhysicsSystem::applyQueuedMovement(float dt)
{ {
// Collision events are only tracked for a single frame, so reset first
mCollisions.clear();
mStandingCollisions.clear();
mMovementResults.clear(); mMovementResults.clear();
mTimeAccum += dt; mTimeAccum += dt;
@ -810,7 +829,7 @@ namespace MWWorld
Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum, Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum,
world->isFlying(iter->first), world->isFlying(iter->first),
waterlevel, slowFall, mEngine); waterlevel, slowFall, mEngine, mCollisions, mStandingCollisions);
if (waterCollision) if (waterCollision)
mEngine->mDynamicsWorld->removeCollisionObject(&object); mEngine->mDynamicsWorld->removeCollisionObject(&object);
@ -837,4 +856,57 @@ namespace MWWorld
mEngine->stepSimulation(dt); mEngine->stepSimulation(dt);
} }
bool PhysicsSystem::isActorStandingOn(const Ptr &actor, const Ptr &object) const
{
const std::string& actorHandle = actor.getRefData().getHandle();
const std::string& objectHandle = object.getRefData().getHandle();
for (std::map<std::string, std::string>::const_iterator it = mStandingCollisions.begin();
it != mStandingCollisions.end(); ++it)
{
if (it->first == actorHandle && it->second == objectHandle)
return true;
}
return false;
}
void PhysicsSystem::getActorsStandingOn(const Ptr &object, std::vector<std::string> &out) const
{
const std::string& objectHandle = object.getRefData().getHandle();
for (std::map<std::string, std::string>::const_iterator it = mStandingCollisions.begin();
it != mStandingCollisions.end(); ++it)
{
if (it->second == objectHandle)
out.push_back(it->first);
}
}
bool PhysicsSystem::isActorCollidingWith(const Ptr &actor, const Ptr &object) const
{
const std::string& actorHandle = actor.getRefData().getHandle();
const std::string& objectHandle = object.getRefData().getHandle();
for (std::map<std::string, std::string>::const_iterator it = mCollisions.begin();
it != mCollisions.end(); ++it)
{
if (it->first == actorHandle && it->second == objectHandle)
return true;
}
return false;
}
void PhysicsSystem::getActorsCollidingWith(const Ptr &object, std::vector<std::string> &out) const
{
const std::string& objectHandle = object.getRefData().getHandle();
for (std::map<std::string, std::string>::const_iterator it = mCollisions.begin();
it != mCollisions.end(); ++it)
{
if (it->second == objectHandle)
out.push_back(it->first);
}
}
} }

@ -87,12 +87,34 @@ namespace MWWorld
const PtrVelocityList& applyQueuedMovement(float dt); const PtrVelocityList& applyQueuedMovement(float dt);
/// Return true if \a actor has been standing on \a object in this frame
/// This will trigger whenever the object is directly below the actor.
/// It doesn't matter if the actor is stationary or moving.
bool isActorStandingOn(const MWWorld::Ptr& actor, const MWWorld::Ptr& object) const;
/// Get the handle of all actors standing on \a object in this frame.
void getActorsStandingOn(const MWWorld::Ptr& object, std::vector<std::string>& out) const;
/// Return true if \a actor has collided with \a object in this frame.
/// This will detect running into objects, but will not detect climbing stairs, stepping up a small object, etc.
bool isActorCollidingWith(const MWWorld::Ptr& actor, const MWWorld::Ptr& object) const;
/// Get the handle of all actors colliding with \a object in this frame.
void getActorsCollidingWith(const MWWorld::Ptr& object, std::vector<std::string>& out) const;
private: private:
OEngine::Render::OgreRenderer &mRender; OEngine::Render::OgreRenderer &mRender;
OEngine::Physic::PhysicEngine* mEngine; OEngine::Physic::PhysicEngine* mEngine;
std::map<std::string, std::string> handleToMesh; std::map<std::string, std::string> handleToMesh;
// Tracks all movement collisions happening during a single frame. <actor handle, collided handle>
// This will detect e.g. running against a vertical wall. It will not detect climbing up stairs,
// stepping up small objects, etc.
std::map<std::string, std::string> mCollisions;
std::map<std::string, std::string> mStandingCollisions;
PtrVelocityList mMovementQueue; PtrVelocityList mMovementQueue;
PtrVelocityList mMovementResults; PtrVelocityList mMovementResults;

@ -1995,18 +1995,62 @@ namespace MWWorld
bool World::getPlayerStandingOn (const MWWorld::Ptr& object) bool World::getPlayerStandingOn (const MWWorld::Ptr& object)
{ {
MWWorld::Ptr player = mPlayer->getPlayer(); MWWorld::Ptr player = getPlayerPtr();
if (!mPhysEngine->getCharacter("player")->getOnGround()) return mPhysics->isActorStandingOn(player, object);
return false;
btVector3 from (player.getRefData().getPosition().pos[0], player.getRefData().getPosition().pos[1], player.getRefData().getPosition().pos[2]);
btVector3 to = from - btVector3(0,0,5);
std::pair<std::string, float> result = mPhysEngine->rayTest(from, to);
return result.first == object.getRefData().getBaseNode()->getName();
} }
bool World::getActorStandingOn (const MWWorld::Ptr& object) bool World::getActorStandingOn (const MWWorld::Ptr& object)
{ {
return mPhysEngine->isAnyActorStandingOn(object.getRefData().getBaseNode()->getName()); std::vector<std::string> actors;
mPhysics->getActorsStandingOn(object, actors);
return !actors.empty();
}
bool World::getPlayerCollidingWith (const MWWorld::Ptr& object)
{
MWWorld::Ptr player = getPlayerPtr();
return mPhysics->isActorCollidingWith(player, object);
}
bool World::getActorCollidingWith (const MWWorld::Ptr& object)
{
std::vector<std::string> actors;
mPhysics->getActorsCollidingWith(object, actors);
return !actors.empty();
}
void World::hurtStandingActors(const Ptr &object, float healthPerSecond)
{
std::vector<std::string> actors;
mPhysics->getActorsStandingOn(object, actors);
for (std::vector<std::string>::iterator it = actors.begin(); it != actors.end(); ++it)
{
MWWorld::Ptr actor = searchPtrViaHandle(*it); // Collision events are from the last frame, actor might no longer exist
if (actor.isEmpty())
continue;
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
MWMechanics::DynamicStat<float> health = stats.getHealth();
health.setCurrent(health.getCurrent()-healthPerSecond*MWBase::Environment::get().getFrameDuration());
stats.setHealth(health);
}
}
void World::hurtCollidingActors(const Ptr &object, float healthPerSecond)
{
std::vector<std::string> actors;
mPhysics->getActorsCollidingWith(object, actors);
for (std::vector<std::string>::iterator it = actors.begin(); it != actors.end(); ++it)
{
MWWorld::Ptr actor = searchPtrViaHandle(*it); // Collision events are from the last frame, actor might no longer exist
if (actor.isEmpty())
continue;
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
MWMechanics::DynamicStat<float> health = stats.getHealth();
health.setCurrent(health.getCurrent()-healthPerSecond*MWBase::Environment::get().getFrameDuration());
stats.setHealth(health);
}
} }
float World::getWindSpeed() float World::getWindSpeed()

@ -479,6 +479,15 @@ namespace MWWorld
virtual bool getPlayerStandingOn (const MWWorld::Ptr& object); ///< @return true if the player is standing on \a object virtual bool getPlayerStandingOn (const MWWorld::Ptr& object); ///< @return true if the player is standing on \a object
virtual bool getActorStandingOn (const MWWorld::Ptr& object); ///< @return true if any actor is standing on \a object virtual bool getActorStandingOn (const MWWorld::Ptr& object); ///< @return true if any actor is standing on \a object
virtual bool getPlayerCollidingWith(const MWWorld::Ptr& object); ///< @return true if the player is colliding with \a object
virtual bool getActorCollidingWith (const MWWorld::Ptr& object); ///< @return true if any actor is colliding with \a object
virtual void hurtStandingActors (const MWWorld::Ptr& object, float dmgPerSecond);
///< Apply a health difference to any actors standing on \a object.
/// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed.
virtual void hurtCollidingActors (const MWWorld::Ptr& object, float dmgPerSecond);
///< Apply a health difference to any actors colliding with \a object.
/// To hurt actors, healthPerSecond should be a positive value. For a negative value, actors will be healed.
virtual float getWindSpeed(); virtual float getWindSpeed();
virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out); virtual void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out);

@ -272,6 +272,10 @@ namespace Compiler
extensions.registerInstruction ("fall", "", opcodeFall, opcodeFallExplicit); extensions.registerInstruction ("fall", "", opcodeFall, opcodeFallExplicit);
extensions.registerFunction ("getstandingpc", 'l', "", opcodeGetStandingPc, opcodeGetStandingPcExplicit); extensions.registerFunction ("getstandingpc", 'l', "", opcodeGetStandingPc, opcodeGetStandingPcExplicit);
extensions.registerFunction ("getstandingactor", 'l', "", opcodeGetStandingActor, opcodeGetStandingActorExplicit); extensions.registerFunction ("getstandingactor", 'l', "", opcodeGetStandingActor, opcodeGetStandingActorExplicit);
extensions.registerFunction ("getcollidingpc", 'l', "", opcodeGetCollidingPc, opcodeGetCollidingPcExplicit);
extensions.registerFunction ("getcollidingactor", 'l', "", opcodeGetCollidingActor, opcodeGetCollidingActorExplicit);
extensions.registerInstruction ("hurtstandingactor", "f", opcodeHurtStandingActor, opcodeHurtStandingActorExplicit);
extensions.registerInstruction ("hurtcollidingactor", "f", opcodeHurtCollidingActor, opcodeHurtCollidingActorExplicit);
extensions.registerFunction ("getwindspeed", 'f', "", opcodeGetWindSpeed); extensions.registerFunction ("getwindspeed", 'f', "", opcodeGetWindSpeed);
extensions.registerFunction ("hitonme", 'l', "S", opcodeHitOnMe, opcodeHitOnMeExplicit); extensions.registerFunction ("hitonme", 'l', "S", opcodeHitOnMe, opcodeHitOnMeExplicit);
extensions.registerInstruction ("disableteleporting", "", opcodeDisableTeleporting); extensions.registerInstruction ("disableteleporting", "", opcodeDisableTeleporting);

@ -238,6 +238,14 @@ namespace Compiler
const int opcodeGetStandingPcExplicit = 0x200020d; const int opcodeGetStandingPcExplicit = 0x200020d;
const int opcodeGetStandingActor = 0x200020e; const int opcodeGetStandingActor = 0x200020e;
const int opcodeGetStandingActorExplicit = 0x200020f; const int opcodeGetStandingActorExplicit = 0x200020f;
const int opcodeGetCollidingPc = 0x2000250;
const int opcodeGetCollidingPcExplicit = 0x2000251;
const int opcodeGetCollidingActor = 0x2000252;
const int opcodeGetCollidingActorExplicit = 0x2000253;
const int opcodeHurtStandingActor = 0x2000254;
const int opcodeHurtStandingActorExplicit = 0x2000255;
const int opcodeHurtCollidingActor = 0x2000256;
const int opcodeHurtCollidingActorExplicit = 0x2000257;
const int opcodeGetWindSpeed = 0x2000212; const int opcodeGetWindSpeed = 0x2000212;
const int opcodePlayBink = 0x20001f7; const int opcodePlayBink = 0x20001f7;
const int opcodeGoToJail = 0x2000235; const int opcodeGoToJail = 0x2000235;

@ -846,21 +846,5 @@ namespace Physic
} }
} }
bool PhysicEngine::isAnyActorStandingOn (const std::string& objectName)
{
for (PhysicActorContainer::iterator it = mActorMap.begin(); it != mActorMap.end(); ++it)
{
if (!it->second->getOnGround())
continue;
Ogre::Vector3 pos = it->second->getPosition();
btVector3 from (pos.x, pos.y, pos.z);
btVector3 to = from - btVector3(0,0,5);
std::pair<std::string, float> result = rayTest(from, to);
if (result.first == objectName)
return true;
}
return false;
}
} }
} }

@ -296,8 +296,6 @@ namespace Physic
void setSceneManager(Ogre::SceneManager* sceneMgr); void setSceneManager(Ogre::SceneManager* sceneMgr);
bool isAnyActorStandingOn (const std::string& objectName);
/** /**
* Return the closest object hit by a ray. If there are no objects, it will return ("",-1). * Return the closest object hit by a ray. If there are no objects, it will return ("",-1).
* If \a normal is non-NULL, the hit normal will be written there (if there is a hit) * If \a normal is non-NULL, the hit normal will be written there (if there is a hit)

@ -81,12 +81,14 @@ void ActorTracer::doTrace(btCollisionObject *actor, const Ogre::Vector3 &start,
mFraction = newTraceCallback.m_closestHitFraction; mFraction = newTraceCallback.m_closestHitFraction;
mPlaneNormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z()); mPlaneNormal = Ogre::Vector3(tracehitnormal.x(), tracehitnormal.y(), tracehitnormal.z());
mEndPos = (end-start)*mFraction + start; mEndPos = (end-start)*mFraction + start;
mHitObject = newTraceCallback.m_hitCollisionObject;
} }
else else
{ {
mEndPos = end; mEndPos = end;
mPlaneNormal = Ogre::Vector3(0.0f, 0.0f, 1.0f); mPlaneNormal = Ogre::Vector3(0.0f, 0.0f, 1.0f);
mFraction = 1.0f; mFraction = 1.0f;
mHitObject = NULL;
} }
} }

@ -18,6 +18,7 @@ namespace Physic
{ {
Ogre::Vector3 mEndPos; Ogre::Vector3 mEndPos;
Ogre::Vector3 mPlaneNormal; Ogre::Vector3 mPlaneNormal;
const btCollisionObject* mHitObject;
float mFraction; float mFraction;

Loading…
Cancel
Save