mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-30 19:15:41 +00:00
Merge branch 'reset' into 'master'
AI reset argument Closes #6177 and #1465 See merge request OpenMW/openmw!1405
This commit is contained in:
commit
53e14eb238
22 changed files with 153 additions and 112 deletions
|
@ -52,6 +52,7 @@
|
||||||
Bug #6168: Weather particles flicker for a frame at start of storms
|
Bug #6168: Weather particles flicker for a frame at start of storms
|
||||||
Bug #6172: Some creatures can't open doors
|
Bug #6172: Some creatures can't open doors
|
||||||
Bug #6174: Spellmaking and Enchanting sliders differences from vanilla
|
Bug #6174: Spellmaking and Enchanting sliders differences from vanilla
|
||||||
|
Bug #6177: Followers of player follower stop following after waiting for a day
|
||||||
Bug #6184: Command and Calm and Demoralize and Frenzy and Rally magic effects inconsistencies with vanilla
|
Bug #6184: Command and Calm and Demoralize and Frenzy and Rally magic effects inconsistencies with vanilla
|
||||||
Bug #6197: Infinite Casting Loop
|
Bug #6197: Infinite Casting Loop
|
||||||
Bug #6253: Multiple instances of Reflect stack additively
|
Bug #6253: Multiple instances of Reflect stack additively
|
||||||
|
@ -81,6 +82,7 @@
|
||||||
Bug #6416: Morphs are applied to the wrong target
|
Bug #6416: Morphs are applied to the wrong target
|
||||||
Bug #6429: Wyrmhaven: Can't add AI packages to player
|
Bug #6429: Wyrmhaven: Can't add AI packages to player
|
||||||
Feature #890: OpenMW-CS: Column filtering
|
Feature #890: OpenMW-CS: Column filtering
|
||||||
|
Feature #1465: "Reset" argument for AI functions
|
||||||
Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record
|
Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record
|
||||||
Feature #2780: A way to see current OpenMW version in the console
|
Feature #2780: A way to see current OpenMW version in the console
|
||||||
Feature #3616: Allow Zoom levels on the World Map
|
Feature #3616: Allow Zoom levels on the World Map
|
||||||
|
|
|
@ -30,7 +30,7 @@ void printAIPackage(const ESM::AIPackage& p)
|
||||||
{
|
{
|
||||||
std::cout << " Travel Coordinates: (" << p.mTravel.mX << ","
|
std::cout << " Travel Coordinates: (" << p.mTravel.mX << ","
|
||||||
<< p.mTravel.mY << "," << p.mTravel.mZ << ")" << std::endl;
|
<< p.mTravel.mY << "," << p.mTravel.mZ << ")" << std::endl;
|
||||||
std::cout << " Travel Unknown: " << p.mTravel.mUnk << std::endl;
|
std::cout << " Should repeat: " << p.mTravel.mShouldRepeat << std::endl;
|
||||||
}
|
}
|
||||||
else if (p.mType == ESM::AI_Follow || p.mType == ESM::AI_Escort)
|
else if (p.mType == ESM::AI_Follow || p.mType == ESM::AI_Escort)
|
||||||
{
|
{
|
||||||
|
@ -38,12 +38,12 @@ void printAIPackage(const ESM::AIPackage& p)
|
||||||
<< p.mTarget.mY << "," << p.mTarget.mZ << ")" << std::endl;
|
<< p.mTarget.mY << "," << p.mTarget.mZ << ")" << std::endl;
|
||||||
std::cout << " Duration: " << p.mTarget.mDuration << std::endl;
|
std::cout << " Duration: " << p.mTarget.mDuration << std::endl;
|
||||||
std::cout << " Target ID: " << p.mTarget.mId.toString() << std::endl;
|
std::cout << " Target ID: " << p.mTarget.mId.toString() << std::endl;
|
||||||
std::cout << " Unknown: " << p.mTarget.mUnk << std::endl;
|
std::cout << " Should repeat: " << p.mTarget.mShouldRepeat << std::endl;
|
||||||
}
|
}
|
||||||
else if (p.mType == ESM::AI_Activate)
|
else if (p.mType == ESM::AI_Activate)
|
||||||
{
|
{
|
||||||
std::cout << " Name: " << p.mActivate.mName.toString() << std::endl;
|
std::cout << " Name: " << p.mActivate.mName.toString() << std::endl;
|
||||||
std::cout << " Activate Unknown: " << p.mActivate.mUnk << std::endl;
|
std::cout << " Should repeat: " << p.mActivate.mShouldRepeat << std::endl;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::cout << " BadPackage: " << Misc::StringUtils::format("0x%08X", p.mType) << std::endl;
|
std::cout << " BadPackage: " << Misc::StringUtils::format("0x%08X", p.mType) << std::endl;
|
||||||
|
|
|
@ -255,7 +255,7 @@ namespace CSMWorld
|
||||||
{ ColumnId_AiWanderDist, "Wander Dist" },
|
{ ColumnId_AiWanderDist, "Wander Dist" },
|
||||||
{ ColumnId_AiDuration, "Ai Duration" },
|
{ ColumnId_AiDuration, "Ai Duration" },
|
||||||
{ ColumnId_AiWanderToD, "Wander ToD" },
|
{ ColumnId_AiWanderToD, "Wander ToD" },
|
||||||
{ ColumnId_AiWanderRepeat, "Wander Repeat" },
|
{ ColumnId_AiWanderRepeat, "Ai Repeat" },
|
||||||
{ ColumnId_AiActivateName, "Activate" },
|
{ ColumnId_AiActivateName, "Activate" },
|
||||||
{ ColumnId_AiTargetId, "Target ID" },
|
{ ColumnId_AiTargetId, "Target ID" },
|
||||||
{ ColumnId_AiTargetCell, "Target Cell" },
|
{ ColumnId_AiTargetCell, "Target Cell" },
|
||||||
|
|
|
@ -1678,7 +1678,7 @@ namespace CSMWorld
|
||||||
newRow.mWander.mTimeOfDay = 0;
|
newRow.mWander.mTimeOfDay = 0;
|
||||||
for (int i = 0; i < 8; ++i)
|
for (int i = 0; i < 8; ++i)
|
||||||
newRow.mWander.mIdle[i] = 0;
|
newRow.mWander.mIdle[i] = 0;
|
||||||
newRow.mWander.mShouldRepeat = 0;
|
newRow.mWander.mShouldRepeat = 1;
|
||||||
newRow.mCellName = "";
|
newRow.mCellName = "";
|
||||||
|
|
||||||
if (position >= (int)list.size())
|
if (position >= (int)list.size())
|
||||||
|
@ -1784,9 +1784,15 @@ namespace CSMWorld
|
||||||
return static_cast<int>(content.mWander.mIdle[subColIndex-4]);
|
return static_cast<int>(content.mWander.mIdle[subColIndex-4]);
|
||||||
else
|
else
|
||||||
return QVariant();
|
return QVariant();
|
||||||
case 12: // wander repeat
|
case 12: // repeat
|
||||||
if (content.mType == ESM::AI_Wander)
|
if (content.mType == ESM::AI_Wander)
|
||||||
return content.mWander.mShouldRepeat != 0;
|
return content.mWander.mShouldRepeat != 0;
|
||||||
|
else if (content.mType == ESM::AI_Travel)
|
||||||
|
return content.mTravel.mShouldRepeat != 0;
|
||||||
|
else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
|
||||||
|
return content.mTarget.mShouldRepeat != 0;
|
||||||
|
else if (content.mType == ESM::AI_Activate)
|
||||||
|
return content.mActivate.mShouldRepeat != 0;
|
||||||
else
|
else
|
||||||
return QVariant();
|
return QVariant();
|
||||||
case 13: // activate name
|
case 13: // activate name
|
||||||
|
@ -1895,6 +1901,12 @@ namespace CSMWorld
|
||||||
case 12:
|
case 12:
|
||||||
if (content.mType == ESM::AI_Wander)
|
if (content.mType == ESM::AI_Wander)
|
||||||
content.mWander.mShouldRepeat = static_cast<unsigned char>(value.toInt());
|
content.mWander.mShouldRepeat = static_cast<unsigned char>(value.toInt());
|
||||||
|
else if (content.mType == ESM::AI_Travel)
|
||||||
|
content.mTravel.mShouldRepeat = static_cast<unsigned char>(value.toInt());
|
||||||
|
else if (content.mType == ESM::AI_Follow || content.mType == ESM::AI_Escort)
|
||||||
|
content.mTarget.mShouldRepeat = static_cast<unsigned char>(value.toInt());
|
||||||
|
else if (content.mType == ESM::AI_Activate)
|
||||||
|
content.mActivate.mShouldRepeat = static_cast<unsigned char>(value.toInt());
|
||||||
else
|
else
|
||||||
return; // return without saving
|
return; // return without saving
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
AiActivate::AiActivate(const std::string &objectId)
|
AiActivate::AiActivate(const std::string &objectId, bool repeat)
|
||||||
: mObjectId(objectId)
|
: TypedAiPackage<AiActivate>(repeat), mObjectId(objectId)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
std::unique_ptr<ESM::AiSequence::AiActivate> activate(new ESM::AiSequence::AiActivate());
|
std::unique_ptr<ESM::AiSequence::AiActivate> activate(new ESM::AiSequence::AiActivate());
|
||||||
activate->mTargetId = mObjectId;
|
activate->mTargetId = mObjectId;
|
||||||
|
activate->mRepeat = getRepeat();
|
||||||
|
|
||||||
ESM::AiSequence::AiPackageContainer package;
|
ESM::AiSequence::AiPackageContainer package;
|
||||||
package.mType = ESM::AiSequence::Ai_Activate;
|
package.mType = ESM::AiSequence::Ai_Activate;
|
||||||
|
@ -56,7 +57,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
AiActivate::AiActivate(const ESM::AiSequence::AiActivate *activate)
|
AiActivate::AiActivate(const ESM::AiSequence::AiActivate *activate)
|
||||||
: mObjectId(activate->mTargetId)
|
: AiActivate(activate->mTargetId, activate->mRepeat)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace MWMechanics
|
||||||
public:
|
public:
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/** \param objectId Reference to object to activate **/
|
/** \param objectId Reference to object to activate **/
|
||||||
explicit AiActivate(const std::string &objectId);
|
explicit AiActivate(const std::string &objectId, bool repeat);
|
||||||
|
|
||||||
explicit AiActivate(const ESM::AiSequence::AiActivate* activate);
|
explicit AiActivate(const ESM::AiSequence::AiActivate* activate);
|
||||||
|
|
||||||
|
|
|
@ -20,16 +20,16 @@
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z)
|
AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z, bool repeat)
|
||||||
: mX(x), mY(y), mZ(z), mDuration(duration), mRemainingDuration(static_cast<float>(duration))
|
: TypedAiPackage<AiEscort>(repeat), mX(x), mY(y), mZ(z), mDuration(duration), mRemainingDuration(static_cast<float>(duration))
|
||||||
, mCellX(std::numeric_limits<int>::max())
|
, mCellX(std::numeric_limits<int>::max())
|
||||||
, mCellY(std::numeric_limits<int>::max())
|
, mCellY(std::numeric_limits<int>::max())
|
||||||
{
|
{
|
||||||
mTargetActorRefId = actorId;
|
mTargetActorRefId = actorId;
|
||||||
}
|
}
|
||||||
|
|
||||||
AiEscort::AiEscort(const std::string &actorId, const std::string &cellId, int duration, float x, float y, float z)
|
AiEscort::AiEscort(const std::string &actorId, const std::string &cellId, int duration, float x, float y, float z, bool repeat)
|
||||||
: mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration), mRemainingDuration(static_cast<float>(duration))
|
: TypedAiPackage<AiEscort>(repeat), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration), mRemainingDuration(static_cast<float>(duration))
|
||||||
, mCellX(std::numeric_limits<int>::max())
|
, mCellX(std::numeric_limits<int>::max())
|
||||||
, mCellY(std::numeric_limits<int>::max())
|
, mCellY(std::numeric_limits<int>::max())
|
||||||
{
|
{
|
||||||
|
@ -37,11 +37,8 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort)
|
AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort)
|
||||||
: mCellId(escort->mCellId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ)
|
: TypedAiPackage<AiEscort>(escort->mRepeat), mCellId(escort->mCellId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ)
|
||||||
// mDuration isn't saved in the save file, so just giving it "1" for now if the package has a duration.
|
, mDuration(escort->mData.mDuration)
|
||||||
// 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)
|
|
||||||
, mRemainingDuration(escort->mRemainingDuration)
|
, mRemainingDuration(escort->mRemainingDuration)
|
||||||
, mCellX(std::numeric_limits<int>::max())
|
, mCellX(std::numeric_limits<int>::max())
|
||||||
, mCellY(std::numeric_limits<int>::max())
|
, mCellY(std::numeric_limits<int>::max())
|
||||||
|
@ -103,10 +100,12 @@ namespace MWMechanics
|
||||||
escort->mData.mX = mX;
|
escort->mData.mX = mX;
|
||||||
escort->mData.mY = mY;
|
escort->mData.mY = mY;
|
||||||
escort->mData.mZ = mZ;
|
escort->mData.mZ = mZ;
|
||||||
|
escort->mData.mDuration = mDuration;
|
||||||
escort->mTargetId = mTargetActorRefId;
|
escort->mTargetId = mTargetActorRefId;
|
||||||
escort->mTargetActorId = mTargetActorId;
|
escort->mTargetActorId = mTargetActorId;
|
||||||
escort->mRemainingDuration = mRemainingDuration;
|
escort->mRemainingDuration = mRemainingDuration;
|
||||||
escort->mCellId = mCellId;
|
escort->mCellId = mCellId;
|
||||||
|
escort->mRepeat = getRepeat();
|
||||||
|
|
||||||
ESM::AiSequence::AiPackageContainer package;
|
ESM::AiSequence::AiPackageContainer package;
|
||||||
package.mType = ESM::AiSequence::Ai_Escort;
|
package.mType = ESM::AiSequence::Ai_Escort;
|
||||||
|
|
|
@ -22,11 +22,11 @@ namespace MWMechanics
|
||||||
/// Implementation of AiEscort
|
/// Implementation of AiEscort
|
||||||
/** The Actor will escort the specified actor to the world position x, y, z until they reach their position, or they run out of time
|
/** The Actor will escort the specified actor to the world position x, y, z until they reach their position, or they run out of time
|
||||||
\implement AiEscort **/
|
\implement AiEscort **/
|
||||||
AiEscort(const std::string &actorId, int duration, float x, float y, float z);
|
AiEscort(const std::string &actorId, int duration, float x, float y, float z, bool repeat);
|
||||||
/// Implementation of AiEscortCell
|
/// Implementation of AiEscortCell
|
||||||
/** The Actor will escort the specified actor to the cell position x, y, z until they reach their position, or they run out of time
|
/** The Actor will escort the specified actor to the cell position x, y, z until they reach their position, or they run out of time
|
||||||
\implement AiEscortCell **/
|
\implement AiEscortCell **/
|
||||||
AiEscort(const std::string &actorId, const std::string &cellId, int duration, float x, float y, float z);
|
AiEscort(const std::string &actorId, const std::string &cellId, int duration, float x, float y, float z, bool repeat);
|
||||||
|
|
||||||
AiEscort(const ESM::AiSequence::AiEscort* escort);
|
AiEscort(const ESM::AiSequence::AiEscort* escort);
|
||||||
|
|
||||||
|
|
|
@ -28,36 +28,20 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
int AiFollow::mFollowIndexCounter = 0;
|
int AiFollow::mFollowIndexCounter = 0;
|
||||||
|
|
||||||
AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z)
|
AiFollow::AiFollow(const std::string &actorId, float duration, float x, float y, float z, bool repeat)
|
||||||
: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
|
: TypedAiPackage<AiFollow>(repeat), mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
|
||||||
, mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++)
|
, mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++)
|
||||||
{
|
{
|
||||||
mTargetActorRefId = actorId;
|
mTargetActorRefId = actorId;
|
||||||
}
|
}
|
||||||
|
|
||||||
AiFollow::AiFollow(const std::string &actorId, const std::string &cellId, float duration, float x, float y, float z)
|
AiFollow::AiFollow(const std::string &actorId, const std::string &cellId, float duration, float x, float y, float z, bool repeat)
|
||||||
: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
|
: TypedAiPackage<AiFollow>(repeat), mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
|
||||||
, mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++)
|
, mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++)
|
||||||
{
|
{
|
||||||
mTargetActorRefId = actorId;
|
mTargetActorRefId = actorId;
|
||||||
}
|
}
|
||||||
|
|
||||||
AiFollow::AiFollow(const MWWorld::Ptr& actor, float duration, float x, float y, float z)
|
|
||||||
: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
|
|
||||||
, mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++)
|
|
||||||
{
|
|
||||||
mTargetActorRefId = actor.getCellRef().getRefId();
|
|
||||||
mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
|
||||||
}
|
|
||||||
|
|
||||||
AiFollow::AiFollow(const MWWorld::Ptr& actor, const std::string &cellId, float duration, float x, float y, float z)
|
|
||||||
: mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
|
|
||||||
, mCellId(cellId), mActive(false), mFollowIndex(mFollowIndexCounter++)
|
|
||||||
{
|
|
||||||
mTargetActorRefId = actor.getCellRef().getRefId();
|
|
||||||
mTargetActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
|
||||||
}
|
|
||||||
|
|
||||||
AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded)
|
AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded)
|
||||||
: TypedAiPackage<AiFollow>(makeDefaultOptions().withShouldCancelPreviousAi(!commanded))
|
: TypedAiPackage<AiFollow>(makeDefaultOptions().withShouldCancelPreviousAi(!commanded))
|
||||||
, mAlwaysFollow(true), mDuration(0), mRemainingDuration(0), mX(0), mY(0), mZ(0)
|
, mAlwaysFollow(true), mDuration(0), mRemainingDuration(0), mX(0), mY(0), mZ(0)
|
||||||
|
@ -68,12 +52,9 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow)
|
AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow)
|
||||||
: TypedAiPackage<AiFollow>(makeDefaultOptions().withShouldCancelPreviousAi(!follow->mCommanded))
|
: TypedAiPackage<AiFollow>(makeDefaultOptions().withShouldCancelPreviousAi(!follow->mCommanded).withRepeat(follow->mRepeat))
|
||||||
, mAlwaysFollow(follow->mAlwaysFollow)
|
, mAlwaysFollow(follow->mAlwaysFollow)
|
||||||
// mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration.
|
, mDuration(follow->mData.mDuration)
|
||||||
// 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(follow->mRemainingDuration)
|
|
||||||
, mRemainingDuration(follow->mRemainingDuration)
|
, mRemainingDuration(follow->mRemainingDuration)
|
||||||
, mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ)
|
, mX(follow->mData.mX), mY(follow->mData.mY), mZ(follow->mData.mZ)
|
||||||
, mCellId(follow->mCellId), mActive(follow->mActive), mFollowIndex(mFollowIndexCounter++)
|
, mCellId(follow->mCellId), mActive(follow->mActive), mFollowIndex(mFollowIndexCounter++)
|
||||||
|
@ -160,12 +141,15 @@ bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
||||||
if (actor.getCell()->isExterior()) //Outside?
|
if (actor.getCell()->isExterior()) //Outside?
|
||||||
{
|
{
|
||||||
if (mCellId == "") //No cell to travel to
|
if (mCellId == "") //No cell to travel to
|
||||||
|
{
|
||||||
|
mRemainingDuration = mDuration;
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (mCellId == actor.getCell()->getCell()->mName) //Cell to travel to
|
||||||
{
|
{
|
||||||
if (mCellId == actor.getCell()->getCell()->mName) //Cell to travel to
|
mRemainingDuration = mDuration;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,6 +205,7 @@ void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
|
||||||
follow->mData.mX = mX;
|
follow->mData.mX = mX;
|
||||||
follow->mData.mY = mY;
|
follow->mData.mY = mY;
|
||||||
follow->mData.mZ = mZ;
|
follow->mData.mZ = mZ;
|
||||||
|
follow->mData.mDuration = mDuration;
|
||||||
follow->mTargetId = mTargetActorRefId;
|
follow->mTargetId = mTargetActorRefId;
|
||||||
follow->mTargetActorId = mTargetActorId;
|
follow->mTargetActorId = mTargetActorId;
|
||||||
follow->mRemainingDuration = mRemainingDuration;
|
follow->mRemainingDuration = mRemainingDuration;
|
||||||
|
@ -228,6 +213,7 @@ void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
|
||||||
follow->mAlwaysFollow = mAlwaysFollow;
|
follow->mAlwaysFollow = mAlwaysFollow;
|
||||||
follow->mCommanded = isCommanded();
|
follow->mCommanded = isCommanded();
|
||||||
follow->mActive = mActive;
|
follow->mActive = mActive;
|
||||||
|
follow->mRepeat = getRepeat();
|
||||||
|
|
||||||
ESM::AiSequence::AiPackageContainer package;
|
ESM::AiSequence::AiPackageContainer package;
|
||||||
package.mType = ESM::AiSequence::Ai_Follow;
|
package.mType = ESM::AiSequence::Ai_Follow;
|
||||||
|
|
|
@ -40,12 +40,10 @@ namespace MWMechanics
|
||||||
class AiFollow final : public TypedAiPackage<AiFollow>
|
class AiFollow final : public TypedAiPackage<AiFollow>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AiFollow(const std::string &actorId, float duration, float x, float y, float z);
|
|
||||||
AiFollow(const std::string &actorId, const std::string &CellId, float duration, float x, float y, float z);
|
|
||||||
/// Follow Actor for duration or until you arrive at a world position
|
/// Follow Actor for duration or until you arrive at a world position
|
||||||
AiFollow(const MWWorld::Ptr& actor, float duration, float X, float Y, float Z);
|
AiFollow(const std::string &actorId, float duration, float x, float y, float z, bool repeat);
|
||||||
/// Follow Actor for duration or until you arrive at a position in a cell
|
/// Follow Actor for duration or until you arrive at a position in a cell
|
||||||
AiFollow(const MWWorld::Ptr& actor, const std::string &CellId, float duration, float X, float Y, float Z);
|
AiFollow(const std::string &actorId, const std::string &CellId, float duration, float x, float y, float z, bool repeat);
|
||||||
/// Follow Actor indefinitively
|
/// Follow Actor indefinitively
|
||||||
AiFollow(const MWWorld::Ptr& actor, bool commanded=false);
|
AiFollow(const MWWorld::Ptr& actor, bool commanded=false);
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ namespace MWMechanics
|
||||||
/// Upon adding this Ai package, should the Ai Sequence attempt to cancel previous Ai packages (default true)?
|
/// Upon adding this Ai package, should the Ai Sequence attempt to cancel previous Ai packages (default true)?
|
||||||
bool shouldCancelPreviousAi() const { return mOptions.mShouldCancelPreviousAi; }
|
bool shouldCancelPreviousAi() const { return mOptions.mShouldCancelPreviousAi; }
|
||||||
|
|
||||||
/// Return true if this package should repeat. Currently only used for Wander packages.
|
/// Return true if this package should repeat.
|
||||||
bool getRepeat() const { return mOptions.mRepeat; }
|
bool getRepeat() const { return mOptions.mRepeat; }
|
||||||
|
|
||||||
virtual osg::Vec3f getDestination() const { return osg::Vec3f(0, 0, 0); }
|
virtual osg::Vec3f getDestination() const { return osg::Vec3f(0, 0, 0); }
|
||||||
|
|
|
@ -31,14 +31,13 @@ void AiSequence::copy (const AiSequence& sequence)
|
||||||
sequence.mAiState.copy<AiWanderStorage>(mAiState);
|
sequence.mAiState.copy<AiWanderStorage>(mAiState);
|
||||||
}
|
}
|
||||||
|
|
||||||
AiSequence::AiSequence() : mDone (false), mRepeat(false), mLastAiPackage(AiPackageTypeId::None) {}
|
AiSequence::AiSequence() : mDone (false), mLastAiPackage(AiPackageTypeId::None) {}
|
||||||
|
|
||||||
AiSequence::AiSequence (const AiSequence& sequence)
|
AiSequence::AiSequence (const AiSequence& sequence)
|
||||||
{
|
{
|
||||||
copy (sequence);
|
copy (sequence);
|
||||||
mDone = sequence.mDone;
|
mDone = sequence.mDone;
|
||||||
mLastAiPackage = sequence.mLastAiPackage;
|
mLastAiPackage = sequence.mLastAiPackage;
|
||||||
mRepeat = sequence.mRepeat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AiSequence& AiSequence::operator= (const AiSequence& sequence)
|
AiSequence& AiSequence::operator= (const AiSequence& sequence)
|
||||||
|
@ -281,7 +280,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
||||||
if (package->execute(actor, characterController, mAiState, duration))
|
if (package->execute(actor, characterController, mAiState, duration))
|
||||||
{
|
{
|
||||||
// Put repeating noncombat AI packages on the end of the stack so they can be used again
|
// Put repeating noncombat AI packages on the end of the stack so they can be used again
|
||||||
if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat()))
|
if (isActualAiPackage(packageTypeId) && package->getRepeat())
|
||||||
{
|
{
|
||||||
package->reset();
|
package->reset();
|
||||||
mPackages.push_back(package->clone());
|
mPackages.push_back(package->clone());
|
||||||
|
@ -355,7 +354,6 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
|
||||||
else
|
else
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
mRepeat=false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert new package in correct place depending on priority
|
// insert new package in correct place depending on priority
|
||||||
|
@ -401,10 +399,6 @@ const AiPackage& MWMechanics::AiSequence::getActivePackage()
|
||||||
|
|
||||||
void AiSequence::fill(const ESM::AIPackageList &list)
|
void AiSequence::fill(const ESM::AIPackageList &list)
|
||||||
{
|
{
|
||||||
// If there is more than one package in the list, enable repeating
|
|
||||||
if (list.mList.size() >= 2)
|
|
||||||
mRepeat = true;
|
|
||||||
|
|
||||||
for (const auto& esmPackage : list.mList)
|
for (const auto& esmPackage : list.mList)
|
||||||
{
|
{
|
||||||
std::unique_ptr<MWMechanics::AiPackage> package;
|
std::unique_ptr<MWMechanics::AiPackage> package;
|
||||||
|
@ -420,22 +414,22 @@ void AiSequence::fill(const ESM::AIPackageList &list)
|
||||||
else if (esmPackage.mType == ESM::AI_Escort)
|
else if (esmPackage.mType == ESM::AI_Escort)
|
||||||
{
|
{
|
||||||
ESM::AITarget data = esmPackage.mTarget;
|
ESM::AITarget data = esmPackage.mTarget;
|
||||||
package = std::make_unique<MWMechanics::AiEscort>(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);
|
package = std::make_unique<MWMechanics::AiEscort>(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ, data.mShouldRepeat != 0);
|
||||||
}
|
}
|
||||||
else if (esmPackage.mType == ESM::AI_Travel)
|
else if (esmPackage.mType == ESM::AI_Travel)
|
||||||
{
|
{
|
||||||
ESM::AITravel data = esmPackage.mTravel;
|
ESM::AITravel data = esmPackage.mTravel;
|
||||||
package = std::make_unique<MWMechanics::AiTravel>(data.mX, data.mY, data.mZ);
|
package = std::make_unique<MWMechanics::AiTravel>(data.mX, data.mY, data.mZ, data.mShouldRepeat != 0);
|
||||||
}
|
}
|
||||||
else if (esmPackage.mType == ESM::AI_Activate)
|
else if (esmPackage.mType == ESM::AI_Activate)
|
||||||
{
|
{
|
||||||
ESM::AIActivate data = esmPackage.mActivate;
|
ESM::AIActivate data = esmPackage.mActivate;
|
||||||
package = std::make_unique<MWMechanics::AiActivate>(data.mName.toString());
|
package = std::make_unique<MWMechanics::AiActivate>(data.mName.toString(), data.mShouldRepeat != 0);
|
||||||
}
|
}
|
||||||
else //if (esmPackage.mType == ESM::AI_Follow)
|
else //if (esmPackage.mType == ESM::AI_Follow)
|
||||||
{
|
{
|
||||||
ESM::AITarget data = esmPackage.mTarget;
|
ESM::AITarget data = esmPackage.mTarget;
|
||||||
package = std::make_unique<MWMechanics::AiFollow>(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);
|
package = std::make_unique<MWMechanics::AiFollow>(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ, data.mShouldRepeat != 0);
|
||||||
}
|
}
|
||||||
mPackages.push_back(std::move(package));
|
mPackages.push_back(std::move(package));
|
||||||
}
|
}
|
||||||
|
@ -454,24 +448,6 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
||||||
if (!sequence.mPackages.empty())
|
if (!sequence.mPackages.empty())
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
// If there is more than one non-combat, non-pursue package in the list, enable repeating.
|
|
||||||
int count = 0;
|
|
||||||
for (auto& container : sequence.mPackages)
|
|
||||||
{
|
|
||||||
switch (container.mType)
|
|
||||||
{
|
|
||||||
case ESM::AiSequence::Ai_Wander:
|
|
||||||
case ESM::AiSequence::Ai_Travel:
|
|
||||||
case ESM::AiSequence::Ai_Escort:
|
|
||||||
case ESM::AiSequence::Ai_Follow:
|
|
||||||
case ESM::AiSequence::Ai_Activate:
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 1)
|
|
||||||
mRepeat = true;
|
|
||||||
|
|
||||||
// Load packages
|
// Load packages
|
||||||
for (auto& container : sequence.mPackages)
|
for (auto& container : sequence.mPackages)
|
||||||
{
|
{
|
||||||
|
|
|
@ -43,9 +43,6 @@ namespace MWMechanics
|
||||||
///Finished with top AIPackage, set for one frame
|
///Finished with top AIPackage, set for one frame
|
||||||
bool mDone;
|
bool mDone;
|
||||||
|
|
||||||
///Does this AI sequence repeat (repeating of Wander packages handled separately)
|
|
||||||
bool mRepeat;
|
|
||||||
|
|
||||||
///Copy AiSequence
|
///Copy AiSequence
|
||||||
void copy (const AiSequence& sequence);
|
void copy (const AiSequence& sequence);
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,8 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2)
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
AiTravel::AiTravel(float x, float y, float z, AiTravel*)
|
AiTravel::AiTravel(float x, float y, float z, bool repeat, AiTravel*)
|
||||||
: mX(x), mY(y), mZ(z), mHidden(false)
|
: TypedAiPackage<AiTravel>(repeat), mX(x), mY(y), mZ(z), mHidden(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,13 +44,13 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
AiTravel::AiTravel(float x, float y, float z)
|
AiTravel::AiTravel(float x, float y, float z, bool repeat)
|
||||||
: AiTravel(x, y, z, this)
|
: AiTravel(x, y, z, repeat, this)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel)
|
AiTravel::AiTravel(const ESM::AiSequence::AiTravel *travel)
|
||||||
: mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(false)
|
: TypedAiPackage<AiTravel>(travel->mRepeat), mX(travel->mData.mX), mY(travel->mData.mY), mZ(travel->mData.mZ), mHidden(false)
|
||||||
{
|
{
|
||||||
// Hidden ESM::AiSequence::AiTravel package should be converted into MWMechanics::AiInternalTravel type
|
// Hidden ESM::AiSequence::AiTravel package should be converted into MWMechanics::AiInternalTravel type
|
||||||
assert(!travel->mHidden);
|
assert(!travel->mHidden);
|
||||||
|
@ -125,6 +125,7 @@ namespace MWMechanics
|
||||||
travel->mData.mY = mY;
|
travel->mData.mY = mY;
|
||||||
travel->mData.mZ = mZ;
|
travel->mData.mZ = mZ;
|
||||||
travel->mHidden = mHidden;
|
travel->mHidden = mHidden;
|
||||||
|
travel->mRepeat = getRepeat();
|
||||||
|
|
||||||
ESM::AiSequence::AiPackageContainer package;
|
ESM::AiSequence::AiPackageContainer package;
|
||||||
package.mType = ESM::AiSequence::Ai_Travel;
|
package.mType = ESM::AiSequence::Ai_Travel;
|
||||||
|
|
|
@ -19,11 +19,11 @@ namespace MWMechanics
|
||||||
class AiTravel : public TypedAiPackage<AiTravel>
|
class AiTravel : public TypedAiPackage<AiTravel>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AiTravel(float x, float y, float z, AiTravel* derived);
|
AiTravel(float x, float y, float z, bool repeat, AiTravel* derived);
|
||||||
|
|
||||||
AiTravel(float x, float y, float z, AiInternalTravel* derived);
|
AiTravel(float x, float y, float z, AiInternalTravel* derived);
|
||||||
|
|
||||||
AiTravel(float x, float y, float z);
|
AiTravel(float x, float y, float z, bool repeat);
|
||||||
|
|
||||||
explicit AiTravel(const ESM::AiSequence::AiTravel* travel);
|
explicit AiTravel(const ESM::AiSequence::AiTravel* travel);
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
||||||
TypedAiPackage<AiWander>(makeDefaultOptions().withRepeat(repeat)),
|
TypedAiPackage<AiWander>(repeat),
|
||||||
mDistance(std::max(0, distance)),
|
mDistance(std::max(0, distance)),
|
||||||
mDuration(std::max(0, duration)),
|
mDuration(std::max(0, duration)),
|
||||||
mRemainingDuration(duration), mTimeOfDay(timeOfDay),
|
mRemainingDuration(duration), mTimeOfDay(timeOfDay),
|
||||||
|
|
|
@ -11,6 +11,9 @@ namespace MWMechanics
|
||||||
TypedAiPackage() :
|
TypedAiPackage() :
|
||||||
AiPackage(T::getTypeId(), T::makeDefaultOptions()) {}
|
AiPackage(T::getTypeId(), T::makeDefaultOptions()) {}
|
||||||
|
|
||||||
|
TypedAiPackage(bool repeat) :
|
||||||
|
AiPackage(T::getTypeId(), T::makeDefaultOptions().withRepeat(repeat)) {}
|
||||||
|
|
||||||
TypedAiPackage(const Options& options) :
|
TypedAiPackage(const Options& options) :
|
||||||
AiPackage(T::getTypeId(), options) {}
|
AiPackage(T::getTypeId(), options) {}
|
||||||
|
|
||||||
|
|
|
@ -48,13 +48,14 @@ namespace MWScript
|
||||||
std::string objectID = runtime.getStringLiteral (runtime[0].mInteger);
|
std::string objectID = runtime.getStringLiteral (runtime[0].mInteger);
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
// discard additional arguments (reset), because we have no idea what they mean.
|
// The value of the reset argument doesn't actually matter
|
||||||
|
bool repeat = arg0;
|
||||||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||||
|
|
||||||
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MWMechanics::AiActivate activatePackage(objectID);
|
MWMechanics::AiActivate activatePackage(objectID, repeat);
|
||||||
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(activatePackage, ptr);
|
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(activatePackage, ptr);
|
||||||
Log(Debug::Info) << "AiActivate";
|
Log(Debug::Info) << "AiActivate";
|
||||||
}
|
}
|
||||||
|
@ -78,13 +79,14 @@ namespace MWScript
|
||||||
Interpreter::Type_Float z = runtime[0].mFloat;
|
Interpreter::Type_Float z = runtime[0].mFloat;
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
// discard additional arguments (reset), because we have no idea what they mean.
|
// The value of the reset argument doesn't actually matter
|
||||||
|
bool repeat = arg0;
|
||||||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||||
|
|
||||||
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MWMechanics::AiTravel travelPackage(x, y, z);
|
MWMechanics::AiTravel travelPackage(x, y, z, repeat);
|
||||||
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(travelPackage, ptr);
|
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(travelPackage, ptr);
|
||||||
|
|
||||||
Log(Debug::Info) << "AiTravel: " << x << ", " << y << ", " << z;
|
Log(Debug::Info) << "AiTravel: " << x << ", " << y << ", " << z;
|
||||||
|
@ -115,13 +117,14 @@ namespace MWScript
|
||||||
Interpreter::Type_Float z = runtime[0].mFloat;
|
Interpreter::Type_Float z = runtime[0].mFloat;
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
// discard additional arguments (reset), because we have no idea what they mean.
|
// The value of the reset argument doesn't actually matter
|
||||||
|
bool repeat = arg0;
|
||||||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||||
|
|
||||||
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MWMechanics::AiEscort escortPackage(actorID, static_cast<int>(duration), x, y, z);
|
MWMechanics::AiEscort escortPackage(actorID, static_cast<int>(duration), x, y, z, repeat);
|
||||||
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);
|
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);
|
||||||
|
|
||||||
Log(Debug::Info) << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration;
|
Log(Debug::Info) << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration;
|
||||||
|
@ -155,7 +158,8 @@ namespace MWScript
|
||||||
Interpreter::Type_Float z = runtime[0].mFloat;
|
Interpreter::Type_Float z = runtime[0].mFloat;
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
// discard additional arguments (reset), because we have no idea what they mean.
|
// The value of the reset argument doesn't actually matter
|
||||||
|
bool repeat = arg0;
|
||||||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||||
|
|
||||||
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
||||||
|
@ -167,7 +171,7 @@ namespace MWScript
|
||||||
if (!MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>().search(cellID))
|
if (!MWBase::Environment::get().getWorld()->getStore().get<ESM::Cell>().search(cellID))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MWMechanics::AiEscort escortPackage(actorID, cellID, static_cast<int>(duration), x, y, z);
|
MWMechanics::AiEscort escortPackage(actorID, cellID, static_cast<int>(duration), x, y, z, repeat);
|
||||||
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);
|
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(escortPackage, ptr);
|
||||||
|
|
||||||
Log(Debug::Info) << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration;
|
Log(Debug::Info) << "AiEscort: " << x << ", " << y << ", " << z << ", " << duration;
|
||||||
|
@ -237,7 +241,7 @@ namespace MWScript
|
||||||
--arg0;
|
--arg0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// discard additional arguments (reset), because we have no idea what they mean.
|
// discard additional arguments, because we have no idea what they mean.
|
||||||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||||
|
|
||||||
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
||||||
|
@ -331,13 +335,14 @@ namespace MWScript
|
||||||
Interpreter::Type_Float z = runtime[0].mFloat;
|
Interpreter::Type_Float z = runtime[0].mFloat;
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
// discard additional arguments (reset), because we have no idea what they mean.
|
// The value of the reset argument doesn't actually matter
|
||||||
|
bool repeat = arg0;
|
||||||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||||
|
|
||||||
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MWMechanics::AiFollow followPackage(actorID, duration, x, y ,z);
|
MWMechanics::AiFollow followPackage(actorID, duration, x, y, z, repeat);
|
||||||
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
|
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
|
||||||
|
|
||||||
Log(Debug::Info) << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration;
|
Log(Debug::Info) << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration;
|
||||||
|
@ -371,13 +376,14 @@ namespace MWScript
|
||||||
Interpreter::Type_Float z = runtime[0].mFloat;
|
Interpreter::Type_Float z = runtime[0].mFloat;
|
||||||
runtime.pop();
|
runtime.pop();
|
||||||
|
|
||||||
// discard additional arguments (reset), because we have no idea what they mean.
|
// The value of the reset argument doesn't actually matter
|
||||||
|
bool repeat = arg0;
|
||||||
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
for (unsigned int i=0; i<arg0; ++i) runtime.pop();
|
||||||
|
|
||||||
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
if (!ptr.getClass().isActor() || ptr == MWMechanics::getPlayer())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MWMechanics::AiFollow followPackage(actorID, cellID, duration, x, y ,z);
|
MWMechanics::AiFollow followPackage(actorID, cellID, duration, x, y, z, repeat);
|
||||||
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
|
ptr.getClass().getCreatureStats (ptr).getAiSequence().stack(followPackage, ptr);
|
||||||
Log(Debug::Info) << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration;
|
Log(Debug::Info) << "AiFollow: " << actorID << ", " << x << ", " << y << ", " << z << ", " << duration;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,8 @@ namespace ESM
|
||||||
struct AITravel
|
struct AITravel
|
||||||
{
|
{
|
||||||
float mX, mY, mZ;
|
float mX, mY, mZ;
|
||||||
int mUnk;
|
unsigned char mShouldRepeat;
|
||||||
|
unsigned char mPadding[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AITarget
|
struct AITarget
|
||||||
|
@ -45,13 +46,14 @@ namespace ESM
|
||||||
float mX, mY, mZ;
|
float mX, mY, mZ;
|
||||||
short mDuration;
|
short mDuration;
|
||||||
NAME32 mId;
|
NAME32 mId;
|
||||||
short mUnk;
|
unsigned char mShouldRepeat;
|
||||||
|
unsigned char mPadding;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIActivate
|
struct AIActivate
|
||||||
{
|
{
|
||||||
NAME32 mName;
|
NAME32 mName;
|
||||||
unsigned char mUnk;
|
unsigned char mShouldRepeat;
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "esmreader.hpp"
|
#include "esmreader.hpp"
|
||||||
#include "esmwriter.hpp"
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
|
@ -34,12 +35,16 @@ namespace AiSequence
|
||||||
{
|
{
|
||||||
esm.getHNT (mData, "DATA");
|
esm.getHNT (mData, "DATA");
|
||||||
esm.getHNOT (mHidden, "HIDD");
|
esm.getHNOT (mHidden, "HIDD");
|
||||||
|
mRepeat = false;
|
||||||
|
esm.getHNOT(mRepeat, "REPT");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiTravel::save(ESMWriter &esm) const
|
void AiTravel::save(ESMWriter &esm) const
|
||||||
{
|
{
|
||||||
esm.writeHNT ("DATA", mData);
|
esm.writeHNT ("DATA", mData);
|
||||||
esm.writeHNT ("HIDD", mHidden);
|
esm.writeHNT ("HIDD", mHidden);
|
||||||
|
if(mRepeat)
|
||||||
|
esm.writeHNT("REPT", mRepeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiEscort::load(ESMReader &esm)
|
void AiEscort::load(ESMReader &esm)
|
||||||
|
@ -50,6 +55,15 @@ namespace AiSequence
|
||||||
esm.getHNOT (mTargetActorId, "TAID");
|
esm.getHNOT (mTargetActorId, "TAID");
|
||||||
esm.getHNT (mRemainingDuration, "DURA");
|
esm.getHNT (mRemainingDuration, "DURA");
|
||||||
mCellId = esm.getHNOString ("CELL");
|
mCellId = esm.getHNOString ("CELL");
|
||||||
|
mRepeat = false;
|
||||||
|
esm.getHNOT(mRepeat, "REPT");
|
||||||
|
if(esm.getFormat() < 18)
|
||||||
|
{
|
||||||
|
// 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.
|
||||||
|
mData.mDuration = std::max<float>(mRemainingDuration > 0, mRemainingDuration);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiEscort::save(ESMWriter &esm) const
|
void AiEscort::save(ESMWriter &esm) const
|
||||||
|
@ -60,6 +74,8 @@ namespace AiSequence
|
||||||
esm.writeHNT ("DURA", mRemainingDuration);
|
esm.writeHNT ("DURA", mRemainingDuration);
|
||||||
if (!mCellId.empty())
|
if (!mCellId.empty())
|
||||||
esm.writeHNString ("CELL", mCellId);
|
esm.writeHNString ("CELL", mCellId);
|
||||||
|
if(mRepeat)
|
||||||
|
esm.writeHNT("REPT", mRepeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiFollow::load(ESMReader &esm)
|
void AiFollow::load(ESMReader &esm)
|
||||||
|
@ -75,6 +91,15 @@ namespace AiSequence
|
||||||
esm.getHNOT (mCommanded, "CMND");
|
esm.getHNOT (mCommanded, "CMND");
|
||||||
mActive = false;
|
mActive = false;
|
||||||
esm.getHNOT (mActive, "ACTV");
|
esm.getHNOT (mActive, "ACTV");
|
||||||
|
mRepeat = false;
|
||||||
|
esm.getHNOT(mRepeat, "REPT");
|
||||||
|
if(esm.getFormat() < 18)
|
||||||
|
{
|
||||||
|
// 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.
|
||||||
|
mData.mDuration = std::max<float>(mRemainingDuration > 0, mRemainingDuration);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiFollow::save(ESMWriter &esm) const
|
void AiFollow::save(ESMWriter &esm) const
|
||||||
|
@ -89,16 +114,22 @@ namespace AiSequence
|
||||||
esm.writeHNT ("CMND", mCommanded);
|
esm.writeHNT ("CMND", mCommanded);
|
||||||
if (mActive)
|
if (mActive)
|
||||||
esm.writeHNT("ACTV", mActive);
|
esm.writeHNT("ACTV", mActive);
|
||||||
|
if(mRepeat)
|
||||||
|
esm.writeHNT("REPT", mRepeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiActivate::load(ESMReader &esm)
|
void AiActivate::load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
mTargetId = esm.getHNString("TARG");
|
mTargetId = esm.getHNString("TARG");
|
||||||
|
mRepeat = false;
|
||||||
|
esm.getHNOT(mRepeat, "REPT");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiActivate::save(ESMWriter &esm) const
|
void AiActivate::save(ESMWriter &esm) const
|
||||||
{
|
{
|
||||||
esm.writeHNString("TARG", mTargetId);
|
esm.writeHNString("TARG", mTargetId);
|
||||||
|
if(mRepeat)
|
||||||
|
esm.writeHNT("REPT", mRepeat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AiCombat::load(ESMReader &esm)
|
void AiCombat::load(ESMReader &esm)
|
||||||
|
@ -166,6 +197,7 @@ namespace AiSequence
|
||||||
|
|
||||||
void AiSequence::load(ESMReader &esm)
|
void AiSequence::load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
|
int count = 0;
|
||||||
while (esm.isNextSub("AIPK"))
|
while (esm.isNextSub("AIPK"))
|
||||||
{
|
{
|
||||||
int type;
|
int type;
|
||||||
|
@ -181,6 +213,7 @@ namespace AiSequence
|
||||||
std::unique_ptr<AiWander> ptr = std::make_unique<AiWander>();
|
std::unique_ptr<AiWander> ptr = std::make_unique<AiWander>();
|
||||||
ptr->load(esm);
|
ptr->load(esm);
|
||||||
mPackages.back().mPackage = ptr.release();
|
mPackages.back().mPackage = ptr.release();
|
||||||
|
++count;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Ai_Travel:
|
case Ai_Travel:
|
||||||
|
@ -188,6 +221,7 @@ namespace AiSequence
|
||||||
std::unique_ptr<AiTravel> ptr = std::make_unique<AiTravel>();
|
std::unique_ptr<AiTravel> ptr = std::make_unique<AiTravel>();
|
||||||
ptr->load(esm);
|
ptr->load(esm);
|
||||||
mPackages.back().mPackage = ptr.release();
|
mPackages.back().mPackage = ptr.release();
|
||||||
|
++count;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Ai_Escort:
|
case Ai_Escort:
|
||||||
|
@ -195,6 +229,7 @@ namespace AiSequence
|
||||||
std::unique_ptr<AiEscort> ptr = std::make_unique<AiEscort>();
|
std::unique_ptr<AiEscort> ptr = std::make_unique<AiEscort>();
|
||||||
ptr->load(esm);
|
ptr->load(esm);
|
||||||
mPackages.back().mPackage = ptr.release();
|
mPackages.back().mPackage = ptr.release();
|
||||||
|
++count;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Ai_Follow:
|
case Ai_Follow:
|
||||||
|
@ -202,6 +237,7 @@ namespace AiSequence
|
||||||
std::unique_ptr<AiFollow> ptr = std::make_unique<AiFollow>();
|
std::unique_ptr<AiFollow> ptr = std::make_unique<AiFollow>();
|
||||||
ptr->load(esm);
|
ptr->load(esm);
|
||||||
mPackages.back().mPackage = ptr.release();
|
mPackages.back().mPackage = ptr.release();
|
||||||
|
++count;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Ai_Activate:
|
case Ai_Activate:
|
||||||
|
@ -209,6 +245,7 @@ namespace AiSequence
|
||||||
std::unique_ptr<AiActivate> ptr = std::make_unique<AiActivate>();
|
std::unique_ptr<AiActivate> ptr = std::make_unique<AiActivate>();
|
||||||
ptr->load(esm);
|
ptr->load(esm);
|
||||||
mPackages.back().mPackage = ptr.release();
|
mPackages.back().mPackage = ptr.release();
|
||||||
|
++count;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Ai_Combat:
|
case Ai_Combat:
|
||||||
|
@ -231,6 +268,23 @@ namespace AiSequence
|
||||||
}
|
}
|
||||||
|
|
||||||
esm.getHNOT (mLastAiPackage, "LAST");
|
esm.getHNOT (mLastAiPackage, "LAST");
|
||||||
|
|
||||||
|
if(count > 1 && esm.getFormat() < 18)
|
||||||
|
{
|
||||||
|
for(auto& pkg : mPackages)
|
||||||
|
{
|
||||||
|
if(pkg.mType == Ai_Wander)
|
||||||
|
static_cast<AiWander*>(pkg.mPackage)->mData.mShouldRepeat = true;
|
||||||
|
else if(pkg.mType == Ai_Travel)
|
||||||
|
static_cast<AiTravel*>(pkg.mPackage)->mRepeat = true;
|
||||||
|
else if(pkg.mType == Ai_Escort)
|
||||||
|
static_cast<AiEscort*>(pkg.mPackage)->mRepeat = true;
|
||||||
|
else if(pkg.mType == Ai_Follow)
|
||||||
|
static_cast<AiFollow*>(pkg.mPackage)->mRepeat = true;
|
||||||
|
else if(pkg.mType == Ai_Activate)
|
||||||
|
static_cast<AiActivate*>(pkg.mPackage)->mRepeat = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,7 @@ namespace ESM
|
||||||
{
|
{
|
||||||
AiTravelData mData;
|
AiTravelData mData;
|
||||||
bool mHidden;
|
bool mHidden;
|
||||||
|
bool mRepeat;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm) const;
|
||||||
|
@ -94,6 +95,7 @@ namespace ESM
|
||||||
std::string mTargetId;
|
std::string mTargetId;
|
||||||
std::string mCellId;
|
std::string mCellId;
|
||||||
float mRemainingDuration;
|
float mRemainingDuration;
|
||||||
|
bool mRepeat;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm) const;
|
||||||
|
@ -112,6 +114,7 @@ namespace ESM
|
||||||
bool mCommanded;
|
bool mCommanded;
|
||||||
|
|
||||||
bool mActive;
|
bool mActive;
|
||||||
|
bool mRepeat;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm) const;
|
||||||
|
@ -120,6 +123,7 @@ namespace ESM
|
||||||
struct AiActivate : AiPackage
|
struct AiActivate : AiPackage
|
||||||
{
|
{
|
||||||
std::string mTargetId;
|
std::string mTargetId;
|
||||||
|
bool mRepeat;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm);
|
||||||
void save(ESMWriter &esm) const;
|
void save(ESMWriter &esm) const;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "esmwriter.hpp"
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
|
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
|
||||||
int ESM::SavedGame::sCurrentFormat = 17;
|
int ESM::SavedGame::sCurrentFormat = 18;
|
||||||
|
|
||||||
void ESM::SavedGame::load (ESMReader &esm)
|
void ESM::SavedGame::load (ESMReader &esm)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue