2012-11-14 17:42:04 +00:00
|
|
|
#include "aiescort.hpp"
|
2013-05-08 22:27:20 +00:00
|
|
|
|
2014-06-12 21:27:04 +00:00
|
|
|
#include <components/esm/aisequence.hpp>
|
2015-12-06 22:32:20 +00:00
|
|
|
#include <components/esm/loadcell.hpp>
|
2014-06-12 21:27:04 +00:00
|
|
|
|
2013-05-09 03:02:24 +00:00
|
|
|
#include "../mwbase/world.hpp"
|
2013-05-08 22:27:20 +00:00
|
|
|
#include "../mwbase/environment.hpp"
|
2013-05-09 03:02:24 +00:00
|
|
|
#include "../mwbase/mechanicsmanager.hpp"
|
2013-05-08 22:27:20 +00:00
|
|
|
|
2014-02-23 19:11:05 +00:00
|
|
|
#include "../mwworld/class.hpp"
|
2015-12-06 22:32:20 +00:00
|
|
|
#include "../mwworld/cellstore.hpp"
|
2014-02-23 19:11:05 +00:00
|
|
|
|
2016-06-17 14:07:16 +00:00
|
|
|
#include "creaturestats.hpp"
|
2014-02-23 19:11:05 +00:00
|
|
|
#include "movement.hpp"
|
2014-01-29 19:29:07 +00:00
|
|
|
|
2013-05-09 03:02:24 +00:00
|
|
|
/*
|
|
|
|
TODO: Different behavior for AIEscort a d x y z and AIEscortCell a c d x y z.
|
|
|
|
TODO: Take account for actors being in different cells.
|
|
|
|
*/
|
2013-05-08 22:27:20 +00:00
|
|
|
|
2013-06-01 00:49:52 +00:00
|
|
|
namespace MWMechanics
|
2013-05-08 22:27:20 +00:00
|
|
|
{
|
2014-05-16 10:11:34 +00:00
|
|
|
AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z)
|
2017-11-21 16:00:51 +00:00
|
|
|
: mX(x), mY(y), mZ(z), mDuration(duration), mRemainingDuration(static_cast<float>(duration))
|
2014-01-12 21:47:22 +00:00
|
|
|
, mCellX(std::numeric_limits<int>::max())
|
|
|
|
, mCellY(std::numeric_limits<int>::max())
|
2013-06-01 00:49:52 +00:00
|
|
|
{
|
2017-11-21 16:00:51 +00:00
|
|
|
mTargetActorRefId = actorId;
|
2013-05-08 22:27:20 +00:00
|
|
|
}
|
2012-11-30 00:16:16 +00:00
|
|
|
|
2017-11-21 16:00:51 +00:00
|
|
|
AiEscort::AiEscort(const std::string &actorId, const std::string &cellId, int duration, float x, float y, float z)
|
|
|
|
: mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration), mRemainingDuration(static_cast<float>(duration))
|
2014-01-12 21:47:22 +00:00
|
|
|
, mCellX(std::numeric_limits<int>::max())
|
|
|
|
, mCellY(std::numeric_limits<int>::max())
|
2013-06-01 00:49:52 +00:00
|
|
|
{
|
2017-11-21 16:00:51 +00:00
|
|
|
mTargetActorRefId = actorId;
|
2014-06-12 21:27:04 +00:00
|
|
|
}
|
2013-05-08 22:27:20 +00:00
|
|
|
|
2014-06-12 21:27:04 +00:00
|
|
|
AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort)
|
2017-11-21 16:00:51 +00:00
|
|
|
: mCellId(escort->mCellId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ)
|
2020-06-02 19:30:46 +00:00
|
|
|
// mDuration isn't saved in the save file, so just giving it "1" for now if the package has a duration.
|
|
|
|
// The exact value of mDuration only matters for repeating packages.
|
|
|
|
// Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves.
|
|
|
|
, mDuration(escort->mRemainingDuration > 0)
|
2015-05-01 00:24:27 +00:00
|
|
|
, mRemainingDuration(escort->mRemainingDuration)
|
2014-06-12 21:27:04 +00:00
|
|
|
, mCellX(std::numeric_limits<int>::max())
|
|
|
|
, mCellY(std::numeric_limits<int>::max())
|
|
|
|
{
|
2017-11-21 16:00:51 +00:00
|
|
|
mTargetActorRefId = escort->mTargetId;
|
|
|
|
mTargetActorId = escort->mTargetActorId;
|
2013-05-08 22:27:20 +00:00
|
|
|
}
|
|
|
|
|
2015-06-26 15:47:04 +00:00
|
|
|
bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
2013-06-01 00:49:52 +00:00
|
|
|
{
|
|
|
|
// If AiEscort has ran for as long or longer then the duration specified
|
|
|
|
// and the duration is not infinite, the package is complete.
|
2017-02-17 16:38:21 +00:00
|
|
|
if (mDuration > 0)
|
2013-06-01 00:49:52 +00:00
|
|
|
{
|
2016-06-11 13:34:49 +00:00
|
|
|
mRemainingDuration -= ((duration*MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600);
|
|
|
|
if (mRemainingDuration <= 0)
|
2016-05-28 16:02:22 +00:00
|
|
|
{
|
2016-06-11 13:34:49 +00:00
|
|
|
mRemainingDuration = mDuration;
|
2013-06-01 00:49:52 +00:00
|
|
|
return true;
|
2016-05-28 16:02:22 +00:00
|
|
|
}
|
2013-06-01 00:49:52 +00:00
|
|
|
}
|
2013-05-08 22:27:20 +00:00
|
|
|
|
2015-12-06 22:32:20 +00:00
|
|
|
if (!mCellId.empty() && mCellId != actor.getCell()->getCell()->getCellId().mWorldspace)
|
|
|
|
return false; // Not in the correct cell, pause and rely on the player to go back through a teleport door
|
|
|
|
|
2014-07-28 15:28:00 +00:00
|
|
|
actor.getClass().getCreatureStats(actor).setDrawState(DrawState_Nothing);
|
2014-08-11 02:21:04 +00:00
|
|
|
actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, false);
|
2014-07-28 15:28:00 +00:00
|
|
|
|
2017-11-21 16:00:51 +00:00
|
|
|
const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mTargetActorRefId, false);
|
2018-11-02 11:24:43 +00:00
|
|
|
const osg::Vec3f leaderPos = actor.getRefData().getPosition().asVec3();
|
|
|
|
const osg::Vec3f followerPos = follower.getRefData().getPosition().asVec3();
|
2021-03-23 22:59:45 +00:00
|
|
|
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
|
|
|
|
const float maxHalfExtent = std::max(halfExtents.x(), std::max(halfExtents.y(), halfExtents.z()));
|
2013-05-08 22:27:20 +00:00
|
|
|
|
2018-11-02 11:24:43 +00:00
|
|
|
if ((leaderPos - followerPos).length2() <= mMaxDist * mMaxDist)
|
2013-06-01 00:49:52 +00:00
|
|
|
{
|
2018-11-02 11:24:43 +00:00
|
|
|
const osg::Vec3f dest(mX, mY, mZ);
|
2021-03-23 22:59:45 +00:00
|
|
|
if (pathTo(actor, dest, duration, maxHalfExtent)) //Returns true on path complete
|
2016-05-28 16:02:22 +00:00
|
|
|
{
|
2016-06-11 13:34:49 +00:00
|
|
|
mRemainingDuration = mDuration;
|
2014-05-14 05:44:11 +00:00
|
|
|
return true;
|
2016-05-28 16:02:22 +00:00
|
|
|
}
|
2021-03-23 22:59:45 +00:00
|
|
|
mMaxDist = maxHalfExtent + 450.0f;
|
2013-06-01 00:49:52 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-06-15 18:43:09 +00:00
|
|
|
// Stop moving if the player is too far away
|
2013-06-01 00:49:52 +00:00
|
|
|
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, "idle3", 0, 1);
|
2014-05-22 18:37:22 +00:00
|
|
|
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
2021-03-23 22:59:45 +00:00
|
|
|
mMaxDist = maxHalfExtent + 250.0f;
|
2013-06-01 00:49:52 +00:00
|
|
|
}
|
2013-05-09 00:19:47 +00:00
|
|
|
|
2013-06-01 00:49:52 +00:00
|
|
|
return false;
|
2013-05-08 22:27:20 +00:00
|
|
|
}
|
2013-06-01 00:49:52 +00:00
|
|
|
|
2014-06-12 21:27:04 +00:00
|
|
|
void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const
|
|
|
|
{
|
2017-04-28 15:30:26 +00:00
|
|
|
std::unique_ptr<ESM::AiSequence::AiEscort> escort(new ESM::AiSequence::AiEscort());
|
2014-06-12 21:27:04 +00:00
|
|
|
escort->mData.mX = mX;
|
|
|
|
escort->mData.mY = mY;
|
|
|
|
escort->mData.mZ = mZ;
|
2017-11-21 16:00:51 +00:00
|
|
|
escort->mTargetId = mTargetActorRefId;
|
|
|
|
escort->mTargetActorId = mTargetActorId;
|
2014-06-12 21:27:04 +00:00
|
|
|
escort->mRemainingDuration = mRemainingDuration;
|
|
|
|
escort->mCellId = mCellId;
|
|
|
|
|
|
|
|
ESM::AiSequence::AiPackageContainer package;
|
|
|
|
package.mType = ESM::AiSequence::Ai_Escort;
|
|
|
|
package.mPackage = escort.release();
|
|
|
|
sequence.mPackages.push_back(package);
|
|
|
|
}
|
2016-06-11 13:34:49 +00:00
|
|
|
|
2017-02-17 16:38:21 +00:00
|
|
|
void AiEscort::fastForward(const MWWorld::Ptr& actor, AiState &state)
|
|
|
|
{
|
|
|
|
// Update duration counter if this package has a duration
|
|
|
|
if (mDuration > 0)
|
|
|
|
mRemainingDuration--;
|
|
|
|
}
|
2012-11-30 00:16:16 +00:00
|
|
|
}
|
|
|
|
|