Implement collision script instructions (Fixes #1111)

pull/196/head
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 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 void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out) = 0;

@ -405,5 +405,13 @@ op 0x200024c: Face
op 0x200024d: Face, explicit
op 0x200024e: GetStat (dummy function)
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
{
public:
@ -967,6 +1021,14 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingPcExplicit, new OpGetStandingPc<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeGetStandingActor, new OpGetStandingActor<ImplicitRef>);
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::opcodeHitOnMe, new OpHitOnMe<ImplicitRef>);
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,
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();
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
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;
// if we're on the ground, don't try to fall any more
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?
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
{
@ -771,6 +786,10 @@ namespace MWWorld
const PtrVelocityList& PhysicsSystem::applyQueuedMovement(float dt)
{
// Collision events are only tracked for a single frame, so reset first
mCollisions.clear();
mStandingCollisions.clear();
mMovementResults.clear();
mTimeAccum += dt;
@ -810,7 +829,7 @@ namespace MWWorld
Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum,
world->isFlying(iter->first),
waterlevel, slowFall, mEngine);
waterlevel, slowFall, mEngine, mCollisions, mStandingCollisions);
if (waterCollision)
mEngine->mDynamicsWorld->removeCollisionObject(&object);
@ -837,4 +856,57 @@ namespace MWWorld
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);
/// 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:
OEngine::Render::OgreRenderer &mRender;
OEngine::Physic::PhysicEngine* mEngine;
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 mMovementResults;

@ -1995,18 +1995,62 @@ namespace MWWorld
bool World::getPlayerStandingOn (const MWWorld::Ptr& object)
{
MWWorld::Ptr player = mPlayer->getPlayer();
if (!mPhysEngine->getCharacter("player")->getOnGround())
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();
MWWorld::Ptr player = getPlayerPtr();
return mPhysics->isActorStandingOn(player, 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()

@ -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 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 void getContainersOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out);

@ -272,6 +272,10 @@ namespace Compiler
extensions.registerInstruction ("fall", "", opcodeFall, opcodeFallExplicit);
extensions.registerFunction ("getstandingpc", 'l', "", opcodeGetStandingPc, opcodeGetStandingPcExplicit);
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 ("hitonme", 'l', "S", opcodeHitOnMe, opcodeHitOnMeExplicit);
extensions.registerInstruction ("disableteleporting", "", opcodeDisableTeleporting);

@ -238,6 +238,14 @@ namespace Compiler
const int opcodeGetStandingPcExplicit = 0x200020d;
const int opcodeGetStandingActor = 0x200020e;
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 opcodePlayBink = 0x20001f7;
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);
bool isAnyActorStandingOn (const std::string& objectName);
/**
* 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)

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

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

Loading…
Cancel
Save