1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-04 07:13:29 +00:00

Save repeat and duration

This commit is contained in:
Evil Eye 2021-11-17 20:44:55 +01:00
parent 231da19aa4
commit 3c57ffd81f
9 changed files with 82 additions and 19 deletions

View File

@ -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

View File

@ -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;

View File

@ -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)
{ {
} }
} }

View File

@ -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;

View File

@ -52,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++)
@ -144,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;
} }
} }
} }
@ -205,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;
@ -212,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;

View File

@ -50,7 +50,7 @@ namespace MWMechanics
} }
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;

View File

@ -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;
}
}
} }
} }
} }

View File

@ -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;

View File

@ -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)
{ {