1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-07-21 05:14:10 +00:00

Add OpenMW commits up to 11 Jun 2020

# Conflicts:
#	apps/openmw/mwbase/world.hpp
#	apps/openmw/mwgui/jailscreen.cpp
#	apps/openmw/mwmechanics/activespells.cpp
#	apps/openmw/mwmechanics/aiactivate.cpp
#	apps/openmw/mwmechanics/aiactivate.hpp
#	apps/openmw/mwmechanics/creaturestats.cpp
#	apps/openmw/mwscript/aiextensions.cpp
#	apps/openmw/mwscript/statsextensions.cpp
#	apps/openmw/mwworld/worldimp.cpp
#	apps/openmw/mwworld/worldimp.hpp
This commit is contained in:
David Cernat 2020-06-11 13:21:58 +03:00
commit e5b1843089
138 changed files with 2108 additions and 1543 deletions

View file

@ -4,6 +4,9 @@
Bug #1952: Incorrect particle lighting Bug #1952: Incorrect particle lighting
Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs
Bug #3676: NiParticleColorModifier isn't applied properly Bug #3676: NiParticleColorModifier isn't applied properly
Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects
Bug #4021: Attributes and skills are not stored as floats
Bug #4623: Corprus implementation is incorrect
Bug #4774: Guards are ignorant of an invisible player that tries to attack them Bug #4774: Guards are ignorant of an invisible player that tries to attack them
Bug #5108: Savegame bloating due to inefficient fog textures format Bug #5108: Savegame bloating due to inefficient fog textures format
Bug #5165: Active spells should use real time intead of timestamps Bug #5165: Active spells should use real time intead of timestamps
@ -15,6 +18,7 @@
Bug #5370: Opening an unlocked but trapped door uses the key Bug #5370: Opening an unlocked but trapped door uses the key
Bug #5397: NPC greeting does not reset if you leave + reenter area Bug #5397: NPC greeting does not reset if you leave + reenter area
Bug #5400: Editor: Verifier checks race of non-skin bodyparts Bug #5400: Editor: Verifier checks race of non-skin bodyparts
Bug #5403: Enchantment effect doesn't show on an enemy during death animation
Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work Bug #5415: Environment maps in ebony cuirass and HiRez Armors Indoril cuirass don't work
Bug #5416: Junk non-node records before the root node are not handled gracefully Bug #5416: Junk non-node records before the root node are not handled gracefully
Bug #5424: Creatures do not headtrack player Bug #5424: Creatures do not headtrack player
@ -22,7 +26,10 @@
Bug #5427: GetDistance unknown ID error is misleading Bug #5427: GetDistance unknown ID error is misleading
Bug #5435: Enemies can't hurt the player when collision is off Bug #5435: Enemies can't hurt the player when collision is off
Bug #5441: Enemies can't push a player character when in critical strike stance Bug #5441: Enemies can't push a player character when in critical strike stance
Bug #5451: Magic projectiles don't disappear with the caster
Bug #5452: Autowalk is being included in savegames
Feature #5362: Show the soul gems' trapped soul in count dialog Feature #5362: Show the soul gems' trapped soul in count dialog
Feature #5445: Handle NiLines
0.46.0 0.46.0
------ ------
@ -294,7 +301,7 @@
Feature #5147: Show spell magicka cost in spell buying window Feature #5147: Show spell magicka cost in spell buying window
Feature #5170: Editor: Land shape editing, land selection Feature #5170: Editor: Land shape editing, land selection
Feature #5172: Editor: Delete instances/references with keypress in scene window Feature #5172: Editor: Delete instances/references with keypress in scene window
Feature #5193: Weapon sheathing Feature #5193: Shields sheathing
Feature #5201: Editor: Show tool outline in scene view, when using editmodes Feature #5201: Editor: Show tool outline in scene view, when using editmodes
Feature #5219: Impelement TestCells console command Feature #5219: Impelement TestCells console command
Feature #5224: Handle NiKeyframeController for NiTriShape Feature #5224: Handle NiKeyframeController for NiTriShape

View file

@ -13,7 +13,16 @@ MISSINGTOOLS=0
command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; } command -v 7z >/dev/null 2>&1 || { echo "Error: 7z (7zip) is not on the path."; MISSINGTOOLS=1; }
command -v cmake >/dev/null 2>&1 || { echo "Error: cmake (CMake) is not on the path."; MISSINGTOOLS=1; } command -v cmake >/dev/null 2>&1 || { echo "Error: cmake (CMake) is not on the path."; MISSINGTOOLS=1; }
command -v python >/dev/null 2>&1 || { echo "Warning: Python is not on the path, automatic Qt installation impossible."; }
MISSINGPYTHON=0
if ! command -v python >/dev/null 2>&1; then
echo "Warning: Python is not on the path, automatic Qt installation impossible."
MISSINGPYTHON=1
elif ! python --version >/dev/null 2>&1; then
echo "Warning: Python is (probably) fake stub Python that comes bundled with newer versions of Windows, automatic Qt installation impossible."
echo "If you think you have Python installed, try changing the order of your PATH environment variable in Advanced System Settings."
MISSINGPYTHON=1
fi
if [ $MISSINGTOOLS -ne 0 ]; then if [ $MISSINGTOOLS -ne 0 ]; then
wrappedExit 1 wrappedExit 1
@ -745,6 +754,11 @@ fi
if [ -d 'Qt/5.15.0' ]; then if [ -d 'Qt/5.15.0' ]; then
printf "Exists. " printf "Exists. "
elif [ -z $SKIP_EXTRACT ]; then elif [ -z $SKIP_EXTRACT ]; then
if [ $MISSINGPYTHON -ne 0 ]; then
echo "Can't be automatically installed without Python."
wrappedExit 1
fi
pushd "$DEPS" > /dev/null pushd "$DEPS" > /dev/null
if ! [ -d 'aqt-venv' ]; then if ! [ -d 'aqt-venv' ]; then
echo " Creating Virtualenv for aqt..." echo " Creating Virtualenv for aqt..."
@ -988,6 +1002,11 @@ RET=$?
if [ -z $VERBOSE ]; then if [ -z $VERBOSE ]; then
if [ $RET -eq 0 ]; then if [ $RET -eq 0 ]; then
echo Done. echo Done.
else
echo Failed.
fi
fi
if [ -n $ACTIVATE_MSVC ]; then if [ -n $ACTIVATE_MSVC ]; then
echo echo
echo "Note: you must manually activate MSVC for the shell in which you want to do the build." echo "Note: you must manually activate MSVC for the shell in which you want to do the build."
@ -1014,8 +1033,5 @@ if [ -z $VERBOSE ]; then
fi fi
echo "In Visual Studio 15.3 (2017 Update 3) or later, try setting '\"inheritEnvironments\": [ \"$inheritEnvironments\" ]' in CMakeSettings.json to build in the IDE." echo "In Visual Studio 15.3 (2017 Update 3) or later, try setting '\"inheritEnvironments\": [ \"$inheritEnvironments\" ]' in CMakeSettings.json to build in the IDE."
fi fi
else
echo Failed.
fi
fi
wrappedExit $RET wrappedExit $RET

View file

@ -183,19 +183,19 @@ int list(Bsa::BSAFile& bsa, Arguments& info)
{ {
// List all files // List all files
const Bsa::BSAFile::FileList &files = bsa.getList(); const Bsa::BSAFile::FileList &files = bsa.getList();
for(unsigned int i=0; i<files.size(); i++) for (const auto& file : files)
{ {
if(info.longformat) if(info.longformat)
{ {
// Long format // Long format
std::ios::fmtflags f(std::cout.flags()); std::ios::fmtflags f(std::cout.flags());
std::cout << std::setw(50) << std::left << files[i].name; std::cout << std::setw(50) << std::left << file.name;
std::cout << std::setw(8) << std::left << std::dec << files[i].fileSize; std::cout << std::setw(8) << std::left << std::dec << file.fileSize;
std::cout << "@ 0x" << std::hex << files[i].offset << std::endl; std::cout << "@ 0x" << std::hex << file.offset << std::endl;
std::cout.flags(f); std::cout.flags(f);
} }
else else
std::cout << files[i].name << std::endl; std::cout << file.name << std::endl;
} }
return 0; return 0;
@ -252,14 +252,9 @@ int extract(Bsa::BSAFile& bsa, Arguments& info)
int extractAll(Bsa::BSAFile& bsa, Arguments& info) int extractAll(Bsa::BSAFile& bsa, Arguments& info)
{ {
// Get the list of files present in the archive for (const auto &file : bsa.getList())
Bsa::BSAFile::FileList list = bsa.getList(); {
std::string extractPath(file.name);
// Iter on the list
for(Bsa::BSAFile::FileList::iterator it = list.begin(); it != list.end(); ++it) {
const char* archivePath = it->name;
std::string extractPath (archivePath);
replaceAll(extractPath, "\\", "/"); replaceAll(extractPath, "\\", "/");
// Get the target path (the path the file will be extracted to) // Get the target path (the path the file will be extracted to)
@ -278,7 +273,7 @@ int extractAll(Bsa::BSAFile& bsa, Arguments& info)
// Get a stream for the file to extract // Get a stream for the file to extract
// (inefficient because getFile iter on the list again) // (inefficient because getFile iter on the list again)
Files::IStreamPtr data = bsa.getFile(archivePath); Files::IStreamPtr data = bsa.getFile(file.name);
bfs::ofstream out(target, std::ios::binary); bfs::ofstream out(target, std::ios::binary);
// Write the file to disk // Write the file to disk

View file

@ -352,12 +352,12 @@ int load(Arguments& info)
std::cout << "Author: " << esm.getAuthor() << std::endl std::cout << "Author: " << esm.getAuthor() << std::endl
<< "Description: " << esm.getDesc() << std::endl << "Description: " << esm.getDesc() << std::endl
<< "File format version: " << esm.getFVer() << std::endl; << "File format version: " << esm.getFVer() << std::endl;
std::vector<ESM::Header::MasterData> m = esm.getGameFiles(); std::vector<ESM::Header::MasterData> masterData = esm.getGameFiles();
if (!m.empty()) if (!masterData.empty())
{ {
std::cout << "Masters:" << std::endl; std::cout << "Masters:" << std::endl;
for(unsigned int i=0;i<m.size();i++) for(const auto& master : masterData)
std::cout << " " << m[i].name << ", " << m[i].size << " bytes" << std::endl; std::cout << " " << master.name << ", " << master.size << " bytes" << std::endl;
} }
} }
@ -369,7 +369,7 @@ int load(Arguments& info)
esm.getRecHeader(flags); esm.getRecHeader(flags);
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n); EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
if (record == 0) if (record == nullptr)
{ {
if (std::find(skipped.begin(), skipped.end(), n.intval) == skipped.end()) if (std::find(skipped.begin(), skipped.end(), n.intval) == skipped.end())
{ {
@ -538,8 +538,8 @@ int comp(Arguments& info)
Arguments fileOne; Arguments fileOne;
Arguments fileTwo; Arguments fileTwo;
fileOne.raw_given = 0; fileOne.raw_given = false;
fileTwo.raw_given = 0; fileTwo.raw_given = false;
fileOne.mode = "clone"; fileOne.mode = "clone";
fileTwo.mode = "clone"; fileTwo.mode = "clone";

View file

@ -779,7 +779,7 @@ std::string creatureListFlags(int flags)
std::string lightFlags(int flags) std::string lightFlags(int flags)
{ {
std::string properties = ""; std::string properties;
if (flags == 0) properties += "[None] "; if (flags == 0) properties += "[None] ";
if (flags & ESM::Light::Dynamic) properties += "Dynamic "; if (flags & ESM::Light::Dynamic) properties += "Dynamic ";
if (flags & ESM::Light::Fire) properties += "Fire "; if (flags & ESM::Light::Fire) properties += "Fire ";

View file

@ -9,7 +9,7 @@
namespace namespace
{ {
void printAIPackage(ESM::AIPackage p) void printAIPackage(const ESM::AIPackage& p)
{ {
std::cout << " AI Type: " << aiTypeLabel(p.mType) std::cout << " AI Type: " << aiTypeLabel(p.mType)
<< " (" << Misc::StringUtils::format("0x%08X", p.mType) << ")" << std::endl; << " (" << Misc::StringUtils::format("0x%08X", p.mType) << ")" << std::endl;
@ -53,7 +53,7 @@ void printAIPackage(ESM::AIPackage p)
std::cout << " Cell Name: " << p.mCellName << std::endl; std::cout << " Cell Name: " << p.mCellName << std::endl;
} }
std::string ruleString(ESM::DialInfo::SelectStruct ss) std::string ruleString(const ESM::DialInfo::SelectStruct& ss)
{ {
std::string rule = ss.mSelectRule; std::string rule = ss.mSelectRule;
@ -126,7 +126,7 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss)
return result; return result;
} }
void printEffectList(ESM::EffectList effects) void printEffectList(const ESM::EffectList& effects)
{ {
int i = 0; int i = 0;
for (const ESM::ENAMstruct& effect : effects.mList) for (const ESM::ENAMstruct& effect : effects.mList)
@ -174,7 +174,7 @@ namespace EsmTool {
RecordBase * RecordBase *
RecordBase::create(ESM::NAME type) RecordBase::create(ESM::NAME type)
{ {
RecordBase *record = 0; RecordBase *record = nullptr;
switch (type.intval) { switch (type.intval) {
case ESM::REC_ACTI: case ESM::REC_ACTI:
@ -388,7 +388,7 @@ RecordBase::create(ESM::NAME type)
break; break;
} }
default: default:
record = 0; record = nullptr;
} }
if (record) { if (record) {
record->mType = type; record->mType = type;
@ -728,10 +728,9 @@ void Record<ESM::Faction>::print()
<< " (" << mData.mData.mAttribute[0] << ")" << std::endl; << " (" << mData.mData.mAttribute[0] << ")" << std::endl;
std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1]) std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1])
<< " (" << mData.mData.mAttribute[1] << ")" << std::endl; << " (" << mData.mData.mAttribute[1] << ")" << std::endl;
for (int i = 0; i < 7; i++) for (int skill : mData.mData.mSkills)
if (mData.mData.mSkills[i] != -1) if (skill != -1)
std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i]) std::cout << " Skill: " << skillLabel(skill) << " (" << skill << ")" << std::endl;
<< " (" << mData.mData.mSkills[i] << ")" << std::endl;
for (int i = 0; i != 10; i++) for (int i = 0; i != 10; i++)
if (!mData.mRanks[i].empty()) if (!mData.mRanks[i].empty())
{ {

View file

@ -74,7 +74,7 @@ namespace EsmTool
: mIsDeleted(false) : mIsDeleted(false)
{} {}
std::string getId() const { std::string getId() const override {
return mData.mId; return mData.mId;
} }
@ -82,15 +82,15 @@ namespace EsmTool
return mData; return mData;
} }
void save(ESM::ESMWriter &esm) { void save(ESM::ESMWriter &esm) override {
mData.save(esm, mIsDeleted); mData.save(esm, mIsDeleted);
} }
void load(ESM::ESMReader &esm) { void load(ESM::ESMReader &esm) override {
mData.load(esm, mIsDeleted); mData.load(esm, mIsDeleted);
} }
void print(); void print() override;
}; };
template<> std::string Record<ESM::Cell>::getId() const; template<> std::string Record<ESM::Cell>::getId() const;

View file

@ -52,9 +52,7 @@ namespace
// a dynamically created record e.g. player-enchanted weapon // a dynamically created record e.g. player-enchanted weapon
std::string index = indexedRefId.substr(indexedRefId.size()-8); std::string index = indexedRefId.substr(indexedRefId.size()-8);
if(index.find_first_not_of("0123456789ABCDEF") == std::string::npos ) return index.find_first_not_of("0123456789ABCDEF") == std::string::npos;
return true;
return false;
} }
void splitIndexedRefId(const std::string& indexedRefId, int& refIndex, std::string& refId) void splitIndexedRefId(const std::string& indexedRefId, int& refIndex, std::string& refId)
@ -139,12 +137,12 @@ namespace ESSImport
image2->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE); image2->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE);
memcpy(image2->data(), &data[0], data.size()); memcpy(image2->data(), &data[0], data.size());
for (std::set<std::pair<int, int> >::const_iterator it = mContext->mExploredCells.begin(); it != mContext->mExploredCells.end(); ++it) for (const auto & exploredCell : mContext->mExploredCells)
{ {
if (it->first > mContext->mGlobalMapState.mBounds.mMaxX if (exploredCell.first > mContext->mGlobalMapState.mBounds.mMaxX
|| it->first < mContext->mGlobalMapState.mBounds.mMinX || exploredCell.first < mContext->mGlobalMapState.mBounds.mMinX
|| it->second > mContext->mGlobalMapState.mBounds.mMaxY || exploredCell.second > mContext->mGlobalMapState.mBounds.mMaxY
|| it->second < mContext->mGlobalMapState.mBounds.mMinY) || exploredCell.second < mContext->mGlobalMapState.mBounds.mMinY)
{ {
// out of bounds, I think this could happen, since the original engine had a fixed-size map // out of bounds, I think this could happen, since the original engine had a fixed-size map
continue; continue;
@ -152,12 +150,12 @@ namespace ESSImport
int imageLeftSrc = mGlobalMapImage->s()/2; int imageLeftSrc = mGlobalMapImage->s()/2;
int imageTopSrc = mGlobalMapImage->t()/2; int imageTopSrc = mGlobalMapImage->t()/2;
imageLeftSrc += it->first * cellSize; imageLeftSrc += exploredCell.first * cellSize;
imageTopSrc -= it->second * cellSize; imageTopSrc -= exploredCell.second * cellSize;
int imageLeftDst = width/2; int imageLeftDst = width/2;
int imageTopDst = height/2; int imageTopDst = height/2;
imageLeftDst += it->first * cellSize; imageLeftDst += exploredCell.first * cellSize;
imageTopDst -= it->second * cellSize; imageTopDst -= exploredCell.second * cellSize;
for (int x=0; x<cellSize; ++x) for (int x=0; x<cellSize; ++x)
for (int y=0; y<cellSize; ++y) for (int y=0; y<cellSize; ++y)
{ {
@ -329,9 +327,8 @@ namespace ESSImport
csta.mWaterLevel = esmcell.mWater; csta.mWaterLevel = esmcell.mWater;
csta.save(esm); csta.save(esm);
for (std::vector<CellRef>::const_iterator refIt = cell.mRefs.begin(); refIt != cell.mRefs.end(); ++refIt) for (const auto & cellref : cell.mRefs)
{ {
const CellRef& cellref = *refIt;
ESM::CellRef out (cellref); ESM::CellRef out (cellref);
// TODO: use mContext->mCreatures/mNpcs // TODO: use mContext->mCreatures/mNpcs
@ -437,16 +434,16 @@ namespace ESSImport
void ConvertCell::write(ESM::ESMWriter &esm) void ConvertCell::write(ESM::ESMWriter &esm)
{ {
for (std::map<std::string, Cell>::const_iterator it = mIntCells.begin(); it != mIntCells.end(); ++it) for (const auto & cell : mIntCells)
writeCell(it->second, esm); writeCell(cell.second, esm);
for (std::map<std::pair<int, int>, Cell>::const_iterator it = mExtCells.begin(); it != mExtCells.end(); ++it) for (const auto & cell : mExtCells)
writeCell(it->second, esm); writeCell(cell.second, esm);
for (std::vector<ESM::CustomMarker>::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it) for (const auto & marker : mMarkers)
{ {
esm.startRecord(ESM::REC_MARK); esm.startRecord(ESM::REC_MARK);
it->save(esm); marker.save(esm);
esm.endRecord(ESM::REC_MARK); esm.endRecord(ESM::REC_MARK);
} }
} }

View file

@ -79,9 +79,9 @@ template <typename T>
class DefaultConverter : public Converter class DefaultConverter : public Converter
{ {
public: public:
virtual int getStage() { return 0; } int getStage() override { return 0; }
virtual void read(ESM::ESMReader& esm) void read(ESM::ESMReader& esm) override
{ {
T record; T record;
bool isDeleted = false; bool isDeleted = false;
@ -90,7 +90,7 @@ public:
mRecords[record.mId] = record; mRecords[record.mId] = record;
} }
virtual void write(ESM::ESMWriter& esm) void write(ESM::ESMWriter& esm) override
{ {
for (typename std::map<std::string, T>::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it) for (typename std::map<std::string, T>::const_iterator it = mRecords.begin(); it != mRecords.end(); ++it)
{ {
@ -107,7 +107,7 @@ protected:
class ConvertNPC : public Converter class ConvertNPC : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader &esm) void read(ESM::ESMReader &esm) override
{ {
ESM::NPC npc; ESM::NPC npc;
bool isDeleted = false; bool isDeleted = false;
@ -127,8 +127,8 @@ public:
ESM::SpellState::SpellParams empty; ESM::SpellState::SpellParams empty;
// FIXME: player start spells and birthsign spells aren't listed here, // FIXME: player start spells and birthsign spells aren't listed here,
// need to fix openmw to account for this // need to fix openmw to account for this
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) for (const auto & spell : npc.mSpells.mList)
mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[spell] = empty;
// Clear the list now that we've written it, this prevents issues cropping up with // Clear the list now that we've written it, this prevents issues cropping up with
// ensureCustomData() in OpenMW tripping over no longer existing spells, where an error would be fatal. // ensureCustomData() in OpenMW tripping over no longer existing spells, where an error would be fatal.
@ -144,7 +144,7 @@ public:
class ConvertCREA : public Converter class ConvertCREA : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader &esm) void read(ESM::ESMReader &esm) override
{ {
// See comment in ConvertNPC // See comment in ConvertNPC
ESM::Creature creature; ESM::Creature creature;
@ -162,7 +162,7 @@ public:
class ConvertGlobal : public DefaultConverter<ESM::Global> class ConvertGlobal : public DefaultConverter<ESM::Global>
{ {
public: public:
virtual void read(ESM::ESMReader &esm) void read(ESM::ESMReader &esm) override
{ {
ESM::Global global; ESM::Global global;
bool isDeleted = false; bool isDeleted = false;
@ -183,7 +183,7 @@ public:
class ConvertClass : public DefaultConverter<ESM::Class> class ConvertClass : public DefaultConverter<ESM::Class>
{ {
public: public:
virtual void read(ESM::ESMReader &esm) void read(ESM::ESMReader &esm) override
{ {
ESM::Class class_; ESM::Class class_;
bool isDeleted = false; bool isDeleted = false;
@ -199,7 +199,7 @@ public:
class ConvertBook : public DefaultConverter<ESM::Book> class ConvertBook : public DefaultConverter<ESM::Book>
{ {
public: public:
virtual void read(ESM::ESMReader &esm) void read(ESM::ESMReader &esm) override
{ {
ESM::Book book; ESM::Book book;
bool isDeleted = false; bool isDeleted = false;
@ -215,7 +215,7 @@ public:
class ConvertNPCC : public Converter class ConvertNPCC : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader &esm) void read(ESM::ESMReader &esm) override
{ {
std::string id = esm.getHNString("NAME"); std::string id = esm.getHNString("NAME");
NPCC npcc; NPCC npcc;
@ -235,7 +235,7 @@ public:
class ConvertREFR : public Converter class ConvertREFR : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader &esm) void read(ESM::ESMReader &esm) override
{ {
REFR refr; REFR refr;
refr.load(esm); refr.load(esm);
@ -261,7 +261,7 @@ public:
} }
} }
} }
virtual void write(ESM::ESMWriter& esm) void write(ESM::ESMWriter& esm) override
{ {
esm.startRecord(ESM::REC_ASPL); esm.startRecord(ESM::REC_ASPL);
esm.writeHNString("ID__", mSelectedSpell); esm.writeHNString("ID__", mSelectedSpell);
@ -280,14 +280,14 @@ public:
mLevitationEnabled(true) mLevitationEnabled(true)
{} {}
virtual void read(ESM::ESMReader &esm) void read(ESM::ESMReader &esm) override
{ {
PCDT pcdt; PCDT pcdt;
pcdt.load(esm); pcdt.load(esm);
convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam, mTeleportingEnabled, mLevitationEnabled, mContext->mControlsState); convertPCDT(pcdt, mContext->mPlayer, mContext->mDialogueState.mKnownTopics, mFirstPersonCam, mTeleportingEnabled, mLevitationEnabled, mContext->mControlsState);
} }
virtual void write(ESM::ESMWriter &esm) void write(ESM::ESMWriter &esm) override
{ {
esm.startRecord(ESM::REC_ENAB); esm.startRecord(ESM::REC_ENAB);
esm.writeHNT("TELE", mTeleportingEnabled); esm.writeHNT("TELE", mTeleportingEnabled);
@ -306,7 +306,7 @@ private:
class ConvertCNTC : public Converter class ConvertCNTC : public Converter
{ {
virtual void read(ESM::ESMReader &esm) void read(ESM::ESMReader &esm) override
{ {
std::string id = esm.getHNString("NAME"); std::string id = esm.getHNString("NAME");
CNTC cntc; CNTC cntc;
@ -318,7 +318,7 @@ class ConvertCNTC : public Converter
class ConvertCREC : public Converter class ConvertCREC : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader &esm) void read(ESM::ESMReader &esm) override
{ {
std::string id = esm.getHNString("NAME"); std::string id = esm.getHNString("NAME");
CREC crec; CREC crec;
@ -330,8 +330,8 @@ public:
class ConvertFMAP : public Converter class ConvertFMAP : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader &esm); void read(ESM::ESMReader &esm) override;
virtual void write(ESM::ESMWriter &esm); void write(ESM::ESMWriter &esm) override;
private: private:
osg::ref_ptr<osg::Image> mGlobalMapImage; osg::ref_ptr<osg::Image> mGlobalMapImage;
@ -340,8 +340,8 @@ private:
class ConvertCell : public Converter class ConvertCell : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader& esm); void read(ESM::ESMReader& esm) override;
virtual void write(ESM::ESMWriter& esm); void write(ESM::ESMWriter& esm) override;
private: private:
struct Cell struct Cell
@ -362,7 +362,7 @@ private:
class ConvertKLST : public Converter class ConvertKLST : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader& esm) void read(ESM::ESMReader& esm) override
{ {
KLST klst; KLST klst;
klst.load(esm); klst.load(esm);
@ -371,7 +371,7 @@ public:
mContext->mPlayer.mObject.mNpcStats.mWerewolfKills = klst.mWerewolfKills; mContext->mPlayer.mObject.mNpcStats.mWerewolfKills = klst.mWerewolfKills;
} }
virtual void write(ESM::ESMWriter &esm) void write(ESM::ESMWriter &esm) override
{ {
esm.startRecord(ESM::REC_DCOU); esm.startRecord(ESM::REC_DCOU);
for (std::map<std::string, int>::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it) for (std::map<std::string, int>::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it)
@ -389,7 +389,7 @@ private:
class ConvertFACT : public Converter class ConvertFACT : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader& esm) void read(ESM::ESMReader& esm) override
{ {
ESM::Faction faction; ESM::Faction faction;
bool isDeleted = false; bool isDeleted = false;
@ -409,7 +409,7 @@ public:
class ConvertSTLN : public Converter class ConvertSTLN : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader &esm) void read(ESM::ESMReader &esm) override
{ {
std::string itemid = esm.getHNString("NAME"); std::string itemid = esm.getHNString("NAME");
Misc::StringUtils::lowerCaseInPlace(itemid); Misc::StringUtils::lowerCaseInPlace(itemid);
@ -428,15 +428,15 @@ public:
} }
} }
} }
virtual void write(ESM::ESMWriter &esm) void write(ESM::ESMWriter &esm) override
{ {
ESM::StolenItems items; ESM::StolenItems items;
for (std::map<std::string, std::set<Owner> >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it) for (std::map<std::string, std::set<Owner> >::const_iterator it = mStolenItems.begin(); it != mStolenItems.end(); ++it)
{ {
std::map<std::pair<std::string, bool>, int> owners; std::map<std::pair<std::string, bool>, int> owners;
for (std::set<Owner>::const_iterator ownerIt = it->second.begin(); ownerIt != it->second.end(); ++ownerIt) for (const auto & ownerIt : it->second)
{ {
owners.insert(std::make_pair(std::make_pair(ownerIt->first, ownerIt->second) owners.insert(std::make_pair(std::make_pair(ownerIt.first, ownerIt.second)
// Since OpenMW doesn't suffer from the owner contamination bug, // Since OpenMW doesn't suffer from the owner contamination bug,
// it needs a count argument. But for legacy savegames, we don't know // it needs a count argument. But for legacy savegames, we don't know
// this count, so must assume all items of that ID are stolen, // this count, so must assume all items of that ID are stolen,
@ -467,7 +467,7 @@ private:
class ConvertINFO : public Converter class ConvertINFO : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader& esm) void read(ESM::ESMReader& esm) override
{ {
INFO info; INFO info;
info.load(esm); info.load(esm);
@ -477,7 +477,7 @@ public:
class ConvertDIAL : public Converter class ConvertDIAL : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader& esm) void read(ESM::ESMReader& esm) override
{ {
std::string id = esm.getHNString("NAME"); std::string id = esm.getHNString("NAME");
DIAL dial; DIAL dial;
@ -485,7 +485,7 @@ public:
if (dial.mIndex > 0) if (dial.mIndex > 0)
mDials[id] = dial; mDials[id] = dial;
} }
virtual void write(ESM::ESMWriter &esm) void write(ESM::ESMWriter &esm) override
{ {
for (std::map<std::string, DIAL>::const_iterator it = mDials.begin(); it != mDials.end(); ++it) for (std::map<std::string, DIAL>::const_iterator it = mDials.begin(); it != mDials.end(); ++it)
{ {
@ -505,7 +505,7 @@ private:
class ConvertQUES : public Converter class ConvertQUES : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader& esm) void read(ESM::ESMReader& esm) override
{ {
std::string id = esm.getHNString("NAME"); std::string id = esm.getHNString("NAME");
QUES quest; QUES quest;
@ -516,7 +516,7 @@ public:
class ConvertJOUR : public Converter class ConvertJOUR : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader& esm) void read(ESM::ESMReader& esm) override
{ {
JOUR journal; JOUR journal;
journal.load(esm); journal.load(esm);
@ -531,7 +531,7 @@ public:
{ {
} }
virtual void read(ESM::ESMReader &esm) void read(ESM::ESMReader &esm) override
{ {
mGame.load(esm); mGame.load(esm);
mHasGame = true; mHasGame = true;
@ -551,7 +551,7 @@ public:
} }
} }
virtual void write(ESM::ESMWriter &esm) void write(ESM::ESMWriter &esm) override
{ {
if (!mHasGame) if (!mHasGame)
return; return;
@ -578,7 +578,7 @@ private:
class ConvertSCPT : public Converter class ConvertSCPT : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader &esm) void read(ESM::ESMReader &esm) override
{ {
SCPT script; SCPT script;
script.load(esm); script.load(esm);
@ -586,12 +586,12 @@ public:
convertSCPT(script, out); convertSCPT(script, out);
mScripts.push_back(out); mScripts.push_back(out);
} }
virtual void write(ESM::ESMWriter &esm) void write(ESM::ESMWriter &esm) override
{ {
for (std::vector<ESM::GlobalScript>::const_iterator it = mScripts.begin(); it != mScripts.end(); ++it) for (const auto & script : mScripts)
{ {
esm.startRecord(ESM::REC_GSCR); esm.startRecord(ESM::REC_GSCR);
it->save(esm); script.save(esm);
esm.endRecord(ESM::REC_GSCR); esm.endRecord(ESM::REC_GSCR);
} }
} }
@ -603,9 +603,9 @@ private:
class ConvertPROJ : public Converter class ConvertPROJ : public Converter
{ {
public: public:
virtual int getStage() override { return 2; } int getStage() override { return 2; }
virtual void read(ESM::ESMReader& esm) override; void read(ESM::ESMReader& esm) override;
virtual void write(ESM::ESMWriter& esm) override; void write(ESM::ESMWriter& esm) override;
private: private:
void convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam); void convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam);
PROJ mProj; PROJ mProj;
@ -614,8 +614,8 @@ private:
class ConvertSPLM : public Converter class ConvertSPLM : public Converter
{ {
public: public:
virtual void read(ESM::ESMReader& esm) override; void read(ESM::ESMReader& esm) override;
virtual void write(ESM::ESMWriter& esm) override; void write(ESM::ESMWriter& esm) override;
private: private:
SPLM mSPLM; SPLM mSPLM;
}; };

View file

@ -9,21 +9,20 @@ namespace ESSImport
void convertInventory(const Inventory &inventory, ESM::InventoryState &state) void convertInventory(const Inventory &inventory, ESM::InventoryState &state)
{ {
int index = 0; int index = 0;
for (std::vector<Inventory::InventoryItem>::const_iterator it = inventory.mItems.begin(); for (const auto & item : inventory.mItems)
it != inventory.mItems.end(); ++it)
{ {
ESM::ObjectState objstate; ESM::ObjectState objstate;
objstate.blank(); objstate.blank();
objstate.mRef = *it; objstate.mRef = item;
objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId); objstate.mRef.mRefID = Misc::StringUtils::lowerCase(item.mId);
objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile objstate.mCount = std::abs(item.mCount); // restocking items have negative count in the savefile
// openmw handles them differently, so no need to set any flags // openmw handles them differently, so no need to set any flags
state.mItems.push_back(objstate); state.mItems.push_back(objstate);
if (it->mRelativeEquipmentSlot != -1) if (item.mRelativeEquipmentSlot != -1)
// Note we should really write the absolute slot here, which we do not know about // Note we should really write the absolute slot here, which we do not know about
// Not a big deal, OpenMW will auto-correct to a valid slot, the only problem is when // Not a big deal, OpenMW will auto-correct to a valid slot, the only problem is when
// an item could be equipped in two different slots (e.g. equipped two rings) // an item could be equipped in two different slots (e.g. equipped two rings)
state.mEquipmentSlots[index] = it->mRelativeEquipmentSlot; state.mEquipmentSlots[index] = item.mRelativeEquipmentSlot;
++index; ++index;
} }
} }

View file

@ -10,13 +10,13 @@ namespace ESSImport
{ {
out.mBirthsign = pcdt.mBirthsign; out.mBirthsign = pcdt.mBirthsign;
out.mObject.mNpcStats.mBounty = pcdt.mBounty; out.mObject.mNpcStats.mBounty = pcdt.mBounty;
for (std::vector<PCDT::FNAM>::const_iterator it = pcdt.mFactions.begin(); it != pcdt.mFactions.end(); ++it) for (const auto & essFaction : pcdt.mFactions)
{ {
ESM::NpcStats::Faction faction; ESM::NpcStats::Faction faction;
faction.mExpelled = (it->mFlags & 0x2) != 0; faction.mExpelled = (essFaction.mFlags & 0x2) != 0;
faction.mRank = it->mRank; faction.mRank = essFaction.mRank;
faction.mReputation = it->mReputation; faction.mReputation = essFaction.mReputation;
out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction; out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(essFaction.mFactionName.toString())] = faction;
} }
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
out.mObject.mNpcStats.mSpecIncreases[i] = pcdt.mPNAM.mSpecIncreases[i]; out.mObject.mNpcStats.mSpecIncreases[i] = pcdt.mPNAM.mSpecIncreases[i];
@ -35,10 +35,9 @@ namespace ESSImport
teleportingEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_TeleportingDisabled); teleportingEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_TeleportingDisabled);
levitationEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LevitationDisabled); levitationEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LevitationDisabled);
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin(); for (const auto & knownDialogueTopic : pcdt.mKnownDialogueTopics)
it != pcdt.mKnownDialogueTopics.end(); ++it)
{ {
outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it)); outDialogueTopics.push_back(Misc::StringUtils::lowerCase(knownDialogueTopic));
} }
controls.mViewSwitchDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ViewSwitchDisabled; controls.mViewSwitchDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ViewSwitchDisabled;

View file

@ -1,18 +1,16 @@
#include "convertscri.hpp" #include "convertscri.hpp"
#include <iostream>
namespace namespace
{ {
template <typename T, ESM::VarType VariantType> template <typename T, ESM::VarType VariantType>
void storeVariables(const std::vector<T>& variables, ESM::Locals& locals, const std::string& scriptname) void storeVariables(const std::vector<T>& variables, ESM::Locals& locals, const std::string& scriptname)
{ {
for (typename std::vector<T>::const_iterator it = variables.begin(); it != variables.end(); ++it) for (const auto& variable : variables)
{ {
ESM::Variant val(*it); ESM::Variant val(variable);
val.setType(VariantType); val.setType(VariantType);
locals.mVariables.push_back(std::make_pair(std::string(), val)); locals.mVariables.emplace_back(std::string(), val);
} }
} }

View file

@ -86,7 +86,9 @@ namespace ESSImport
bool mHasANIS; bool mHasANIS;
ANIS mANIS; // scripted animation state ANIS mANIS; // scripted animation state
void load(ESM::ESMReader& esm); virtual void load(ESM::ESMReader& esm);
virtual ~ActorData() = default;
}; };
} }

View file

@ -25,7 +25,9 @@ namespace ESSImport
bool mDeleted; bool mDeleted;
void load(ESM::ESMReader& esm); void load(ESM::ESMReader& esm) override;
virtual ~CellRef() = default;
}; };
} }

View file

@ -16,15 +16,12 @@
#include <components/esm/player.hpp> #include <components/esm/player.hpp>
#include <components/esm/loadalch.hpp> #include <components/esm/loadalch.hpp>
#include <components/esm/loadclas.hpp>
#include <components/esm/loadspel.hpp> #include <components/esm/loadspel.hpp>
#include <components/esm/loadarmo.hpp> #include <components/esm/loadarmo.hpp>
#include <components/esm/loadweap.hpp> #include <components/esm/loadweap.hpp>
#include <components/esm/loadclot.hpp> #include <components/esm/loadclot.hpp>
#include <components/esm/loadench.hpp> #include <components/esm/loadench.hpp>
#include <components/esm/loadweap.hpp>
#include <components/esm/loadlevlist.hpp> #include <components/esm/loadlevlist.hpp>
#include <components/esm/loadglob.hpp>
#include <components/misc/constants.hpp> #include <components/misc/constants.hpp>
@ -49,7 +46,7 @@ namespace
image->allocateImage(128, 128, 1, GL_RGB, GL_UNSIGNED_BYTE); image->allocateImage(128, 128, 1, GL_RGB, GL_UNSIGNED_BYTE);
// need to convert pixel format from BGRA to RGB as the jpg readerwriter doesn't support it otherwise // need to convert pixel format from BGRA to RGB as the jpg readerwriter doesn't support it otherwise
std::vector<unsigned char>::const_iterator it = fileHeader.mSCRS.begin(); auto it = fileHeader.mSCRS.begin();
for (int y=0; y<128; ++y) for (int y=0; y<128; ++y)
{ {
for (int x=0; x<128; ++x) for (int x=0; x<128; ++x)
@ -317,10 +314,9 @@ namespace ESSImport
std::set<unsigned int> unknownRecords; std::set<unsigned int> unknownRecords;
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin(); for (const auto & converter : converters)
it != converters.end(); ++it)
{ {
it->second->setContext(context); converter.second->setContext(context);
} }
while (esm.hasMoreRecs()) while (esm.hasMoreRecs())
@ -328,7 +324,7 @@ namespace ESSImport
ESM::NAME n = esm.getRecName(); ESM::NAME n = esm.getRecName();
esm.getRecHeader(); esm.getRecHeader();
std::map<unsigned int, std::shared_ptr<Converter> >::iterator it = converters.find(n.intval); auto it = converters.find(n.intval);
if (it != converters.end()) if (it != converters.end())
{ {
it->second->read(esm); it->second->read(esm);
@ -358,17 +354,15 @@ namespace ESSImport
writer.setDescription(""); writer.setDescription("");
writer.setRecordCount (0); writer.setRecordCount (0);
for (std::vector<ESM::Header::MasterData>::const_iterator it = header.mMaster.begin(); for (const auto & master : header.mMaster)
it != header.mMaster.end(); ++it) writer.addMaster(master.name, 0); // not using the size information anyway -> use value of 0
writer.addMaster (it->name, 0); // not using the size information anyway -> use value of 0
writer.save (stream); writer.save (stream);
ESM::SavedGame profile; ESM::SavedGame profile;
for (std::vector<ESM::Header::MasterData>::const_iterator it = header.mMaster.begin(); for (const auto & master : header.mMaster)
it != header.mMaster.end(); ++it)
{ {
profile.mContentFiles.push_back(it->name); profile.mContentFiles.push_back(master.name);
} }
profile.mDescription = esm.getDesc(); profile.mDescription = esm.getDesc();
profile.mInGameTime.mDay = context.mDay; profile.mInGameTime.mDay = context.mDay;

View file

@ -63,7 +63,6 @@ namespace ESSImport
, mHour(0.f) , mHour(0.f)
, mNextActorId(0) , mNextActorId(0)
{ {
mPlayer.mAutoMove = 0;
ESM::CellId playerCellId; ESM::CellId playerCellId;
playerCellId.mPaged = true; playerCellId.mPaged = true;
playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0; playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;

View file

@ -4,8 +4,6 @@
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include <components/esm/loadcont.hpp>
namespace ESSImport namespace ESSImport
{ {

View file

@ -15,17 +15,13 @@ set(GAME_HEADER
engine.hpp engine.hpp
) )
if (BULLET_USE_DOUBLES)
add_definitions(-DBT_USE_DOUBLE_PRECISION)
endif()
source_group(game FILES ${GAME} ${GAME_HEADER}) source_group(game FILES ${GAME} ${GAME_HEADER})
add_openmw_dir (mwrender add_openmw_dir (mwrender
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation bulletdebugdraw globalmap characterpreview camera localmap water terrainstorage ripplesimulation
renderbin actoranimation landmanager navmesh actorspaths recastmesh renderbin actoranimation landmanager navmesh actorspaths recastmesh fogmanager
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput
@ -71,7 +67,7 @@ add_openmw_dir (mwworld
actionequip timestamp actionalchemy cellstore actionapply actioneat actionequip timestamp actionalchemy cellstore actionapply actioneat
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager
cellpreloader cellpreloader datetimemanager
) )
add_openmw_dir (mwphysics add_openmw_dir (mwphysics

View file

@ -311,6 +311,8 @@ namespace MWBase
virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const = 0; virtual float getAngleToPlayer(const MWWorld::Ptr& ptr) const = 0;
virtual MWMechanics::GreetingState getGreetingState(const MWWorld::Ptr& ptr) const = 0; virtual MWMechanics::GreetingState getGreetingState(const MWWorld::Ptr& ptr) const = 0;
virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const = 0; virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const = 0;
virtual void restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId) = 0;
}; };
} }

View file

@ -36,6 +36,7 @@ namespace ESM
struct Position; struct Position;
struct Cell; struct Cell;
struct Class; struct Class;
struct Creature;
struct Potion; struct Potion;
struct Spell; struct Spell;
struct NPC; struct NPC;
@ -47,7 +48,7 @@ namespace ESM
struct EffectList; struct EffectList;
struct CreatureLevList; struct CreatureLevList;
struct ItemLevList; struct ItemLevList;
struct Creature; struct TimeStamp;
} }
namespace MWRender namespace MWRender
@ -234,54 +235,14 @@ namespace MWBase
virtual void advanceTime (double hours, bool incremental = false) = 0; virtual void advanceTime (double hours, bool incremental = false) = 0;
///< Advance in-game time. ///< Advance in-game time.
virtual void setHour (double hour) = 0;
///< Set in-game time hour.
virtual void setMonth (int month) = 0;
///< Set in-game time month.
virtual void setDay (int day) = 0;
///< Set in-game time day.
/*
Start of tes3mp addition
Make it possible to set the year from elsewhere
*/
virtual void setYear(int year) = 0;
/*
End of tes3mp addition
*/
/*
Start of tes3mp addition
Make it possible to set the number of days passed from elsewhere
*/
virtual void setDaysPassed(int daysPassed) = 0;
/*
End of tes3mp addition
*/
/*
Start of tes3mp addition
Make it possible to set a custom timeScale from elsewhere
*/
virtual void setTimeScale(float timeScale) = 0;
/*
End of tes3mp addition
*/
virtual int getDay() const = 0;
virtual int getMonth() const = 0;
virtual int getYear() const = 0;
virtual std::string getMonthName (int month = -1) const = 0; virtual std::string getMonthName (int month = -1) const = 0;
///< Return name of month (-1: current month) ///< Return name of month (-1: current month)
virtual MWWorld::TimeStamp getTimeStamp() const = 0; virtual MWWorld::TimeStamp getTimeStamp() const = 0;
///< Return current in-game time stamp. ///< Return current in-game time and number of day since new game start.
virtual ESM::EpochTimeStamp getEpochTimeStamp() const = 0;
///< Return current in-game date and time.
virtual bool toggleSky() = 0; virtual bool toggleSky() = 0;
///< \return Resulting mode ///< \return Resulting mode
@ -530,6 +491,14 @@ namespace MWBase
///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID. ///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.
/// \return pointer to created record /// \return pointer to created record
virtual const ESM::Creature *createOverrideRecord (const ESM::Creature& record) = 0;
///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.
/// \return pointer to created record
virtual const ESM::NPC *createOverrideRecord (const ESM::NPC& record) = 0;
///< Write this record to the ESM store, allowing it to override a pre-existing record with the same ID.
/// \return pointer to created record
virtual void update (float duration, bool paused) = 0; virtual void update (float duration, bool paused) = 0;
virtual void updatePhysics (float duration, bool paused) = 0; virtual void updatePhysics (float duration, bool paused) = 0;

View file

@ -322,7 +322,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>(); const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();
int armorSkillType = getEquipmentSkill(ptr); int armorSkillType = getEquipmentSkill(ptr);
int armorSkill = actor.getClass().getSkill(actor, armorSkillType); float armorSkill = actor.getClass().getSkill(actor, armorSkillType);
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
int iBaseArmorSkill = world->getStore().get<ESM::GameSetting>().find("iBaseArmorSkill")->mValue.getInteger(); int iBaseArmorSkill = world->getStore().get<ESM::GameSetting>().find("iBaseArmorSkill")->mValue.getInteger();

View file

@ -793,7 +793,7 @@ namespace MWClass
float Creature::getCapacity (const MWWorld::Ptr& ptr) const float Creature::getCapacity (const MWWorld::Ptr& ptr) const
{ {
const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); const MWMechanics::CreatureStats& stats = getCreatureStats (ptr);
return static_cast<float>(stats.getAttribute(ESM::Attribute::Strength).getModified() * 5); return stats.getAttribute(ESM::Attribute::Strength).getModified() * 5;
} }
int Creature::getServices(const MWWorld::ConstPtr &actor) const int Creature::getServices(const MWWorld::ConstPtr &actor) const
@ -933,7 +933,7 @@ namespace MWClass
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
} }
int Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const float Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const
{ {
MWWorld::LiveCellRef<ESM::Creature> *ref = MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>(); ptr.get<ESM::Creature>();
@ -997,6 +997,12 @@ namespace MWClass
return; return;
} }
if (ptr.getRefData().getCount() <= 0)
{
state.mHasCustomState = false;
return;
}
const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData(); const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();
ESM::CreatureState& creatureState = state.asCreatureState(); ESM::CreatureState& creatureState = state.asCreatureState();
customData.mContainerStore->writeState (creatureState.mInventory); customData.mContainerStore->writeState (creatureState.mInventory);
@ -1066,4 +1072,9 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>(); const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
scale *= ref->mBase->mScale; scale *= ref->mBase->mScale;
} }
void Creature::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const
{
MWMechanics::setBaseAISetting<ESM::Creature>(id, setting, value);
}
} }

View file

@ -118,7 +118,7 @@ namespace MWClass
virtual bool canSwim (const MWWorld::ConstPtr &ptr) const; virtual bool canSwim (const MWWorld::ConstPtr &ptr) const;
virtual bool canWalk (const MWWorld::ConstPtr &ptr) const; virtual bool canWalk (const MWWorld::ConstPtr &ptr) const;
virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; virtual float getSkill(const MWWorld::Ptr &ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const;
@ -139,6 +139,8 @@ namespace MWClass
virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const; virtual void adjustScale(const MWWorld::ConstPtr& ptr, osg::Vec3f& scale, bool rendering) const;
/// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
}; };
} }

View file

@ -158,7 +158,7 @@ namespace MWClass
} }
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
int alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); float alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy);
static const float fWortChanceValue = static const float fWortChanceValue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat();

View file

@ -144,8 +144,8 @@ namespace
} }
// initial health // initial health
int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); float strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); float endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();
int multiplier = 3; int multiplier = 3;
@ -1234,7 +1234,7 @@ namespace MWClass
gmst.fJumpEncumbranceMultiplier->mValue.getFloat() * gmst.fJumpEncumbranceMultiplier->mValue.getFloat() *
(1.0f - Npc::getNormalizedEncumbrance(ptr)); (1.0f - Npc::getNormalizedEncumbrance(ptr));
float a = static_cast<float>(getSkill(ptr, ESM::Skill::Acrobatics)); float a = getSkill(ptr, ESM::Skill::Acrobatics);
float b = 0.0f; float b = 0.0f;
if(a > 50.0f) if(a > 50.0f)
{ {
@ -1359,7 +1359,7 @@ namespace MWClass
float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat();
int unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored); float unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored);
float ratings[MWWorld::InventoryStore::Slots]; float ratings[MWWorld::InventoryStore::Slots];
for(int i = 0;i < MWWorld::InventoryStore::Slots;i++) for(int i = 0;i < MWWorld::InventoryStore::Slots;i++)
@ -1506,7 +1506,7 @@ namespace MWClass
return MWWorld::Ptr(cell.insert(ref), &cell); return MWWorld::Ptr(cell.insert(ref), &cell);
} }
int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const float Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const
{ {
return getNpcStats(ptr).getSkill(skill).getModified(); return getNpcStats(ptr).getSkill(skill).getModified();
} }
@ -1550,6 +1550,12 @@ namespace MWClass
return; return;
} }
if (ptr.getRefData().getCount() <= 0)
{
state.mHasCustomState = false;
return;
}
const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData(); const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData();
ESM::NpcState& npcState = state.asNpcState(); ESM::NpcState& npcState = state.asNpcState();
customData.mInventoryStore.writeState (npcState.mInventory); customData.mInventoryStore.writeState (npcState.mInventory);
@ -1660,4 +1666,9 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
return ref->mBase->getFactionRank(); return ref->mBase->getFactionRank();
} }
void Npc::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const
{
MWMechanics::setBaseAISetting<ESM::NPC>(id, setting, value);
}
} }

View file

@ -139,7 +139,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; virtual std::string getModel(const MWWorld::ConstPtr &ptr) const;
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const;
@ -174,6 +174,8 @@ namespace MWClass
virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const; virtual std::string getPrimaryFaction(const MWWorld::ConstPtr &ptr) const;
virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const; virtual int getPrimaryFactionRank(const MWWorld::ConstPtr &ptr) const;
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
}; };
} }

View file

@ -134,6 +134,12 @@ namespace MWGui
End of tes3mp change (major) End of tes3mp change (major)
*/ */
// We should not worsen corprus when in prison
for (auto& spell : player.getClass().getCreatureStats(player).getCorprusSpells())
{
spell.second.mNextWorsening += mDays * 24;
}
std::set<int> skills; std::set<int> skills;
for (int day=0; day<mDays; ++day) for (int day=0; day<mDays; ++day)
{ {
@ -148,14 +154,14 @@ namespace MWGui
Disable increases for Security and Sneak when using ignoreJailSkillIncreases Disable increases for Security and Sneak when using ignoreJailSkillIncreases
*/ */
if (localPlayer->ignoreJailSkillIncreases) if (localPlayer->ignoreJailSkillIncreases)
value.setBase(std::max(0, value.getBase()-1)); value.setBase(std::max(0.f, value.getBase()-1));
else if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak) else if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak)
/* /*
End of tes3mp change (minor) End of tes3mp change (minor)
*/ */
value.setBase(std::min(100, value.getBase()+1)); value.setBase(std::min(100.f, value.getBase() + 1));
else else
value.setBase(std::max(0, value.getBase()-1)); value.setBase(std::max(0.f, value.getBase()-1));
} }
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();

View file

@ -157,7 +157,7 @@ namespace MWGui
mAttributeValues[i]->setEnabled(true); mAttributeValues[i]->setEnabled(true);
availableAttributes++; availableAttributes++;
int mult = pcStats.getLevelupAttributeMultiplier (i); float mult = pcStats.getLevelupAttributeMultiplier (i);
mult = std::min(mult, 100-pcStats.getAttribute(i).getBase()); mult = std::min(mult, 100-pcStats.getAttribute(i).getBase());
text->setCaption(mult <= 1 ? "" : "x" + MyGUI::utility::toString(mult)); text->setCaption(mult <= 1 ? "" : "x" + MyGUI::utility::toString(mult));
} }

View file

@ -22,7 +22,7 @@ namespace MWGui
{ {
MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::Ptr player = MWMechanics::getPlayer();
mSourceModel = sourceModel; mSourceModel = sourceModel;
int chance = player.getClass().getSkill(player, ESM::Skill::Sneak); float chance = player.getClass().getSkill(player, ESM::Skill::Sneak);
mSourceModel->update(); mSourceModel->update();

View file

@ -159,7 +159,7 @@ namespace MWGui
for (int i=0; ids[i]; ++i) for (int i=0; ids[i]; ++i)
if (ids[i]==id) if (ids[i]==id)
{ {
setText (id, std::to_string(value.getModified())); setText (id, std::to_string(static_cast<int>(value.getModified())));
MyGUI::TextBox* box; MyGUI::TextBox* box;
getWidget(box, id); getWidget(box, id);

View file

@ -86,11 +86,11 @@ namespace MWGui
mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold));
// NPC can train you in his best 3 skills // NPC can train you in his best 3 skills
std::vector< std::pair<int, int> > skills; std::vector< std::pair<int, float> > skills;
for (int i=0; i<ESM::Skill::Length; ++i) for (int i=0; i<ESM::Skill::Length; ++i)
{ {
int value = actor.getClass().getSkill(actor, i); float value = actor.getClass().getSkill(actor, i);
skills.push_back(std::make_pair(i, value)); skills.push_back(std::make_pair(i, value));
} }

View file

@ -180,11 +180,10 @@ namespace MWGui
if (hour >= 13) hour -= 12; if (hour >= 13) hour -= 12;
if (hour == 0) hour = 12; if (hour == 0) hour = 12;
std::string dateTimeText = ESM::EpochTimeStamp currentDate = MWBase::Environment::get().getWorld()->getEpochTimeStamp();
MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getDay ()) + " " int daysPassed = MWBase::Environment::get().getWorld()->getTimeStamp().getDay();
+ month + " (#{sDay} " + MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay()) std::string formattedHour = pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}";
+ ") " + MyGUI::utility::toString(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}"); std::string dateTimeText = Misc::StringUtils::format("%i %s (#{sDay} %i) %i %s", currentDate.mDay, month, daysPassed, hour, formattedHour);
mDateTimeText->setCaptionWithReplacing (dateTimeText); mDateTimeText->setCaptionWithReplacing (dateTimeText);
} }

View file

@ -29,13 +29,23 @@ namespace MWMechanics
} }
else else
{ {
bool interrupt = false;
std::vector<ActiveEffect>& effects = iter->second.mEffects; std::vector<ActiveEffect>& effects = iter->second.mEffects;
for (std::vector<ActiveEffect>::iterator effectIt = effects.begin(); effectIt != effects.end();) for (std::vector<ActiveEffect>::iterator effectIt = effects.begin(); effectIt != effects.end();)
{ {
if (effectIt->mTimeLeft <= 0) if (effectIt->mTimeLeft <= 0)
{ {
effectIt = effects.erase(effectIt);
rebuild = true; rebuild = true;
// Note: it we expire a Corprus effect, we should remove the whole spell.
if (effectIt->mEffectId == ESM::MagicEffect::Corprus)
{
iter = mSpells.erase (iter);
interrupt = true;
break;
}
effectIt = effects.erase(effectIt);
} }
else else
{ {
@ -43,6 +53,8 @@ namespace MWMechanics
++effectIt; ++effectIt;
} }
} }
if (!interrupt)
++iter; ++iter;
} }
} }
@ -327,6 +339,31 @@ namespace MWMechanics
End of tes3mp addition End of tes3mp addition
*/ */
void ActiveSpells::purgeCorprusDisease()
{
for (TContainer::iterator iter = mSpells.begin(); iter!=mSpells.end();)
{
bool hasCorprusEffect = false;
for (std::vector<ActiveEffect>::iterator effectIt = iter->second.mEffects.begin();
effectIt != iter->second.mEffects.end();++effectIt)
{
if (effectIt->mEffectId == ESM::MagicEffect::Corprus)
{
hasCorprusEffect = true;
break;
}
}
if (hasCorprusEffect)
{
mSpells.erase(iter++);
mSpellsChanged = true;
}
else
++iter;
}
}
void ActiveSpells::clear() void ActiveSpells::clear()
{ {
mSpells.clear(); mSpells.clear();

View file

@ -119,6 +119,8 @@ namespace MWMechanics
bool isSpellActive (const std::string& id) const; bool isSpellActive (const std::string& id) const;
///< case insensitive ///< case insensitive
void purgeCorprusDisease();
const MagicEffects& getMagicEffects() const; const MagicEffects& getMagicEffects() const;
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const; void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;

View file

@ -131,11 +131,11 @@ void adjustCommandedActor (const MWWorld::Ptr& actor)
bool hasCommandPackage = false; bool hasCommandPackage = false;
std::list<MWMechanics::AiPackage*>::const_iterator it; auto it = stats.getAiSequence().begin();
for (it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it) for (; it != stats.getAiSequence().end(); ++it)
{ {
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow && if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow &&
static_cast<MWMechanics::AiFollow*>(*it)->isCommanded()) static_cast<const MWMechanics::AiFollow*>(it->get())->isCommanded())
{ {
hasCommandPackage = true; hasCommandPackage = true;
break; break;
@ -151,7 +151,7 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr); MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
health = 0.1f * endurance; health = 0.1f * endurance;
float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat (); float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat ();
@ -195,6 +195,49 @@ namespace MWMechanics
} }
}; };
class GetCurrentMagnitudes : public MWMechanics::EffectSourceVisitor
{
std::string mSpellId;
public:
GetCurrentMagnitudes(const std::string& spellId)
: mSpellId(spellId)
{
}
virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, const std::string& sourceId, int casterActorId,
float magnitude, float remainingTime = -1, float totalTime = -1)
{
if (magnitude <= 0)
return;
if (sourceId != mSpellId)
return;
mMagnitudes.push_back(std::make_pair(key, magnitude));
}
std::vector<std::pair<MWMechanics::EffectKey, float>> mMagnitudes;
};
class GetCorprusSpells : public MWMechanics::EffectSourceVisitor
{
public:
virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, const std::string& sourceId, int casterActorId,
float magnitude, float remainingTime = -1, float totalTime = -1)
{
if (key.mId != ESM::MagicEffect::Corprus)
return;
mSpells.push_back(sourceId);
}
std::vector<std::string> mSpells;
};
class SoulTrap : public MWMechanics::EffectSourceVisitor class SoulTrap : public MWMechanics::EffectSourceVisitor
{ {
MWWorld::Ptr mCreature; MWWorld::Ptr mCreature;
@ -468,9 +511,9 @@ namespace MWMechanics
CreatureStats &stats = actor.getClass().getCreatureStats(actor); CreatureStats &stats = actor.getClass().getCreatureStats(actor);
MWMechanics::AiSequence& seq = stats.getAiSequence(); MWMechanics::AiSequence& seq = stats.getAiSequence();
if (!seq.isEmpty() && seq.getActivePackage()->useVariableSpeed()) if (!seq.isEmpty() && seq.getActivePackage().useVariableSpeed())
{ {
osg::Vec3f targetPos = seq.getActivePackage()->getDestination(); osg::Vec3f targetPos = seq.getActivePackage().getDestination();
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3(); osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
float distance = (targetPos - actorPos).length(); float distance = (targetPos - actorPos).length();
if (distance < DECELERATE_DISTANCE) if (distance < DECELERATE_DISTANCE)
@ -718,7 +761,7 @@ namespace MWMechanics
return; return;
bool followerOrEscorter = false; bool followerOrEscorter = false;
for (const AiPackage* package : creatureStats2.getAiSequence()) for (const auto& package : creatureStats2.getAiSequence())
{ {
// The follow package must be first or have nothing but combat before it // The follow package must be first or have nothing but combat before it
if (package->sideWithTarget()) if (package->sideWithTarget())
@ -767,7 +810,7 @@ namespace MWMechanics
{ {
CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr); CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr);
int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified(); float intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified();
float base = 1.f; float base = 1.f;
if (ptr == getPlayer()) if (ptr == getPlayer())
@ -846,7 +889,7 @@ namespace MWMechanics
float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat (); float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat ();
int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified ();
float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr);
if (normalizedEncumbrance > 1) if (normalizedEncumbrance > 1)
@ -873,7 +916,7 @@ namespace MWMechanics
return; return;
// Restore fatigue // Restore fatigue
int endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified(); float endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified();
const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat (); static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat ();
static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
@ -987,20 +1030,74 @@ namespace MWMechanics
if (creatureStats.needToRecalcDynamicStats()) if (creatureStats.needToRecalcDynamicStats())
calculateDynamicStats(ptr); calculateDynamicStats(ptr);
{
Spells & spells = creatureStats.getSpells();
for (Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
{
if (spells.getCorprusSpells().find(it->first) != spells.getCorprusSpells().end())
{
if (MWBase::Environment::get().getWorld()->getTimeStamp() >= spells.getCorprusSpells().at(it->first).mNextWorsening)
{
spells.worsenCorprus(it->first);
if (ptr == getPlayer()) if (ptr == getPlayer())
{
GetCorprusSpells getCorprusSpellsVisitor;
creatureStats.getSpells().visitEffectSources(getCorprusSpellsVisitor);
creatureStats.getActiveSpells().visitEffectSources(getCorprusSpellsVisitor);
ptr.getClass().getInventoryStore(ptr).visitEffectSources(getCorprusSpellsVisitor);
std::vector<std::string> corprusSpells = getCorprusSpellsVisitor.mSpells;
std::vector<std::string> corprusSpellsToRemove;
for (auto it = creatureStats.getCorprusSpells().begin(); it != creatureStats.getCorprusSpells().end(); ++it)
{
if(std::find(corprusSpells.begin(), corprusSpells.end(), it->first) == corprusSpells.end())
{
// Corprus effect expired, remove entry and restore stats.
MWBase::Environment::get().getMechanicsManager()->restoreStatsAfterCorprus(ptr, it->first);
corprusSpellsToRemove.push_back(it->first);
corprusSpells.erase(std::remove(corprusSpells.begin(), corprusSpells.end(), it->first), corprusSpells.end());
continue;
}
corprusSpells.erase(std::remove(corprusSpells.begin(), corprusSpells.end(), it->first), corprusSpells.end());
if (MWBase::Environment::get().getWorld()->getTimeStamp() >= it->second.mNextWorsening)
{
it->second.mNextWorsening += CorprusStats::sWorseningPeriod;
GetCurrentMagnitudes getMagnitudesVisitor (it->first);
creatureStats.getSpells().visitEffectSources(getMagnitudesVisitor);
creatureStats.getActiveSpells().visitEffectSources(getMagnitudesVisitor);
ptr.getClass().getInventoryStore(ptr).visitEffectSources(getMagnitudesVisitor);
for (auto& effectMagnitude : getMagnitudesVisitor.mMagnitudes)
{
if (effectMagnitude.first.mId == ESM::MagicEffect::FortifyAttribute)
{
AttributeValue attr = creatureStats.getAttribute(effectMagnitude.first.mArg);
attr.damage(-effectMagnitude.second);
creatureStats.setAttribute(effectMagnitude.first.mArg, attr);
it->second.mWorsenings[effectMagnitude.first.mArg] = 0;
}
else if (effectMagnitude.first.mId == ESM::MagicEffect::DrainAttribute)
{
AttributeValue attr = creatureStats.getAttribute(effectMagnitude.first.mArg);
int currentDamage = attr.getDamage();
if (currentDamage >= 0)
it->second.mWorsenings[effectMagnitude.first.mArg] = std::min(it->second.mWorsenings[effectMagnitude.first.mArg], currentDamage);
it->second.mWorsenings[effectMagnitude.first.mArg] += effectMagnitude.second;
attr.damage(effectMagnitude.second);
creatureStats.setAttribute(effectMagnitude.first.mArg, attr);
}
}
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}");
} }
} }
for (std::string& oldCorprusSpell : corprusSpellsToRemove)
{
creatureStats.removeCorprusSpell(oldCorprusSpell);
}
for (std::string& newCorprusSpell : corprusSpells)
{
CorprusStats corprus;
for (int i=0; i<ESM::Attribute::Length; ++i)
corprus.mWorsenings[i] = 0;
corprus.mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod;
creatureStats.addCorprusSpell(newCorprusSpell, corprus);
} }
} }
@ -1786,9 +1883,14 @@ namespace MWMechanics
iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration); iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);
// For dead actors we need to remove looping spell particles // For dead actors we need to update looping spell particles
if (iter->first.getClass().getCreatureStats(iter->first).isDead()) if (iter->first.getClass().getCreatureStats(iter->first).isDead())
{
// They can be added during the death animation
if (!iter->first.getClass().getCreatureStats(iter->first).isDeathAnimationFinished())
adjustMagicEffects(iter->first);
ctrl->updateContinuousVfx(); ctrl->updateContinuousVfx();
}
else else
{ {
bool cellChanged = world->hasCellChanged(); bool cellChanged = world->hasCellChanged();
@ -1913,7 +2015,7 @@ namespace MWMechanics
if (!isPlayer && isConscious(iter->first) && !stats.isParalyzed()) if (!isPlayer && isConscious(iter->first) && !stats.isParalyzed())
{ {
MWMechanics::AiSequence& seq = stats.getAiSequence(); MWMechanics::AiSequence& seq = stats.getAiSequence();
alwaysActive = !seq.isEmpty() && seq.getActivePackage()->alwaysActive(); alwaysActive = !seq.isEmpty() && seq.getActivePackage().alwaysActive();
} }
bool inRange = isPlayer || dist <= mActorsProcessingRange || alwaysActive; bool inRange = isPlayer || dist <= mActorsProcessingRange || alwaysActive;
int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower) int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower)
@ -2179,6 +2281,8 @@ namespace MWMechanics
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter) for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{ {
iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);
if (iter->first.getClass().getCreatureStats(iter->first).isDead()) if (iter->first.getClass().getCreatureStats(iter->first).isDead())
continue; continue;
@ -2434,7 +2538,7 @@ namespace MWMechanics
// An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat and Wander packages before the Follow/Escort package // An actor counts as siding with this actor if Follow or Escort is the current AI package, or there are only Combat and Wander packages before the Follow/Escort package
// Actors that are targeted by this actor's Follow or Escort packages also side with them // Actors that are targeted by this actor's Follow or Escort packages also side with them
for (const AiPackage* package : stats.getAiSequence()) for (const auto& package : stats.getAiSequence())
{ {
if (package->sideWithTarget() && !package->getTarget().isEmpty()) if (package->sideWithTarget() && !package->getTarget().isEmpty())
{ {
@ -2470,7 +2574,7 @@ namespace MWMechanics
// An actor counts as following if AiFollow is the current AiPackage, // An actor counts as following if AiFollow is the current AiPackage,
// or there are only Combat and Wander packages before the AiFollow package // or there are only Combat and Wander packages before the AiFollow package
for (const AiPackage* package : stats.getAiSequence()) for (const auto& package : stats.getAiSequence())
{ {
if (package->followTargetThroughDoors() && package->getTarget() == actor) if (package->followTargetThroughDoors() && package->getTarget() == actor)
list.push_back(iteratedActor); list.push_back(iteratedActor);
@ -2533,11 +2637,11 @@ namespace MWMechanics
// An actor counts as following if AiFollow is the current AiPackage, // An actor counts as following if AiFollow is the current AiPackage,
// or there are only Combat and Wander packages before the AiFollow package // or there are only Combat and Wander packages before the AiFollow package
for (AiPackage* package : stats.getAiSequence()) for (const auto& package : stats.getAiSequence())
{ {
if (package->followTargetThroughDoors() && package->getTarget() == actor) if (package->followTargetThroughDoors() && package->getTarget() == actor)
{ {
list.push_back(static_cast<AiFollow*>(package)->getFollowIndex()); list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex());
break; break;
} }
else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander) else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander)

View file

@ -1,6 +1,16 @@
#ifndef OPENMW_MWMECHANICS_ACTORUTIL_H #ifndef OPENMW_MWMECHANICS_ACTORUTIL_H
#define OPENMW_MWMECHANICS_ACTORUTIL_H #define OPENMW_MWMECHANICS_ACTORUTIL_H
#include <components/esm/loadcrea.hpp>
#include <components/esm/loadnpc.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/esmstore.hpp"
#include "./creaturestats.hpp"
namespace MWWorld namespace MWWorld
{ {
class Ptr; class Ptr;
@ -18,6 +28,33 @@ namespace MWMechanics
MWWorld::Ptr getPlayer(); MWWorld::Ptr getPlayer();
bool isPlayerInCombat(); bool isPlayerInCombat();
bool canActorMoveByZAxis(const MWWorld::Ptr& actor); bool canActorMoveByZAxis(const MWWorld::Ptr& actor);
template<class T>
void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value)
{
T copy = *MWBase::Environment::get().getWorld()->getStore().get<T>().find(id);
switch(setting)
{
case MWMechanics::CreatureStats::AiSetting::AI_Hello:
copy.mAiData.mHello = value;
break;
case MWMechanics::CreatureStats::AiSetting::AI_Fight:
copy.mAiData.mFight = value;
break;
case MWMechanics::CreatureStats::AiSetting::AI_Flee:
copy.mAiData.mFlee = value;
break;
case MWMechanics::CreatureStats::AiSetting::AI_Alarm:
copy.mAiData.mAlarm = value;
break;
default:
assert(0);
}
MWBase::Environment::get().getWorld()->createOverrideRecord(copy);
}
template void setBaseAISetting<ESM::Creature>(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value);
template void setBaseAISetting<ESM::NPC>(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value);
} }
#endif #endif

View file

@ -44,11 +44,6 @@ namespace MWMechanics
End of tes3mp addition End of tes3mp addition
*/ */
AiActivate *MWMechanics::AiActivate::clone() const
{
return new AiActivate(*this);
}
bool AiActivate::execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) bool AiActivate::execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
{ {
/* /*
@ -109,11 +104,6 @@ namespace MWMechanics
return false; return false;
} }
int AiActivate::getTypeId() const
{
return TypeIdActivate;
}
void AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const void AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const
{ {
std::unique_ptr<ESM::AiSequence::AiActivate> activate(new ESM::AiSequence::AiActivate()); std::unique_ptr<ESM::AiSequence::AiActivate> activate(new ESM::AiSequence::AiActivate());

View file

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AIACTIVATE_H #ifndef GAME_MWMECHANICS_AIACTIVATE_H
#define GAME_MWMECHANICS_AIACTIVATE_H #define GAME_MWMECHANICS_AIACTIVATE_H
#include "aipackage.hpp" #include "typedaipackage.hpp"
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -29,7 +29,7 @@ namespace MWMechanics
{ {
/// \brief Causes actor to walk to activatable object and activate it /// \brief Causes actor to walk to activatable object and activate it
/** Will activate when close to object **/ /** Will activate when close to object **/
class AiActivate final : public AiPackage class AiActivate final : public TypedAiPackage<AiActivate>
{ {
public: public:
/// Constructor /// Constructor
@ -49,14 +49,14 @@ namespace MWMechanics
AiActivate(const ESM::AiSequence::AiActivate* activate); AiActivate(const ESM::AiSequence::AiActivate* activate);
AiActivate *clone() const final;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final;
static constexpr TypeId getTypeId() { return TypeIdActivate; }
void writeState(ESM::AiSequence::AiSequence& sequence) const final; void writeState(ESM::AiSequence::AiSequence& sequence) const final;
private: private:
std::string mObjectId; const std::string mObjectId;
/* /*
Start of tes3mp addition Start of tes3mp addition

View file

@ -16,7 +16,7 @@
static const int MAX_DIRECTIONS = 4; static const int MAX_DIRECTIONS = 4;
MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::ConstPtr& doorPtr) MWMechanics::AiAvoidDoor::AiAvoidDoor(const MWWorld::ConstPtr& doorPtr)
: AiPackage(), mDuration(1), mDoorPtr(doorPtr), mDirection(0) : mDuration(1), mDoorPtr(doorPtr), mDirection(0)
{ {
} }
@ -72,21 +72,6 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor, CharacterCont
return false; return false;
} }
MWMechanics::AiAvoidDoor *MWMechanics::AiAvoidDoor::clone() const
{
return new AiAvoidDoor(*this);
}
int MWMechanics::AiAvoidDoor::getTypeId() const
{
return TypeIdAvoidDoor;
}
unsigned int MWMechanics::AiAvoidDoor::getPriority() const
{
return 2;
}
bool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const bool MWMechanics::AiAvoidDoor::isStuck(const osg::Vec3f& actorPos) const
{ {
return (actorPos - mLastPos).length2() < 10 * 10; return (actorPos - mLastPos).length2() < 10 * 10;

View file

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AIAVOIDDOOR_H #ifndef GAME_MWMECHANICS_AIAVOIDDOOR_H
#define GAME_MWMECHANICS_AIAVOIDDOOR_H #define GAME_MWMECHANICS_AIAVOIDDOOR_H
#include "aipackage.hpp" #include "typedaipackage.hpp"
#include <string> #include <string>
@ -16,26 +16,28 @@ namespace MWMechanics
/// \brief AiPackage to have an actor avoid an opening door /// \brief AiPackage to have an actor avoid an opening door
/** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it /** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it
**/ **/
class AiAvoidDoor final : public AiPackage class AiAvoidDoor final : public TypedAiPackage<AiAvoidDoor>
{ {
public: public:
/// Avoid door until the door is fully open /// Avoid door until the door is fully open
AiAvoidDoor(const MWWorld::ConstPtr& doorPtr); AiAvoidDoor(const MWWorld::ConstPtr& doorPtr);
AiAvoidDoor *clone() const final;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final; static constexpr TypeId getTypeId() { return TypeIdAvoidDoor; }
unsigned int getPriority() const final; static constexpr Options makeDefaultOptions()
{
bool canCancel() const final { return false; } AiPackage::Options options;
bool shouldCancelPreviousAi() const final { return false; } options.mPriority = 2;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
private: private:
float mDuration; float mDuration;
MWWorld::ConstPtr mDoorPtr; const MWWorld::ConstPtr mDoorPtr;
osg::Vec3f mLastPos; osg::Vec3f mLastPos;
int mDirection; int mDirection;

View file

@ -11,12 +11,6 @@
#include "movement.hpp" #include "movement.hpp"
#include "steering.hpp" #include "steering.hpp"
MWMechanics::AiBreathe::AiBreathe()
: AiPackage()
{
}
bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
{ {
static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHoldBreathTime")->mValue.getFloat(); static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fHoldBreathTime")->mValue.getFloat();
@ -37,18 +31,3 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro
return true; return true;
} }
MWMechanics::AiBreathe *MWMechanics::AiBreathe::clone() const
{
return new AiBreathe(*this);
}
int MWMechanics::AiBreathe::getTypeId() const
{
return TypeIdBreathe;
}
unsigned int MWMechanics::AiBreathe::getPriority() const
{
return 2;
}

View file

@ -1,28 +1,27 @@
#ifndef GAME_MWMECHANICS_AIBREATHE_H #ifndef GAME_MWMECHANICS_AIBREATHE_H
#define GAME_MWMECHANICS_AIBREATHE_H #define GAME_MWMECHANICS_AIBREATHE_H
#include "aipackage.hpp" #include "typedaipackage.hpp"
namespace MWMechanics namespace MWMechanics
{ {
/// \brief AiPackage to have an actor resurface to breathe /// \brief AiPackage to have an actor resurface to breathe
// The AI will go up if lesser than half breath left // The AI will go up if lesser than half breath left
class AiBreathe final : public AiPackage class AiBreathe final : public TypedAiPackage<AiBreathe>
{ {
public: public:
AiBreathe();
AiBreathe *clone() const final;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final; static constexpr TypeId getTypeId() { return TypeIdBreathe; }
unsigned int getPriority() const final; static constexpr Options makeDefaultOptions()
{
bool canCancel() const final { return false; } AiPackage::Options options;
bool shouldCancelPreviousAi() const final { return false; } options.mPriority = 2;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
}; };
} }
#endif #endif

View file

@ -10,17 +10,22 @@
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "steering.hpp" #include "steering.hpp"
MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell) namespace MWMechanics
: mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(0) {
namespace
{
float getInitialDistance(const std::string& spellId)
{ {
ActionSpell action = ActionSpell(spellId); ActionSpell action = ActionSpell(spellId);
bool isRanged; bool isRanged;
mDistance = action.getCombatRange(isRanged); return action.getCombatRange(isRanged);
}
}
} }
MWMechanics::AiPackage *MWMechanics::AiCast::clone() const MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell)
: mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(getInitialDistance(spellId))
{ {
return new AiCast(*this);
} }
bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration) bool MWMechanics::AiCast::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& characterController, MWMechanics::AiState& state, float duration)
@ -84,13 +89,3 @@ MWWorld::Ptr MWMechanics::AiCast::getTarget() const
return target; return target;
} }
int MWMechanics::AiCast::getTypeId() const
{
return AiPackage::TypeIdCast;
}
unsigned int MWMechanics::AiCast::getPriority() const
{
return 3;
}

View file

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AICAST_H #ifndef GAME_MWMECHANICS_AICAST_H
#define GAME_MWMECHANICS_AICAST_H #define GAME_MWMECHANICS_AICAST_H
#include "aipackage.hpp" #include "typedaipackage.hpp"
namespace MWWorld namespace MWWorld
{ {
@ -11,29 +11,31 @@ namespace MWWorld
namespace MWMechanics namespace MWMechanics
{ {
/// AiPackage which makes an actor to cast given spell. /// AiPackage which makes an actor to cast given spell.
class AiCast final : public AiPackage { class AiCast final : public TypedAiPackage<AiCast> {
public: public:
AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell=false); AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell=false);
AiPackage *clone() const final;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final; static constexpr TypeId getTypeId() { return TypeIdCast; }
MWWorld::Ptr getTarget() const final; MWWorld::Ptr getTarget() const final;
unsigned int getPriority() const final; static constexpr Options makeDefaultOptions()
{
bool canCancel() const final { return false; } AiPackage::Options options;
bool shouldCancelPreviousAi() const final { return false; } options.mPriority = 3;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
private: private:
std::string mTargetId; const std::string mTargetId;
std::string mSpellId; const std::string mSpellId;
bool mCasting; bool mCasting;
bool mManual; const bool mManual;
float mDistance; const float mDistance;
}; };
} }

View file

@ -474,26 +474,11 @@ namespace MWMechanics
} }
} }
int AiCombat::getTypeId() const
{
return TypeIdCombat;
}
unsigned int AiCombat::getPriority() const
{
return 1;
}
MWWorld::Ptr AiCombat::getTarget() const MWWorld::Ptr AiCombat::getTarget() const
{ {
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
} }
AiCombat *MWMechanics::AiCombat::clone() const
{
return new AiCombat(*this);
}
void AiCombat::writeState(ESM::AiSequence::AiSequence &sequence) const void AiCombat::writeState(ESM::AiSequence::AiSequence &sequence) const
{ {
std::unique_ptr<ESM::AiSequence::AiCombat> combat(new ESM::AiSequence::AiCombat()); std::unique_ptr<ESM::AiSequence::AiCombat> combat(new ESM::AiSequence::AiCombat());

View file

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AICOMBAT_H #ifndef GAME_MWMECHANICS_AICOMBAT_H
#define GAME_MWMECHANICS_AICOMBAT_H #define GAME_MWMECHANICS_AICOMBAT_H
#include "aipackage.hpp" #include "typedaipackage.hpp"
#include "../mwworld/cellstore.hpp" // for Doors #include "../mwworld/cellstore.hpp" // for Doors
@ -91,7 +91,7 @@ namespace MWMechanics
}; };
/// \brief Causes the actor to fight another actor /// \brief Causes the actor to fight another actor
class AiCombat final : public AiPackage class AiCombat final : public TypedAiPackage<AiCombat>
{ {
public: public:
///Constructor ///Constructor
@ -102,22 +102,24 @@ namespace MWMechanics
void init(); void init();
AiCombat *clone() const final;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final; static constexpr TypeId getTypeId() { return TypeIdCombat; }
unsigned int getPriority() const final; static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mPriority = 1;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
///Returns target ID ///Returns target ID
MWWorld::Ptr getTarget() const final; MWWorld::Ptr getTarget() const final;
void writeState(ESM::AiSequence::AiSequence &sequence) const final; void writeState(ESM::AiSequence::AiSequence &sequence) const final;
bool canCancel() const final { return false; }
bool shouldCancelPreviousAi() const final { return false; }
private: private:
/// Returns true if combat should end /// Returns true if combat should end
bool attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController); bool attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController);

View file

@ -26,7 +26,6 @@ namespace MWMechanics
, mCellY(std::numeric_limits<int>::max()) , mCellY(std::numeric_limits<int>::max())
{ {
mTargetActorRefId = actorId; mTargetActorRefId = actorId;
mMaxDist = 450;
} }
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)
@ -35,30 +34,20 @@ namespace MWMechanics
, mCellY(std::numeric_limits<int>::max()) , mCellY(std::numeric_limits<int>::max())
{ {
mTargetActorRefId = actorId; mTargetActorRefId = actorId;
mMaxDist = 450;
} }
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) : mCellId(escort->mCellId), mX(escort->mData.mX), mY(escort->mData.mY), mZ(escort->mData.mZ)
, mMaxDist(450) // 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)
, 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())
{ {
mTargetActorRefId = escort->mTargetId; mTargetActorRefId = escort->mTargetId;
mTargetActorId = escort->mTargetActorId; mTargetActorId = escort->mTargetActorId;
// 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.
if (mRemainingDuration > 0) // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves.
mDuration = 1;
else
mDuration = 0;
}
AiEscort *MWMechanics::AiEscort::clone() const
{
return new AiEscort(*this);
} }
bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) bool AiEscort::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
@ -106,11 +95,6 @@ namespace MWMechanics
return false; return false;
} }
int AiEscort::getTypeId() const
{
return TypeIdEscort;
}
void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const
{ {
std::unique_ptr<ESM::AiSequence::AiEscort> escort(new ESM::AiSequence::AiEscort()); std::unique_ptr<ESM::AiSequence::AiEscort> escort(new ESM::AiSequence::AiEscort());

View file

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AIESCORT_H #ifndef GAME_MWMECHANICS_AIESCORT_H
#define GAME_MWMECHANICS_AIESCORT_H #define GAME_MWMECHANICS_AIESCORT_H
#include "aipackage.hpp" #include "typedaipackage.hpp"
#include <string> #include <string>
@ -16,7 +16,7 @@ namespace AiSequence
namespace MWMechanics namespace MWMechanics
{ {
/// \brief AI Package to have an NPC lead the player to a specific point /// \brief AI Package to have an NPC lead the player to a specific point
class AiEscort final : public AiPackage class AiEscort final : public TypedAiPackage<AiEscort>
{ {
public: public:
/// Implementation of AiEscort /// Implementation of AiEscort
@ -30,15 +30,17 @@ namespace MWMechanics
AiEscort(const ESM::AiSequence::AiEscort* escort); AiEscort(const ESM::AiSequence::AiEscort* escort);
AiEscort *clone() const final;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final; static constexpr TypeId getTypeId() { return TypeIdEscort; }
bool useVariableSpeed() const final { return true; } static constexpr Options makeDefaultOptions()
{
bool sideWithTarget() const final { return true; } AiPackage::Options options;
options.mUseVariableSpeed = true;
options.mSideWithTarget = true;
return options;
}
void writeState(ESM::AiSequence::AiSequence &sequence) const final; void writeState(ESM::AiSequence::AiSequence &sequence) const final;
@ -47,16 +49,16 @@ namespace MWMechanics
osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); }
private: private:
std::string mCellId; const std::string mCellId;
float mX; const float mX;
float mY; const float mY;
float mZ; const float mZ;
float mMaxDist; float mMaxDist = 450;
float mDuration; // In hours const float mDuration; // In hours
float mRemainingDuration; // In hours float mRemainingDuration; // In hours
int mCellX; const int mCellX;
int mCellY; const int mCellY;
}; };
} }
#endif #endif

View file

@ -9,23 +9,8 @@ MWMechanics::AiFace::AiFace(float targetX, float targetY)
{ {
} }
MWMechanics::AiPackage *MWMechanics::AiFace::clone() const
{
return new AiFace(*this);
}
bool MWMechanics::AiFace::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& /*characterController*/, MWMechanics::AiState& /*state*/, float /*duration*/) bool MWMechanics::AiFace::execute(const MWWorld::Ptr& actor, MWMechanics::CharacterController& /*characterController*/, MWMechanics::AiState& /*state*/, float /*duration*/)
{ {
osg::Vec3f dir = osg::Vec3f(mTargetX, mTargetY, 0) - actor.getRefData().getPosition().asVec3(); osg::Vec3f dir = osg::Vec3f(mTargetX, mTargetY, 0) - actor.getRefData().getPosition().asVec3();
return zTurn(actor, std::atan2(dir.x(), dir.y()), osg::DegreesToRadians(3.f)); return zTurn(actor, std::atan2(dir.x(), dir.y()), osg::DegreesToRadians(3.f));
} }
int MWMechanics::AiFace::getTypeId() const
{
return AiPackage::TypeIdFace;
}
unsigned int MWMechanics::AiFace::getPriority() const
{
return 2;
}

View file

@ -1,28 +1,31 @@
#ifndef GAME_MWMECHANICS_AIFACE_H #ifndef GAME_MWMECHANICS_AIFACE_H
#define GAME_MWMECHANICS_AIFACE_H #define GAME_MWMECHANICS_AIFACE_H
#include "aipackage.hpp" #include "typedaipackage.hpp"
namespace MWMechanics namespace MWMechanics
{ {
/// AiPackage which makes an actor face a certain direction. /// AiPackage which makes an actor face a certain direction.
class AiFace final : public AiPackage { class AiFace final : public TypedAiPackage<AiFace> {
public: public:
AiFace(float targetX, float targetY); AiFace(float targetX, float targetY);
AiPackage *clone() const final;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final; static constexpr TypeId getTypeId() { return TypeIdFace; }
unsigned int getPriority() const final; static constexpr Options makeDefaultOptions()
{
bool canCancel() const final { return false; } AiPackage::Options options;
bool shouldCancelPreviousAi() const final { return false; } options.mPriority = 2;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
private: private:
float mTargetX, mTargetY; const float mTargetX;
const float mTargetY;
}; };
} }

View file

@ -16,25 +16,24 @@
namespace MWMechanics 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)
: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) : 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)
: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) : 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) AiFollow::AiFollow(const MWWorld::Ptr& actor, float duration, float x, float y, float z)
: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) : mAlwaysFollow(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z)
, mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++)
{ {
mTargetActorRefId = actor.getCellRef().getRefId(); mTargetActorRefId = actor.getCellRef().getRefId();
@ -42,7 +41,7 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, float duration, float x, float y,
} }
AiFollow::AiFollow(const MWWorld::Ptr& actor, const std::string &cellId, float duration, float x, float y, float z) AiFollow::AiFollow(const MWWorld::Ptr& actor, const std::string &cellId, float duration, float x, float y, float z)
: mAlwaysFollow(false), mCommanded(false), mDuration(duration), mRemainingDuration(duration), mX(x), mY(y), mZ(z) : 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 = actor.getCellRef().getRefId(); mTargetActorRefId = actor.getCellRef().getRefId();
@ -50,7 +49,8 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, const std::string &cellId, float d
} }
AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded) AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded)
: mAlwaysFollow(true), mCommanded(commanded), mDuration(0), mRemainingDuration(0), mX(0), mY(0), mZ(0) : TypedAiPackage<AiFollow>(makeDefaultOptions().withShouldCancelPreviousAi(!commanded))
, mAlwaysFollow(true), mDuration(0), mRemainingDuration(0), mX(0), mY(0), mZ(0)
, mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++) , mCellId(""), mActive(false), mFollowIndex(mFollowIndexCounter++)
{ {
mTargetActorRefId = actor.getCellRef().getRefId(); mTargetActorRefId = actor.getCellRef().getRefId();
@ -58,18 +58,18 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded)
} }
AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow) AiFollow::AiFollow(const ESM::AiSequence::AiFollow *follow)
: mAlwaysFollow(follow->mAlwaysFollow), mCommanded(follow->mCommanded), mRemainingDuration(follow->mRemainingDuration) : TypedAiPackage<AiFollow>(makeDefaultOptions().withShouldCancelPreviousAi(!follow->mCommanded))
, mAlwaysFollow(follow->mAlwaysFollow)
// mDuration isn't saved in the save file, so just giving it "1" for now if the package had 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(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++)
{ {
mTargetActorRefId = follow->mTargetId; mTargetActorRefId = follow->mTargetId;
mTargetActorId = follow->mTargetActorId; mTargetActorId = follow->mTargetActorId;
// mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration.
// The exact value of mDuration only matters for repeating packages.
if (mRemainingDuration > 0) // Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves.
mDuration = 1;
else
mDuration = 0;
} }
bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) bool AiFollow::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
@ -212,19 +212,9 @@ std::string AiFollow::getFollowedActor()
return mTargetActorRefId; return mTargetActorRefId;
} }
AiFollow *MWMechanics::AiFollow::clone() const
{
return new AiFollow(*this);
}
int AiFollow::getTypeId() const
{
return TypeIdFollow;
}
bool AiFollow::isCommanded() const bool AiFollow::isCommanded() const
{ {
return mCommanded; return !mOptions.mShouldCancelPreviousAi;
} }
void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
@ -238,7 +228,7 @@ void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
follow->mRemainingDuration = mRemainingDuration; follow->mRemainingDuration = mRemainingDuration;
follow->mCellId = mCellId; follow->mCellId = mCellId;
follow->mAlwaysFollow = mAlwaysFollow; follow->mAlwaysFollow = mAlwaysFollow;
follow->mCommanded = mCommanded; follow->mCommanded = isCommanded();
follow->mActive = mActive; follow->mActive = mActive;
ESM::AiSequence::AiPackageContainer package; ESM::AiSequence::AiPackageContainer package;

View file

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AIFOLLOW_H #ifndef GAME_MWMECHANICS_AIFOLLOW_H
#define GAME_MWMECHANICS_AIFOLLOW_H #define GAME_MWMECHANICS_AIFOLLOW_H
#include "aipackage.hpp" #include "typedaipackage.hpp"
#include <string> #include <string>
@ -39,7 +39,7 @@ namespace MWMechanics
/// \brief AiPackage for an actor to follow another actor/the PC /// \brief AiPackage for an actor to follow another actor/the PC
/** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely /** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely
**/ **/
class AiFollow final : public AiPackage 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, float duration, float x, float y, float z);
@ -53,17 +53,18 @@ namespace MWMechanics
AiFollow(const ESM::AiSequence::AiFollow* follow); AiFollow(const ESM::AiSequence::AiFollow* follow);
bool sideWithTarget() const final { return true; }
bool followTargetThroughDoors() const final { return true; }
bool shouldCancelPreviousAi() const final { return !mCommanded; }
AiFollow *clone() const final;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final; static constexpr TypeId getTypeId() { return TypeIdFollow; }
bool useVariableSpeed() const final { return true; } static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mUseVariableSpeed = true;
options.mSideWithTarget = true;
options.mFollowTargetThroughDoors = true;
return options;
}
/// Returns the actor being followed /// Returns the actor being followed
std::string getFollowedActor(); std::string getFollowedActor();
@ -98,16 +99,15 @@ namespace MWMechanics
private: private:
/// This will make the actor always follow. /// This will make the actor always follow.
/** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/
bool mAlwaysFollow; const bool mAlwaysFollow;
bool mCommanded; const float mDuration; // Hours
float mDuration; // Hours
float mRemainingDuration; // Hours float mRemainingDuration; // Hours
float mX; const float mX;
float mY; const float mY;
float mZ; const float mZ;
std::string mCellId; const std::string mCellId;
bool mActive; // have we spotted the target? bool mActive; // have we spotted the target?
int mFollowIndex; const int mFollowIndex;
static int mFollowIndexCounter; static int mFollowIndexCounter;

View file

@ -24,7 +24,9 @@
#include <osg/Quat> #include <osg/Quat>
MWMechanics::AiPackage::AiPackage() : MWMechanics::AiPackage::AiPackage(TypeId typeId, const Options& options) :
mTypeId(typeId),
mOptions(options),
mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild mTimer(AI_REACTION_TIME + 1.0f), // to force initial pathbuild
mTargetActorRefId(""), mTargetActorRefId(""),
mTargetActorId(-1), mTargetActorId(-1),
@ -58,31 +60,6 @@ MWWorld::Ptr MWMechanics::AiPackage::getTarget() const
return MWWorld::Ptr(); return MWWorld::Ptr();
} }
bool MWMechanics::AiPackage::sideWithTarget() const
{
return false;
}
bool MWMechanics::AiPackage::followTargetThroughDoors() const
{
return false;
}
bool MWMechanics::AiPackage::canCancel() const
{
return true;
}
bool MWMechanics::AiPackage::shouldCancelPreviousAi() const
{
return true;
}
bool MWMechanics::AiPackage::getRepeat() const
{
return false;
}
void MWMechanics::AiPackage::reset() void MWMechanics::AiPackage::reset()
{ {
// reset all members // reset all members

View file

@ -1,6 +1,8 @@
#ifndef GAME_MWMECHANICS_AIPACKAGE_H #ifndef GAME_MWMECHANICS_AIPACKAGE_H
#define GAME_MWMECHANICS_AIPACKAGE_H #define GAME_MWMECHANICS_AIPACKAGE_H
#include <memory>
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include "pathfinding.hpp" #include "pathfinding.hpp"
@ -53,13 +55,41 @@ namespace MWMechanics
TypeIdCast = 11 TypeIdCast = 11
}; };
///Default constructor struct Options
AiPackage(); {
unsigned int mPriority = 0;
bool mUseVariableSpeed = false;
bool mSideWithTarget = false;
bool mFollowTargetThroughDoors = false;
bool mCanCancel = true;
bool mShouldCancelPreviousAi = true;
bool mRepeat = false;
bool mAlwaysActive = false;
constexpr Options withRepeat(bool value)
{
mRepeat = value;
return *this;
}
constexpr Options withShouldCancelPreviousAi(bool value)
{
mShouldCancelPreviousAi = value;
return *this;
}
};
AiPackage(TypeId typeId, const Options& options);
virtual ~AiPackage() = default; virtual ~AiPackage() = default;
static constexpr Options makeDefaultOptions()
{
return Options{};
}
///Clones the package ///Clones the package
virtual AiPackage *clone() const = 0; virtual std::unique_ptr<AiPackage> clone() const = 0;
/// Updates and runs the package (Should run every frame) /// Updates and runs the package (Should run every frame)
/// \return Package completed? /// \return Package completed?
@ -67,13 +97,13 @@ namespace MWMechanics
/// Returns the TypeID of the AiPackage /// Returns the TypeID of the AiPackage
/// \see enum TypeId /// \see enum TypeId
virtual int getTypeId() const = 0; TypeId getTypeId() const { return mTypeId; }
/// Higher number is higher priority (0 being the lowest) /// Higher number is higher priority (0 being the lowest)
virtual unsigned int getPriority() const {return 0;} unsigned int getPriority() const { return mOptions.mPriority; }
/// Check if package use movement with variable speed /// Check if package use movement with variable speed
virtual bool useVariableSpeed() const { return false;} bool useVariableSpeed() const { return mOptions.mUseVariableSpeed; }
virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {} virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {}
@ -87,24 +117,24 @@ namespace MWMechanics
virtual osg::Vec3f getDestination(const MWWorld::Ptr& actor) const { return osg::Vec3f(0, 0, 0); }; virtual osg::Vec3f getDestination(const MWWorld::Ptr& actor) const { return osg::Vec3f(0, 0, 0); };
/// Return true if having this AiPackage makes the actor side with the target in fights (default false) /// Return true if having this AiPackage makes the actor side with the target in fights (default false)
virtual bool sideWithTarget() const; bool sideWithTarget() const { return mOptions.mSideWithTarget; }
/// Return true if the actor should follow the target through teleport doors (default false) /// Return true if the actor should follow the target through teleport doors (default false)
virtual bool followTargetThroughDoors() const; bool followTargetThroughDoors() const { return mOptions.mFollowTargetThroughDoors; }
/// Can this Ai package be canceled? (default true) /// Can this Ai package be canceled? (default true)
virtual bool canCancel() const; bool canCancel() const { return mOptions.mCanCancel; }
/// 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)?
virtual bool shouldCancelPreviousAi() const; 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. Currently only used for Wander packages.
virtual bool getRepeat() const; 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); }
// Return true if any loaded actor with this AI package must be active. /// Return true if any loaded actor with this AI package must be active.
virtual bool alwaysActive() const { return false; } bool alwaysActive() const { return mOptions.mAlwaysActive; }
/// Reset pathfinding state /// Reset pathfinding state
void reset(); void reset();
@ -137,6 +167,9 @@ namespace MWMechanics
DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const; DetourNavigator::Flags getNavigatorFlags(const MWWorld::Ptr& actor) const;
const TypeId mTypeId;
const Options mOptions;
// TODO: all this does not belong here, move into temporary storage // TODO: all this does not belong here, move into temporary storage
PathFinder mPathFinder; PathFinder mPathFinder;
ObstacleCheck mObstacleCheck; ObstacleCheck mObstacleCheck;

View file

@ -40,10 +40,6 @@ AiPursue::AiPursue(const ESM::AiSequence::AiPursue *pursue)
mTargetActorId = pursue->mTargetActorId; mTargetActorId = pursue->mTargetActorId;
} }
AiPursue *MWMechanics::AiPursue::clone() const
{
return new AiPursue(*this);
}
bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
{ {
if(actor.getClass().getCreatureStats(actor).isDead()) if(actor.getClass().getCreatureStats(actor).isDead())
@ -116,11 +112,6 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte
return false; return false;
} }
int AiPursue::getTypeId() const
{
return TypeIdPursue;
}
MWWorld::Ptr AiPursue::getTarget() const MWWorld::Ptr AiPursue::getTarget() const
{ {
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);

View file

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AIPURSUE_H #ifndef GAME_MWMECHANICS_AIPURSUE_H
#define GAME_MWMECHANICS_AIPURSUE_H #define GAME_MWMECHANICS_AIPURSUE_H
#include "aipackage.hpp" #include "typedaipackage.hpp"
namespace ESM namespace ESM
{ {
@ -17,7 +17,7 @@ namespace MWMechanics
/** Used for arresting players. Causes the actor to run to the pursued actor and activate them, to arrest them. /** Used for arresting players. Causes the actor to run to the pursued actor and activate them, to arrest them.
Note that while very similar to AiActivate, it will ONLY activate when evry close to target (Not also when the Note that while very similar to AiActivate, it will ONLY activate when evry close to target (Not also when the
path is completed). **/ path is completed). **/
class AiPursue final : public AiPackage class AiPursue final : public TypedAiPackage<AiPursue>
{ {
public: public:
///Constructor ///Constructor
@ -26,16 +26,21 @@ namespace MWMechanics
AiPursue(const ESM::AiSequence::AiPursue* pursue); AiPursue(const ESM::AiSequence::AiPursue* pursue);
AiPursue *clone() const final;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final;
static constexpr TypeId getTypeId() { return TypeIdPursue; }
static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mCanCancel = false;
options.mShouldCancelPreviousAi = false;
return options;
}
MWWorld::Ptr getTarget() const final; MWWorld::Ptr getTarget() const final;
void writeState (ESM::AiSequence::AiSequence& sequence) const final; void writeState (ESM::AiSequence::AiSequence& sequence) const final;
bool canCancel() const final { return false; }
bool shouldCancelPreviousAi() const final { return false; }
}; };
} }
#endif #endif

View file

@ -25,9 +25,8 @@ namespace MWMechanics
void AiSequence::copy (const AiSequence& sequence) void AiSequence::copy (const AiSequence& sequence)
{ {
for (std::list<AiPackage *>::const_iterator iter (sequence.mPackages.begin()); for (const auto& package : sequence.mPackages)
iter!=sequence.mPackages.end(); ++iter) mPackages.push_back(package->clone());
mPackages.push_back ((*iter)->clone());
// We need to keep an AiWander storage, if present - it has a state machine. // We need to keep an AiWander storage, if present - it has a state machine.
// Not sure about another temporary storages // Not sure about another temporary storages
@ -82,7 +81,7 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const
bool AiSequence::getCombatTargets(std::vector<MWWorld::Ptr> &targetActors) const bool AiSequence::getCombatTargets(std::vector<MWWorld::Ptr> &targetActors) const
{ {
for (std::list<AiPackage*>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCombat) if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCombat)
targetActors.push_back((*it)->getTarget()); targetActors.push_back((*it)->getTarget());
@ -91,24 +90,23 @@ bool AiSequence::getCombatTargets(std::vector<MWWorld::Ptr> &targetActors) const
return !targetActors.empty(); return !targetActors.empty();
} }
std::list<AiPackage*>::const_iterator AiSequence::begin() const std::list<std::unique_ptr<AiPackage>>::const_iterator AiSequence::begin() const
{ {
return mPackages.begin(); return mPackages.begin();
} }
std::list<AiPackage*>::const_iterator AiSequence::end() const std::list<std::unique_ptr<AiPackage>>::const_iterator AiSequence::end() const
{ {
return mPackages.end(); return mPackages.end();
} }
void AiSequence::erase(std::list<AiPackage*>::const_iterator package) void AiSequence::erase(std::list<std::unique_ptr<AiPackage>>::const_iterator package)
{ {
// Not sure if manually terminated packages should trigger mDone, probably not? // Not sure if manually terminated packages should trigger mDone, probably not?
for(std::list<AiPackage*>::iterator it = mPackages.begin(); it != mPackages.end(); ++it) for(auto it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
if (package == it) if (package == it)
{ {
delete *it;
mPackages.erase(it); mPackages.erase(it);
return; return;
} }
@ -118,7 +116,7 @@ void AiSequence::erase(std::list<AiPackage*>::const_iterator package)
bool AiSequence::isInCombat() const bool AiSequence::isInCombat() const
{ {
for(std::list<AiPackage*>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
if ((*it)->getTypeId() == AiPackage::TypeIdCombat) if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
return true; return true;
@ -128,7 +126,7 @@ bool AiSequence::isInCombat() const
bool AiSequence::isEngagedWithActor() const bool AiSequence::isEngagedWithActor() const
{ {
for (std::list<AiPackage *>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
if ((*it)->getTypeId() == AiPackage::TypeIdCombat) if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
{ {
@ -142,7 +140,7 @@ bool AiSequence::isEngagedWithActor() const
bool AiSequence::hasPackage(int typeId) const bool AiSequence::hasPackage(int typeId) const
{ {
for (std::list<AiPackage*>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
if ((*it)->getTypeId() == typeId) if ((*it)->getTypeId() == typeId)
return true; return true;
@ -152,7 +150,7 @@ bool AiSequence::hasPackage(int typeId) const
bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const
{ {
for(std::list<AiPackage*>::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
if ((*it)->getTypeId() == AiPackage::TypeIdCombat) if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
{ {
@ -165,11 +163,10 @@ bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const
void AiSequence::stopCombat() void AiSequence::stopCombat()
{ {
for(std::list<AiPackage*>::iterator it = mPackages.begin(); it != mPackages.end(); ) for(auto it = mPackages.begin(); it != mPackages.end(); )
{ {
if ((*it)->getTypeId() == AiPackage::TypeIdCombat) if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
{ {
delete *it;
it = mPackages.erase(it); it = mPackages.erase(it);
} }
else else
@ -179,11 +176,10 @@ void AiSequence::stopCombat()
void AiSequence::stopPursuit() void AiSequence::stopPursuit()
{ {
for(std::list<AiPackage*>::iterator it = mPackages.begin(); it != mPackages.end(); ) for(auto it = mPackages.begin(); it != mPackages.end(); )
{ {
if ((*it)->getTypeId() == AiPackage::TypeIdPursue) if ((*it)->getTypeId() == AiPackage::TypeIdPursue)
{ {
delete *it;
it = mPackages.erase(it); it = mPackages.erase(it);
} }
else else
@ -213,7 +209,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
} }
auto packageIt = mPackages.begin(); auto packageIt = mPackages.begin();
MWMechanics::AiPackage* package = *packageIt; MWMechanics::AiPackage* package = packageIt->get();
if (!package->alwaysActive() && outOfRange) if (!package->alwaysActive() && outOfRange)
return; return;
@ -231,7 +227,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
float bestRating = 0.f; float bestRating = 0.f;
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end();) for (auto it = mPackages.begin(); it != mPackages.end();)
{ {
if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break; if ((*it)->getTypeId() != AiPackage::TypeIdCombat) break;
@ -240,7 +236,6 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
// target disappeared (e.g. summoned creatures) // target disappeared (e.g. summoned creatures)
if (target.isEmpty()) if (target.isEmpty())
{ {
delete *it;
it = mPackages.erase(it); it = mPackages.erase(it);
} }
else else
@ -276,7 +271,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
} }
packageIt = mPackages.begin(); packageIt = mPackages.begin();
package = *packageIt; package = packageIt->get();
packageTypeId = package->getTypeId(); packageTypeId = package->getTypeId();
} }
@ -293,7 +288,6 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
// To account for the rare case where AiPackage::execute() queued another AI package // To account for the rare case where AiPackage::execute() queued another AI package
// (e.g. AiPursue executing a dialogue script that uses startCombat) // (e.g. AiPursue executing a dialogue script that uses startCombat)
mPackages.erase(packageIt); mPackages.erase(packageIt);
delete package;
if (isActualAiPackage(packageTypeId)) if (isActualAiPackage(packageTypeId))
mDone = true; mDone = true;
} }
@ -311,9 +305,6 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
void AiSequence::clear() void AiSequence::clear()
{ {
for (std::list<AiPackage *>::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter)
delete *iter;
mPackages.clear(); mPackages.clear();
} }
@ -340,26 +331,24 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
osg::Vec3f dest; osg::Vec3f dest;
if (currentTypeId == MWMechanics::AiPackage::TypeIdWander) if (currentTypeId == MWMechanics::AiPackage::TypeIdWander)
{ {
AiPackage* activePackage = getActivePackage(); dest = getActivePackage().getDestination(actor);
dest = activePackage->getDestination(actor);
} }
else else
{ {
dest = actor.getRefData().getPosition().asVec3(); dest = actor.getRefData().getPosition().asVec3();
} }
MWMechanics::AiTravel travelPackage(dest.x(), dest.y(), dest.z(), true); MWMechanics::AiInternalTravel travelPackage(dest.x(), dest.y(), dest.z());
stack(travelPackage, actor, false); stack(travelPackage, actor, false);
} }
// remove previous packages if required // remove previous packages if required
if (cancelOther && package.shouldCancelPreviousAi()) if (cancelOther && package.shouldCancelPreviousAi())
{ {
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end();) for (auto it = mPackages.begin(); it != mPackages.end();)
{ {
if((*it)->canCancel()) if((*it)->canCancel())
{ {
delete *it;
it = mPackages.erase(it); it = mPackages.erase(it);
} }
else else
@ -369,7 +358,7 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
} }
// insert new package in correct place depending on priority // insert new package in correct place depending on priority
for(std::list<AiPackage *>::iterator it = mPackages.begin(); it != mPackages.end(); ++it) for (auto it = mPackages.begin(); it != mPackages.end(); ++it)
{ {
// We should keep current AiCast package, if we try to add a new one. // We should keep current AiCast package, if we try to add a new one.
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast && if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast &&
@ -401,12 +390,11 @@ bool MWMechanics::AiSequence::isEmpty() const
return mPackages.empty(); return mPackages.empty();
} }
AiPackage* MWMechanics::AiSequence::getActivePackage() const AiPackage& MWMechanics::AiSequence::getActivePackage()
{ {
if(mPackages.empty()) if(mPackages.empty())
throw std::runtime_error(std::string("No AI Package!")); throw std::runtime_error(std::string("No AI Package!"));
else return *mPackages.front();
return mPackages.front();
} }
void AiSequence::fill(const ESM::AIPackageList &list) void AiSequence::fill(const ESM::AIPackageList &list)
@ -417,7 +405,7 @@ void AiSequence::fill(const ESM::AIPackageList &list)
for (std::vector<ESM::AIPackage>::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it) for (std::vector<ESM::AIPackage>::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it)
{ {
MWMechanics::AiPackage* package; std::unique_ptr<MWMechanics::AiPackage> package;
if (it->mType == ESM::AI_Wander) if (it->mType == ESM::AI_Wander)
{ {
ESM::AIWander data = it->mWander; ESM::AIWander data = it->mWander;
@ -425,38 +413,36 @@ void AiSequence::fill(const ESM::AIPackageList &list)
idles.reserve(8); idles.reserve(8);
for (int i=0; i<8; ++i) for (int i=0; i<8; ++i)
idles.push_back(data.mIdle[i]); idles.push_back(data.mIdle[i]);
package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat != 0); package = std::make_unique<MWMechanics::AiWander>(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mShouldRepeat != 0);
} }
else if (it->mType == ESM::AI_Escort) else if (it->mType == ESM::AI_Escort)
{ {
ESM::AITarget data = it->mTarget; ESM::AITarget data = it->mTarget;
package = new 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);
} }
else if (it->mType == ESM::AI_Travel) else if (it->mType == ESM::AI_Travel)
{ {
ESM::AITravel data = it->mTravel; ESM::AITravel data = it->mTravel;
package = new MWMechanics::AiTravel(data.mX, data.mY, data.mZ); package = std::make_unique<MWMechanics::AiTravel>(data.mX, data.mY, data.mZ);
} }
else if (it->mType == ESM::AI_Activate) else if (it->mType == ESM::AI_Activate)
{ {
ESM::AIActivate data = it->mActivate; ESM::AIActivate data = it->mActivate;
package = new MWMechanics::AiActivate(data.mName.toString()); package = std::make_unique<MWMechanics::AiActivate>(data.mName.toString());
} }
else //if (it->mType == ESM::AI_Follow) else //if (it->mType == ESM::AI_Follow)
{ {
ESM::AITarget data = it->mTarget; ESM::AITarget data = it->mTarget;
package = new 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);
} }
mPackages.push_back(package); mPackages.push_back(std::move(package));
} }
} }
void AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const void AiSequence::writeState(ESM::AiSequence::AiSequence &sequence) const
{ {
for (std::list<AiPackage *>::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter) for (const auto& package : mPackages)
{ package->writeState(sequence);
(*iter)->writeState(sequence);
}
sequence.mLastAiPackage = mLastAiPackage; sequence.mLastAiPackage = mLastAiPackage;
} }
@ -492,7 +478,11 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
} }
case ESM::AiSequence::Ai_Travel: case ESM::AiSequence::Ai_Travel:
{ {
package.reset(new AiTravel(static_cast<ESM::AiSequence::AiTravel*>(it->mPackage))); const auto source = static_cast<const ESM::AiSequence::AiTravel*>(it->mPackage);
if (source->mHidden)
package.reset(new AiInternalTravel(source));
else
package.reset(new AiTravel(source));
break; break;
} }
case ESM::AiSequence::Ai_Escort: case ESM::AiSequence::Ai_Escort:
@ -527,7 +517,7 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
if (!package.get()) if (!package.get())
continue; continue;
mPackages.push_back(package.release()); mPackages.push_back(std::move(package));
} }
mLastAiPackage = sequence.mLastAiPackage; mLastAiPackage = sequence.mLastAiPackage;
@ -537,8 +527,7 @@ void AiSequence::fastForward(const MWWorld::Ptr& actor)
{ {
if (!mPackages.empty()) if (!mPackages.empty())
{ {
MWMechanics::AiPackage* package = mPackages.front(); mPackages.front()->fastForward(actor, mAiState);
package->fastForward(actor, mAiState);
} }
} }

View file

@ -2,6 +2,7 @@
#define GAME_MWMECHANICS_AISEQUENCE_H #define GAME_MWMECHANICS_AISEQUENCE_H
#include <list> #include <list>
#include <memory>
#include "aistate.hpp" #include "aistate.hpp"
@ -36,7 +37,7 @@ namespace MWMechanics
class AiSequence class AiSequence
{ {
///AiPackages to run though ///AiPackages to run though
std::list<AiPackage *> mPackages; std::list<std::unique_ptr<AiPackage>> mPackages;
///Finished with top AIPackage, set for one frame ///Finished with top AIPackage, set for one frame
bool mDone; bool mDone;
@ -64,10 +65,10 @@ namespace MWMechanics
virtual ~AiSequence(); virtual ~AiSequence();
/// Iterator may be invalidated by any function calls other than begin() or end(). /// Iterator may be invalidated by any function calls other than begin() or end().
std::list<AiPackage*>::const_iterator begin() const; std::list<std::unique_ptr<AiPackage>>::const_iterator begin() const;
std::list<AiPackage*>::const_iterator end() const; std::list<std::unique_ptr<AiPackage>>::const_iterator end() const;
void erase (std::list<AiPackage*>::const_iterator package); void erase(std::list<std::unique_ptr<AiPackage>>::const_iterator package);
/// Returns currently executing AiPackage type /// Returns currently executing AiPackage type
/** \see enum AiPackage::TypeId **/ /** \see enum AiPackage::TypeId **/
@ -125,7 +126,7 @@ namespace MWMechanics
/// Return the current active package. /// Return the current active package.
/** If there is no active package, it will throw an exception **/ /** If there is no active package, it will throw an exception **/
AiPackage* getActivePackage(); const AiPackage& getActivePackage();
/// Fills the AiSequence with packages /// Fills the AiSequence with packages
/** Typically used for loading from the ESM /** Typically used for loading from the ESM

View file

@ -27,19 +27,26 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2)
namespace MWMechanics namespace MWMechanics
{ {
AiTravel::AiTravel(float x, float y, float z, bool hidden) AiTravel::AiTravel(float x, float y, float z, AiTravel*)
: mX(x),mY(y),mZ(z),mHidden(hidden) : mX(x), mY(y), mZ(z), mHidden(false)
{
}
AiTravel::AiTravel(float x, float y, float z, AiInternalTravel* derived)
: TypedAiPackage<AiTravel>(derived), mX(x), mY(y), mZ(z), mHidden(true)
{
}
AiTravel::AiTravel(float x, float y, float z)
: AiTravel(x, y, z, 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(travel->mHidden) : 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
assert(!travel->mHidden);
AiTravel *MWMechanics::AiTravel::clone() const
{
return new AiTravel(*this);
} }
bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
@ -83,11 +90,6 @@ namespace MWMechanics
return false; return false;
} }
int AiTravel::getTypeId() const
{
return mHidden ? TypeIdInternalTravel : TypeIdTravel;
}
void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state) void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state)
{ {
if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3())) if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3()))
@ -112,5 +114,20 @@ namespace MWMechanics
package.mPackage = travel.release(); package.mPackage = travel.release();
sequence.mPackages.push_back(package); sequence.mPackages.push_back(package);
} }
AiInternalTravel::AiInternalTravel(float x, float y, float z)
: AiTravel(x, y, z, this)
{
}
AiInternalTravel::AiInternalTravel(const ESM::AiSequence::AiTravel* travel)
: AiTravel(travel->mData.mX, travel->mData.mY, travel->mData.mZ, this)
{
}
std::unique_ptr<AiPackage> AiInternalTravel::clone() const
{
return std::make_unique<AiInternalTravel>(*this);
}
} }

View file

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AITRAVEL_H #ifndef GAME_MWMECHANICS_AITRAVEL_H
#define GAME_MWMECHANICS_AITRAVEL_H #define GAME_MWMECHANICS_AITRAVEL_H
#include "aipackage.hpp" #include "typedaipackage.hpp"
namespace ESM namespace ESM
{ {
@ -13,12 +13,18 @@ namespace AiSequence
namespace MWMechanics namespace MWMechanics
{ {
struct AiInternalTravel;
/// \brief Causes the AI to travel to the specified point /// \brief Causes the AI to travel to the specified point
class AiTravel final : public AiPackage class AiTravel : public TypedAiPackage<AiTravel>
{ {
public: public:
/// Default constructor AiTravel(float x, float y, float z, AiTravel* derived);
AiTravel(float x, float y, float z, bool hidden = false);
AiTravel(float x, float y, float z, AiInternalTravel* derived);
AiTravel(float x, float y, float z);
AiTravel(const ESM::AiSequence::AiTravel* travel); AiTravel(const ESM::AiSequence::AiTravel* travel);
/// Simulates the passing of time /// Simulates the passing of time
@ -26,24 +32,37 @@ namespace MWMechanics
void writeState(ESM::AiSequence::AiSequence &sequence) const final; void writeState(ESM::AiSequence::AiSequence &sequence) const final;
AiTravel *clone() const final;
bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final; static constexpr TypeId getTypeId() { return TypeIdTravel; }
bool useVariableSpeed() const final { return true; } static constexpr Options makeDefaultOptions()
{
bool alwaysActive() const final { return true; } AiPackage::Options options;
options.mUseVariableSpeed = true;
options.mAlwaysActive = true;
return options;
}
osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); } osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); }
private: private:
float mX; const float mX;
float mY; const float mY;
float mZ; const float mZ;
bool mHidden; const bool mHidden;
};
struct AiInternalTravel final : public AiTravel
{
AiInternalTravel(float x, float y, float z);
explicit AiInternalTravel(const ESM::AiSequence::AiTravel* travel);
static constexpr TypeId getTypeId() { return TypeIdInternalTravel; }
std::unique_ptr<AiPackage> clone() const final;
}; };
} }

View file

@ -1,5 +1,7 @@
#include "aiwander.hpp" #include "aiwander.hpp"
#include <algorithm>
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/esm/aisequence.hpp> #include <components/esm/aisequence.hpp>
@ -33,6 +35,8 @@ namespace MWMechanics
// distance must be long enough that NPC will need to move to get there. // distance must be long enough that NPC will need to move to get there.
static const int MINIMUM_WANDER_DISTANCE = DESTINATION_TOLERANCE * 2; static const int MINIMUM_WANDER_DISTANCE = DESTINATION_TOLERANCE * 2;
static const std::size_t MAX_IDLE_SIZE = 8;
const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] = const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] =
{ {
std::string("idle2"), std::string("idle2"),
@ -94,30 +98,29 @@ namespace MWMechanics
{ {
actor.getClass().getMovementSettings(actor).mPosition[1] = 0; actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
} }
std::vector<unsigned char> getInitialIdle(const std::vector<unsigned char>& idle)
{
std::vector<unsigned char> result(MAX_IDLE_SIZE, 0);
std::copy_n(idle.begin(), std::min(MAX_IDLE_SIZE, idle.size()), result.begin());
return result;
}
std::vector<unsigned char> getInitialIdle(const unsigned char (&idle)[MAX_IDLE_SIZE])
{
return std::vector<unsigned char>(std::begin(idle), std::end(idle));
}
} }
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):
mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), TypedAiPackage<AiWander>(makeDefaultOptions().withRepeat(repeat)),
mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)), mDistance(std::max(0, distance)),
mDuration(std::max(0, duration)),
mRemainingDuration(duration), mTimeOfDay(timeOfDay),
mIdle(getInitialIdle(idle)),
mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)),
mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)), mUsePathgrid(false) mHasDestination(false), mDestination(osg::Vec3f(0, 0, 0)), mUsePathgrid(false)
{ {
mIdle.resize(8, 0);
init();
}
void AiWander::init()
{
// NOTE: mDistance and mDuration must be set already
if(mDistance < 0)
mDistance = 0;
if(mDuration < 0)
mDuration = 0;
}
AiPackage * MWMechanics::AiWander::clone() const
{
return new AiWander(*this);
} }
/* /*
@ -240,7 +243,6 @@ namespace MWMechanics
stopWalking(actor); stopWalking(actor);
// Reset package so it can be used again // Reset package so it can be used again
mRemainingDuration=mDuration; mRemainingDuration=mDuration;
init();
return true; return true;
} }
@ -308,11 +310,6 @@ namespace MWMechanics
return false; // AiWander package not yet completed return false; // AiWander package not yet completed
} }
bool AiWander::getRepeat() const
{
return mRepeat;
}
osg::Vec3f AiWander::getDestination(const MWWorld::Ptr& actor) const osg::Vec3f AiWander::getDestination(const MWWorld::Ptr& actor) const
{ {
if (mHasDestination) if (mHasDestination)
@ -598,11 +595,6 @@ namespace MWMechanics
} }
} }
int AiWander::getTypeId() const
{
return TypeIdWander;
}
void AiWander::stopWalking(const MWWorld::Ptr& actor) void AiWander::stopWalking(const MWWorld::Ptr& actor)
{ {
mPathFinder.clearPath(); mPathFinder.clearPath();
@ -872,7 +864,7 @@ namespace MWMechanics
assert (mIdle.size() == 8); assert (mIdle.size() == 8);
for (int i=0; i<8; ++i) for (int i=0; i<8; ++i)
wander->mData.mIdle[i] = mIdle[i]; wander->mData.mIdle[i] = mIdle[i];
wander->mData.mShouldRepeat = mRepeat; wander->mData.mShouldRepeat = mOptions.mRepeat;
wander->mStoredInitialActorPosition = mStoredInitialActorPosition; wander->mStoredInitialActorPosition = mStoredInitialActorPosition;
if (mStoredInitialActorPosition) if (mStoredInitialActorPosition)
wander->mInitialActorPosition = mInitialActorPosition; wander->mInitialActorPosition = mInitialActorPosition;
@ -884,11 +876,12 @@ namespace MWMechanics
} }
AiWander::AiWander (const ESM::AiSequence::AiWander* wander) AiWander::AiWander (const ESM::AiSequence::AiWander* wander)
: mDistance(wander->mData.mDistance) : TypedAiPackage<AiWander>(makeDefaultOptions().withRepeat(wander->mData.mShouldRepeat != 0))
, mDuration(wander->mData.mDuration) , mDistance(std::max(static_cast<short>(0), wander->mData.mDistance))
, mDuration(std::max(static_cast<short>(0), wander->mData.mDuration))
, mRemainingDuration(wander->mDurationData.mRemainingDuration) , mRemainingDuration(wander->mDurationData.mRemainingDuration)
, mTimeOfDay(wander->mData.mTimeOfDay) , mTimeOfDay(wander->mData.mTimeOfDay)
, mRepeat(wander->mData.mShouldRepeat != 0) , mIdle(getInitialIdle(wander->mData.mIdle))
, mStoredInitialActorPosition(wander->mStoredInitialActorPosition) , mStoredInitialActorPosition(wander->mStoredInitialActorPosition)
, mHasDestination(false) , mHasDestination(false)
, mDestination(osg::Vec3f(0, 0, 0)) , mDestination(osg::Vec3f(0, 0, 0))
@ -896,11 +889,7 @@ namespace MWMechanics
{ {
if (mStoredInitialActorPosition) if (mStoredInitialActorPosition)
mInitialActorPosition = wander->mInitialActorPosition; mInitialActorPosition = wander->mInitialActorPosition;
for (int i=0; i<8; ++i)
mIdle.push_back(wander->mData.mIdle[i]);
if (mRemainingDuration <= 0 || mRemainingDuration >= 24) if (mRemainingDuration <= 0 || mRemainingDuration >= 24)
mRemainingDuration = mDuration; mRemainingDuration = mDuration;
init();
} }
} }

View file

@ -1,7 +1,7 @@
#ifndef GAME_MWMECHANICS_AIWANDER_H #ifndef GAME_MWMECHANICS_AIWANDER_H
#define GAME_MWMECHANICS_AIWANDER_H #define GAME_MWMECHANICS_AIWANDER_H
#include "aipackage.hpp" #include "typedaipackage.hpp"
#include <vector> #include <vector>
@ -78,7 +78,7 @@ namespace MWMechanics
}; };
/// \brief Causes the Actor to wander within a specified range /// \brief Causes the Actor to wander within a specified range
class AiWander final : public AiPackage class AiWander final : public TypedAiPackage<AiWander>
{ {
public: public:
/// Constructor /// Constructor
@ -91,20 +91,22 @@ namespace MWMechanics
AiWander (const ESM::AiSequence::AiWander* wander); AiWander (const ESM::AiSequence::AiWander* wander);
AiPackage *clone() const final;
bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final; bool execute(const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) final;
int getTypeId() const final; static constexpr TypeId getTypeId() { return TypeIdWander; }
bool useVariableSpeed() const final { return true; } static constexpr Options makeDefaultOptions()
{
AiPackage::Options options;
options.mUseVariableSpeed = true;
options.mRepeat = false;
return options;
}
void writeState(ESM::AiSequence::AiSequence &sequence) const final; void writeState(ESM::AiSequence::AiSequence &sequence) const final;
void fastForward(const MWWorld::Ptr& actor, AiState& state) final; void fastForward(const MWWorld::Ptr& actor, AiState& state) final;
bool getRepeat() const final;
osg::Vec3f getDestination(const MWWorld::Ptr& actor) const final; osg::Vec3f getDestination(const MWWorld::Ptr& actor) const final;
osg::Vec3f getDestination() const final osg::Vec3f getDestination() const final
@ -116,8 +118,6 @@ namespace MWMechanics
} }
private: private:
// NOTE: mDistance and mDuration must be set already
void init();
void stopWalking(const MWWorld::Ptr& actor); void stopWalking(const MWWorld::Ptr& actor);
/// Have the given actor play an idle animation /// Have the given actor play an idle animation
@ -138,12 +138,11 @@ namespace MWMechanics
bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination); bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);
void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage); void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage);
int mDistance; // how far the actor can wander from the spawn point const int mDistance; // how far the actor can wander from the spawn point
int mDuration; const int mDuration;
float mRemainingDuration; float mRemainingDuration;
int mTimeOfDay; const int mTimeOfDay;
std::vector<unsigned char> mIdle; const std::vector<unsigned char> mIdle;
bool mRepeat;
bool mStoredInitialActorPosition; bool mStoredInitialActorPosition;
osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell

View file

@ -507,7 +507,7 @@ MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const
bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWWorld::Ptr &npc) bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWWorld::Ptr &npc)
{ {
int alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy); float alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy);
static const float fWortChanceValue = static const float fWortChanceValue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat();
return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue) return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue)

View file

@ -2319,7 +2319,7 @@ void CharacterController::update(float duration, bool animationOnly)
cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), osg::Vec3f(), true); cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), osg::Vec3f(), true);
} }
const int acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); const float acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics);
if (healthLost > (acrobaticsSkill * fatigueTerm)) if (healthLost > (acrobaticsSkill * fatigueTerm))
{ {
if (!godmode) if (!godmode)

View file

@ -116,7 +116,7 @@ namespace MWMechanics
blockerTerm *= gmst.find("fBlockStillBonus")->mValue.getFloat(); blockerTerm *= gmst.find("fBlockStillBonus")->mValue.getFloat();
blockerTerm *= blockerStats.getFatigueTerm(); blockerTerm *= blockerStats.getFatigueTerm();
int attackerSkill = 0; float attackerSkill = 0;
if (weapon.isEmpty()) if (weapon.isEmpty())
attackerSkill = attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand); attackerSkill = attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand);
else else

View file

@ -137,7 +137,7 @@ namespace MWMechanics
return mMagicEffects; return mMagicEffects;
} }
void CreatureStats::setAttribute(int index, int base) void CreatureStats::setAttribute(int index, float base)
{ {
AttributeValue current = getAttribute(index); AttributeValue current = getAttribute(index);
current.setBase(base); current.setBase(base);
@ -163,10 +163,10 @@ namespace MWMechanics
index == ESM::Attribute::Agility || index == ESM::Attribute::Agility ||
index == ESM::Attribute::Endurance) index == ESM::Attribute::Endurance)
{ {
int strength = getAttribute(ESM::Attribute::Strength).getModified(); float strength = getAttribute(ESM::Attribute::Strength).getModified();
int willpower = getAttribute(ESM::Attribute::Willpower).getModified(); float willpower = getAttribute(ESM::Attribute::Willpower).getModified();
int agility = getAttribute(ESM::Attribute::Agility).getModified(); float agility = getAttribute(ESM::Attribute::Agility).getModified();
int endurance = getAttribute(ESM::Attribute::Endurance).getModified(); float endurance = getAttribute(ESM::Attribute::Endurance).getModified();
DynamicStat<float> fatigue = getFatigue(); DynamicStat<float> fatigue = getFatigue();
float diff = (strength+willpower+agility+endurance) - fatigue.getBase(); float diff = (strength+willpower+agility+endurance) - fatigue.getBase();
float currentToBaseRatio = fatigue.getBase() > 0 ? (fatigue.getCurrent() / fatigue.getBase()) : 0; float currentToBaseRatio = fatigue.getBase() > 0 ? (fatigue.getCurrent() / fatigue.getBase()) : 0;
@ -575,6 +575,14 @@ namespace MWMechanics
state.mHasAiSettings = true; state.mHasAiSettings = true;
for (int i=0; i<4; ++i) for (int i=0; i<4; ++i)
mAiSettings[i].writeState (state.mAiSettings[i]); mAiSettings[i].writeState (state.mAiSettings[i]);
for (auto it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it)
{
for (int i=0; i<ESM::Attribute::Length; ++i)
state.mCorprusSpells[it->first].mWorsenings[i] = mCorprusSpells.at(it->first).mWorsenings[i];
state.mCorprusSpells[it->first].mNextWorsening = mCorprusSpells.at(it->first).mNextWorsening.toEsm();
}
} }
void CreatureStats::readState (const ESM::CreatureStats& state) void CreatureStats::readState (const ESM::CreatureStats& state)
@ -613,7 +621,7 @@ namespace MWMechanics
mTimeOfDeath = MWWorld::TimeStamp(state.mTimeOfDeath); mTimeOfDeath = MWWorld::TimeStamp(state.mTimeOfDeath);
//mHitAttemptActorId = state.mHitAttemptActorId; //mHitAttemptActorId = state.mHitAttemptActorId;
mSpells.readState(state.mSpells); mSpells.readState(state.mSpells, this);
mActiveSpells.readState(state.mActiveSpells); mActiveSpells.readState(state.mActiveSpells);
mAiSequence.readState(state.mAiSequence); mAiSequence.readState(state.mAiSequence);
mMagicEffects.readState(state.mMagicEffects); mMagicEffects.readState(state.mMagicEffects);
@ -624,6 +632,15 @@ namespace MWMechanics
if (state.mHasAiSettings) if (state.mHasAiSettings)
for (int i=0; i<4; ++i) for (int i=0; i<4; ++i)
mAiSettings[i].readState(state.mAiSettings[i]); mAiSettings[i].readState(state.mAiSettings[i]);
mCorprusSpells.clear();
for (auto it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it)
{
for (int i=0; i<ESM::Attribute::Length; ++i)
mCorprusSpells[it->first].mWorsenings[i] = state.mCorprusSpells.at(it->first).mWorsenings[i];
mCorprusSpells[it->first].mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening);
}
} }
void CreatureStats::setLastRestockTime(MWWorld::TimeStamp tradeTime) void CreatureStats::setLastRestockTime(MWWorld::TimeStamp tradeTime)
@ -722,4 +739,23 @@ namespace MWMechanics
/* /*
End of tes3mp addition End of tes3mp addition
*/ */
std::map<std::string, CorprusStats> &CreatureStats::getCorprusSpells()
{
return mCorprusSpells;
}
void CreatureStats::addCorprusSpell(const std::string& sourceId, CorprusStats& stats)
{
mCorprusSpells[sourceId] = stats;
}
void CreatureStats::removeCorprusSpell(const std::string& sourceId)
{
auto corprusIt = mCorprusSpells.find(sourceId);
if (corprusIt != mCorprusSpells.end())
{
mCorprusSpells.erase(corprusIt);
}
}
} }

View file

@ -12,6 +12,8 @@
#include "aisequence.hpp" #include "aisequence.hpp"
#include "drawstate.hpp" #include "drawstate.hpp"
#include <components/esm/attr.hpp>
namespace ESM namespace ESM
{ {
struct CreatureStats; struct CreatureStats;
@ -19,6 +21,14 @@ namespace ESM
namespace MWMechanics namespace MWMechanics
{ {
struct CorprusStats
{
static const int sWorseningPeriod = 24;
int mWorsenings[ESM::Attribute::Length];
MWWorld::TimeStamp mNextWorsening;
};
/// \brief Common creature stats /// \brief Common creature stats
/// ///
/// ///
@ -26,7 +36,7 @@ namespace MWMechanics
{ {
static int sActorId; static int sActorId;
DrawState_ mDrawState; DrawState_ mDrawState;
AttributeValue mAttributes[8]; AttributeValue mAttributes[ESM::Attribute::Length];
DynamicStat<float> mDynamic[3]; // health, magicka, fatigue DynamicStat<float> mDynamic[3]; // health, magicka, fatigue
Spells mSpells; Spells mSpells;
ActiveSpells mActiveSpells; ActiveSpells mActiveSpells;
@ -79,6 +89,8 @@ namespace MWMechanics
// This may be necessary when the creature is in an inactive cell. // This may be necessary when the creature is in an inactive cell.
std::vector<int> mSummonGraveyard; std::vector<int> mSummonGraveyard;
std::map<std::string, CorprusStats> mCorprusSpells;
protected: protected:
int mLevel; int mLevel;
@ -126,7 +138,7 @@ namespace MWMechanics
void setAttribute(int index, const AttributeValue &value); void setAttribute(int index, const AttributeValue &value);
// Shortcut to set only the base // Shortcut to set only the base
void setAttribute(int index, int base); void setAttribute(int index, float base);
void setHealth(const DynamicStat<float> &value); void setHealth(const DynamicStat<float> &value);
@ -301,6 +313,12 @@ namespace MWMechanics
/// assigned this function will return false). /// assigned this function will return false).
static void cleanup(); static void cleanup();
std::map<std::string, CorprusStats> & getCorprusSpells();
void addCorprusSpell(const std::string& sourceId, CorprusStats& stats);
void removeCorprusSpell(const std::string& sourceId);
}; };
} }

View file

@ -303,6 +303,24 @@ namespace MWMechanics
mWatched = ptr; mWatched = ptr;
} }
void MechanicsManager::restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId)
{
auto& stats = actor.getClass().getCreatureStats (actor);
auto& corprusSpells = stats.getCorprusSpells();
auto corprusIt = corprusSpells.find(sourceId);
if (corprusIt != corprusSpells.end())
{
for (int i = 0; i < ESM::Attribute::Length; ++i)
{
MWMechanics::AttributeValue attr = stats.getAttribute(i);
attr.restore(corprusIt->second.mWorsenings[i]);
actor.getClass().getCreatureStats(actor).setAttribute(i, attr);
}
}
}
void MechanicsManager::update(float duration, bool paused) void MechanicsManager::update(float duration, bool paused)
{ {
if(!mWatched.isEmpty()) if(!mWatched.isEmpty())
@ -684,10 +702,10 @@ namespace MWMechanics
// I suppose the temporary disposition change (second param to getDerivedDisposition()) _has_ to be considered here, // I suppose the temporary disposition change (second param to getDerivedDisposition()) _has_ to be considered here,
// otherwise one would get different prices when exiting and re-entering the dialogue window... // otherwise one would get different prices when exiting and re-entering the dialogue window...
int clampedDisposition = getDerivedDisposition(ptr); int clampedDisposition = getDerivedDisposition(ptr);
float a = static_cast<float>(std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100)); float a = std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100.f);
float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f);
float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f);
float d = static_cast<float>(std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100)); float d = std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100.f);
float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f);
float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f);
float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm(); float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm();
@ -1262,7 +1280,7 @@ namespace MWMechanics
if (!Misc::StringUtils::ciEqual(item.getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId)) if (!Misc::StringUtils::ciEqual(item.getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId))
{ {
if (victim.isEmpty() || (victim.getClass().isActor() && !victim.getClass().getCreatureStats(victim).isDead())) if (victim.isEmpty() || (victim.getClass().isActor() && victim.getRefData().getCount() > 0 && !victim.getClass().getCreatureStats(victim).isDead()))
mStolenItems[Misc::StringUtils::lowerCase(item.getCellRef().getRefId())][owner] += count; mStolenItems[Misc::StringUtils::lowerCase(item.getCellRef().getRefId())][owner] += count;
} }
if (alarm) if (alarm)
@ -1743,8 +1761,8 @@ namespace MWMechanics
static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat(); static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat();
static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat();
float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak));
int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); float agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float bootWeight = 0; float bootWeight = 0;
if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr)) if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr))
{ {
@ -1767,10 +1785,10 @@ namespace MWMechanics
float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility; float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility;
CreatureStats& observerStats = observer.getClass().getCreatureStats(observer); CreatureStats& observerStats = observer.getClass().getCreatureStats(observer);
int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); float obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified();
int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); float obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified();
float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).getMagnitude(); float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).getMagnitude();
int obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak); float obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak);
float obsTerm = obsSneak + 0.2f * obsAgility + 0.1f * obsLuck - obsBlind; float obsTerm = obsSneak + 0.2f * obsAgility + 0.1f * obsLuck - obsBlind;

View file

@ -277,6 +277,8 @@ namespace MWMechanics
virtual GreetingState getGreetingState(const MWWorld::Ptr& ptr) const override; virtual GreetingState getGreetingState(const MWWorld::Ptr& ptr) const override;
virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const override; virtual bool isTurningToPlayer(const MWWorld::Ptr& ptr) const override;
virtual void restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId) override;
private: private:
bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker);
bool canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set<MWWorld::Ptr> &playerFollowers); bool canReportCrime(const MWWorld::Ptr &actor, const MWWorld::Ptr &victim, std::set<MWWorld::Ptr> &playerFollowers);

View file

@ -226,9 +226,9 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_,
void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress, bool readBook) void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress, bool readBook)
{ {
int base = getSkill (skillIndex).getBase(); float base = getSkill (skillIndex).getBase();
if (base >= 100) if (base >= 100.f)
return; return;
base += 1; base += 1;
@ -265,7 +265,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas
MWBase::Environment::get().getWindowManager()->playSound("skillraise"); MWBase::Environment::get().getWindowManager()->playSound("skillraise");
std::string message = MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", ""); std::string message = MWBase::Environment::get().getWindowManager ()->getGameSettingString ("sNotifyMessage39", "");
message = Misc::StringUtils::format(message, ("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}"), base); message = Misc::StringUtils::format(message, ("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}"), static_cast<int>(base));
if (readBook) if (readBook)
message = "#{sBookSkillMessage}\n" + message; message = "#{sBookSkillMessage}\n" + message;
@ -360,7 +360,7 @@ void MWMechanics::NpcStats::levelUp()
for (int i=0; i<ESM::Attribute::Length; ++i) for (int i=0; i<ESM::Attribute::Length; ++i)
mSkillIncreases[i] = 0; mSkillIncreases[i] = 0;
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); const float endurance = getAttribute(ESM::Attribute::Endurance).getBase();
// "When you gain a level, in addition to increasing three primary attributes, your Health // "When you gain a level, in addition to increasing three primary attributes, your Health
// will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level, // will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level,
@ -377,8 +377,8 @@ void MWMechanics::NpcStats::levelUp()
void MWMechanics::NpcStats::updateHealth() void MWMechanics::NpcStats::updateHealth()
{ {
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase(); const float endurance = getAttribute(ESM::Attribute::Endurance).getBase();
const int strength = getAttribute(ESM::Attribute::Strength).getBase(); const float strength = getAttribute(ESM::Attribute::Strength).getBase();
setHealth(floor(0.5f * (strength + endurance))); setHealth(floor(0.5f * (strength + endurance)));
} }

View file

@ -22,8 +22,8 @@ namespace MWMechanics
float Pickpocket::getChanceModifier(const MWWorld::Ptr &ptr, float add) float Pickpocket::getChanceModifier(const MWWorld::Ptr &ptr, float add)
{ {
NpcStats& stats = ptr.getClass().getNpcStats(ptr); NpcStats& stats = ptr.getClass().getNpcStats(ptr);
float agility = static_cast<float>(stats.getAttribute(ESM::Attribute::Agility).getModified()); float agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
float luck = static_cast<float>(stats.getAttribute(ESM::Attribute::Luck).getModified()); float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak));
return (add + 0.2f * agility + 0.1f * luck + sneak) * stats.getFatigueTerm(); return (add + 0.2f * agility + 0.1f * luck + sneak) * stats.getFatigueTerm();
} }

View file

@ -45,9 +45,9 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair)
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
float fatigueTerm = stats.getFatigueTerm(); float fatigueTerm = stats.getFatigueTerm();
int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); float pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified();
int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
int armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer); float armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer);
float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>() float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
.find("fRepairAmountMult")->mValue.getFloat(); .find("fRepairAmountMult")->mValue.getFloat();

View file

@ -33,8 +33,8 @@ namespace MWMechanics
: mActor(actor) : mActor(actor)
{ {
CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor);
mAgility = static_cast<float>(creatureStats.getAttribute(ESM::Attribute::Agility).getModified()); mAgility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified();
mLuck = static_cast<float>(creatureStats.getAttribute(ESM::Attribute::Luck).getModified()); mLuck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified();
mSecuritySkill = static_cast<float>(actor.getClass().getSkill(actor, ESM::Skill::Security)); mSecuritySkill = static_cast<float>(actor.getClass().getSkill(actor, ESM::Skill::Security));
mFatigueTerm = creatureStats.getFatigueTerm(); mFatigueTerm = creatureStats.getFatigueTerm();
} }

View file

@ -77,8 +77,13 @@ namespace MWMechanics
void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster,
const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded) const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded)
{ {
if (!target.isEmpty() && target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead()) if (!target.isEmpty() && target.getClass().isActor())
{
// Early-out for characters that have departed.
const auto& stats = target.getClass().getCreatureStats(target);
if (stats.isDead() && stats.isDeathAnimationFinished())
return; return;
}
// If none of the effects need to apply, we can early-out // If none of the effects need to apply, we can early-out
bool found = false; bool found = false;
@ -218,9 +223,9 @@ namespace MWMechanics
} }
bool effectAffectsHealth = isHarmful || effectIt->mEffectID == ESM::MagicEffect::RestoreHealth; bool effectAffectsHealth = isHarmful || effectIt->mEffectID == ESM::MagicEffect::RestoreHealth;
if (castByPlayer && target != caster && effectAffectsHealth) if (castByPlayer && target != caster && !target.getClass().getCreatureStats(target).isDead() && effectAffectsHealth)
{ {
// If player is attempting to cast a harmful spell or is healing someone, show the target's HP bar. // If player is attempting to cast a harmful spell on or is healing a living target, show the target's HP bar.
MWBase::Environment::get().getWindowManager()->setEnemy(target); MWBase::Environment::get().getWindowManager()->setEnemy(target);
} }

View file

@ -40,8 +40,8 @@ namespace MWMechanics
float resistance = getEffectResistanceAttribute(effectId, magicEffects); float resistance = getEffectResistanceAttribute(effectId, magicEffects);
int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
float luck = static_cast<float>(stats.getAttribute(ESM::Attribute::Luck).getModified()); float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float x = (willpower + 0.1f * luck) * stats.getFatigueTerm(); float x = (willpower + 0.1f * luck) * stats.getFatigueTerm();
// This makes spells that are easy to cast harder to resist and vice versa // This makes spells that are easy to cast harder to resist and vice versa

View file

@ -18,9 +18,13 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "actorutil.hpp"
#include "creaturestats.hpp"
#include "magiceffects.hpp" #include "magiceffects.hpp"
#include "stat.hpp"
namespace MWMechanics namespace MWMechanics
{ {
@ -74,12 +78,6 @@ namespace MWMechanics
} }
} }
} }
for (std::map<SpellKey, MagicEffects>::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it)
{
mEffects += it->second;
mSourcedEffects[it->first] += it->second;
}
} }
bool Spells::hasSpell(const std::string &spell) const bool Spells::hasSpell(const std::string &spell) const
@ -112,15 +110,6 @@ namespace MWMechanics
} }
} }
if (hasCorprusEffect(spell))
{
CorprusStats corprus;
corprus.mWorsenings = 0;
corprus.mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod;
mCorprusSpells[spell] = corprus;
}
SpellParams params; SpellParams params;
params.mEffectRands = random; params.mEffectRands = random;
mSpells.insert (std::make_pair (spell, params)); mSpells.insert (std::make_pair (spell, params));
@ -138,27 +127,6 @@ namespace MWMechanics
const ESM::Spell* spell = getSpell(spellId); const ESM::Spell* spell = getSpell(spellId);
TContainer::iterator iter = mSpells.find (spell); TContainer::iterator iter = mSpells.find (spell);
std::map<SpellKey, CorprusStats>::iterator corprusIt = mCorprusSpells.find(spell);
// if it's corprus, remove negative and keep positive effects
if (corprusIt != mCorprusSpells.end())
{
worsenCorprus(spell);
if (mPermanentSpellEffects.find(spell) != mPermanentSpellEffects.end())
{
MagicEffects & effects = mPermanentSpellEffects[spell];
for (MagicEffects::Collection::const_iterator effectIt = effects.begin(); effectIt != effects.end();)
{
const ESM::MagicEffect * magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->first.mId);
if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
effects.remove((effectIt++)->first);
else
++effectIt;
}
}
mCorprusSpells.erase(corprusIt);
}
if (iter!=mSpells.end()) if (iter!=mSpells.end())
{ {
mSpells.erase (iter); mSpells.erase (iter);
@ -371,31 +339,6 @@ namespace MWMechanics
} }
} }
void Spells::worsenCorprus(const ESM::Spell* spell)
{
mCorprusSpells[spell].mNextWorsening = MWBase::Environment::get().getWorld()->getTimeStamp() + CorprusStats::sWorseningPeriod;
mCorprusSpells[spell].mWorsenings++;
// update worsened effects
mPermanentSpellEffects[spell] = MagicEffects();
int i=0;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt, ++i)
{
const ESM::MagicEffect * magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
if ((effectIt->mEffectID != ESM::MagicEffect::Corprus) && (magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce))
{
float random = 1.f;
if (mSpells[spell].mEffectRands.find(i) != mSpells[spell].mEffectRands.end())
random = mSpells[spell].mEffectRands.at(i);
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random;
magnitude *= std::max(1, mCorprusSpells[spell].mWorsenings);
mPermanentSpellEffects[spell].add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(magnitude));
mSpellsChanged = true;
}
}
}
bool Spells::hasCorprusEffect(const ESM::Spell *spell) bool Spells::hasCorprusEffect(const ESM::Spell *spell)
{ {
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt) for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = spell->mEffects.mList.begin(); effectIt != spell->mEffects.mList.end(); ++effectIt)
@ -408,11 +351,6 @@ namespace MWMechanics
return false; return false;
} }
const std::map<Spells::SpellKey, Spells::CorprusStats> &Spells::getCorprusSpells() const
{
return mCorprusSpells;
}
void Spells::purgeEffect(int effectId) void Spells::purgeEffect(int effectId)
{ {
for (TContainer::iterator spellIt = mSpells.begin(); spellIt != mSpells.end(); ++spellIt) for (TContainer::iterator spellIt = mSpells.begin(); spellIt != mSpells.end(); ++spellIt)
@ -463,7 +401,7 @@ namespace MWMechanics
mUsedPowers[spell] = MWBase::Environment::get().getWorld()->getTimeStamp(); mUsedPowers[spell] = MWBase::Environment::get().getWorld()->getTimeStamp();
} }
void Spells::readState(const ESM::SpellState &state) void Spells::readState(const ESM::SpellState &state, CreatureStats* creatureStats)
{ {
for (ESM::SpellState::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it) for (ESM::SpellState::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it)
{ {
@ -487,6 +425,32 @@ namespace MWMechanics
mUsedPowers[spell] = MWWorld::TimeStamp(it->second); mUsedPowers[spell] = MWWorld::TimeStamp(it->second);
} }
for (std::map<std::string, ESM::SpellState::CorprusStats>::const_iterator it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it)
{
const ESM::Spell * spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(it->first);
if (!spell)
continue;
CorprusStats stats;
int worsening = state.mCorprusSpells.at(it->first).mWorsenings;
for (int i=0; i<ESM::Attribute::Length; ++i)
stats.mWorsenings[i] = 0;
for (auto& effect : spell->mEffects.mList)
{
if (effect.mEffectID == ESM::MagicEffect::DrainAttribute)
stats.mWorsenings[effect.mAttribute] = worsening;
}
stats.mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening);
creatureStats->addCorprusSpell(it->first, stats);
}
mSpellsChanged = true;
// Permanent effects are used only to keep the custom magnitude of corprus spells effects (after cure too), and only in old saves. Convert data to the new approach.
for (std::map<std::string, std::vector<ESM::SpellState::PermanentSpellEffectInfo> >::const_iterator it = for (std::map<std::string, std::vector<ESM::SpellState::PermanentSpellEffectInfo> >::const_iterator it =
state.mPermanentSpellEffects.begin(); it != state.mPermanentSpellEffects.end(); ++it) state.mPermanentSpellEffects.begin(); it != state.mPermanentSpellEffects.end(); ++it)
{ {
@ -494,24 +458,31 @@ namespace MWMechanics
if (!spell) if (!spell)
continue; continue;
mPermanentSpellEffects[spell] = MagicEffects(); // Import data only for player, other actors should not suffer from corprus worsening.
MWWorld::Ptr player = getPlayer();
if (creatureStats->getActorId() != player.getClass().getCreatureStats(player).getActorId())
return;
// Note: if target actor has the Restore attirbute effects, stats will be restored.
for (std::vector<ESM::SpellState::PermanentSpellEffectInfo>::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt) for (std::vector<ESM::SpellState::PermanentSpellEffectInfo>::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt)
{ {
mPermanentSpellEffects[spell].add(EffectKey(effectIt->mId, effectIt->mArg), effectIt->mMagnitude); // Applied corprus effects are already in loaded stats modifiers
} if (effectIt->mId == ESM::MagicEffect::FortifyAttribute)
}
mCorprusSpells.clear();
for (std::map<std::string, ESM::SpellState::CorprusStats>::const_iterator it = state.mCorprusSpells.begin(); it != state.mCorprusSpells.end(); ++it)
{ {
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(it->first); AttributeValue attr = creatureStats->getAttribute(effectIt->mArg);
if (!spell) // Discard unavailable corprus spells attr.setModifier(attr.getModifier() - effectIt->mMagnitude);
continue; attr.damage(-effectIt->mMagnitude);
mCorprusSpells[spell].mWorsenings = state.mCorprusSpells.at(it->first).mWorsenings; creatureStats->setAttribute(effectIt->mArg, attr);
mCorprusSpells[spell].mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening); }
else if (effectIt->mId == ESM::MagicEffect::DrainAttribute)
{
AttributeValue attr = creatureStats->getAttribute(effectIt->mArg);
attr.setModifier(attr.getModifier() + effectIt->mMagnitude);
attr.damage(effectIt->mMagnitude);
creatureStats->setAttribute(effectIt->mArg, attr);
}
}
} }
mSpellsChanged = true;
} }
void Spells::writeState(ESM::SpellState &state) const void Spells::writeState(ESM::SpellState &state) const
@ -528,26 +499,5 @@ namespace MWMechanics
for (std::map<SpellKey, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it) for (std::map<SpellKey, MWWorld::TimeStamp>::const_iterator it = mUsedPowers.begin(); it != mUsedPowers.end(); ++it)
state.mUsedPowers[it->first->mId] = it->second.toEsm(); state.mUsedPowers[it->first->mId] = it->second.toEsm();
for (std::map<SpellKey, MagicEffects>::const_iterator it = mPermanentSpellEffects.begin(); it != mPermanentSpellEffects.end(); ++it)
{
std::vector<ESM::SpellState::PermanentSpellEffectInfo> effectList;
for (MagicEffects::Collection::const_iterator effectIt = it->second.begin(); effectIt != it->second.end(); ++effectIt)
{
ESM::SpellState::PermanentSpellEffectInfo info;
info.mId = effectIt->first.mId;
info.mArg = effectIt->first.mArg;
info.mMagnitude = effectIt->second.getModifier();
effectList.push_back(info);
}
state.mPermanentSpellEffects[it->first->mId] = effectList;
}
for (std::map<SpellKey, CorprusStats>::const_iterator it = mCorprusSpells.begin(); it != mCorprusSpells.end(); ++it)
{
state.mCorprusSpells[it->first->mId].mWorsenings = mCorprusSpells.at(it->first).mWorsenings;
state.mCorprusSpells[it->first->mId].mNextWorsening = mCorprusSpells.at(it->first).mNextWorsening.toEsm();
}
} }
} }

View file

@ -22,6 +22,8 @@ namespace ESM
namespace MWMechanics namespace MWMechanics
{ {
class CreatureStats;
class MagicEffects; class MagicEffects;
/// \brief Spell list /// \brief Spell list
@ -33,7 +35,8 @@ namespace MWMechanics
public: public:
typedef const ESM::Spell* SpellKey; typedef const ESM::Spell* SpellKey;
struct SpellParams { struct SpellParams
{
std::map<int, float> mEffectRands; // <effect index, normalised random magnitude> std::map<int, float> mEffectRands; // <effect index, normalised random magnitude>
std::set<int> mPurgedEffects; // indices of purged effects std::set<int> mPurgedEffects; // indices of purged effects
}; };
@ -41,27 +44,14 @@ namespace MWMechanics
typedef std::map<SpellKey, SpellParams> TContainer; typedef std::map<SpellKey, SpellParams> TContainer;
typedef TContainer::const_iterator TIterator; typedef TContainer::const_iterator TIterator;
struct CorprusStats
{
static const int sWorseningPeriod = 24;
int mWorsenings;
MWWorld::TimeStamp mNextWorsening;
};
private: private:
TContainer mSpells; TContainer mSpells;
// spell-tied effects that will be applied even after removing the spell (currently used to keep positive effects when corprus is removed)
std::map<SpellKey, MagicEffects> mPermanentSpellEffects;
// Note: this is the spell that's about to be cast, *not* the spell selected in the GUI (which may be different) // Note: this is the spell that's about to be cast, *not* the spell selected in the GUI (which may be different)
std::string mSelectedSpell; std::string mSelectedSpell;
std::map<SpellKey, MWWorld::TimeStamp> mUsedPowers; std::map<SpellKey, MWWorld::TimeStamp> mUsedPowers;
std::map<SpellKey, CorprusStats> mCorprusSpells;
mutable bool mSpellsChanged; mutable bool mSpellsChanged;
mutable MagicEffects mEffects; mutable MagicEffects mEffects;
mutable std::map<SpellKey, MagicEffects> mSourcedEffects; mutable std::map<SpellKey, MagicEffects> mSourcedEffects;
@ -73,9 +63,7 @@ namespace MWMechanics
public: public:
Spells(); Spells();
void worsenCorprus(const ESM::Spell* spell);
static bool hasCorprusEffect(const ESM::Spell *spell); static bool hasCorprusEffect(const ESM::Spell *spell);
const std::map<SpellKey, CorprusStats> & getCorprusSpells() const;
void purgeEffect(int effectId); void purgeEffect(int effectId);
void purgeEffect(int effectId, const std::string & sourceId); void purgeEffect(int effectId, const std::string & sourceId);
@ -128,7 +116,7 @@ namespace MWMechanics
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const; void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;
void readState (const ESM::SpellState& state); void readState (const ESM::SpellState& state, CreatureStats* creatureStats);
void writeState (ESM::SpellState& state) const; void writeState (ESM::SpellState& state) const;
}; };
} }

View file

@ -94,8 +94,8 @@ namespace MWMechanics
CreatureStats& stats = actor.getClass().getCreatureStats(actor); CreatureStats& stats = actor.getClass().getCreatureStats(actor);
int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); float actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck); float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck);

View file

@ -227,39 +227,46 @@ namespace MWMechanics
} }
AttributeValue::AttributeValue() : AttributeValue::AttributeValue() :
mBase(0), mModifier(0), mDamage(0) mBase(0.f), mModifier(0.f), mDamage(0.f)
{ {
} }
int AttributeValue::getModified() const float AttributeValue::getModified() const
{ {
return std::max(0, mBase - (int) mDamage + mModifier); return std::max(0.f, mBase - mDamage + mModifier);
} }
int AttributeValue::getBase() const float AttributeValue::getBase() const
{ {
return mBase; return mBase;
} }
int AttributeValue::getModifier() const float AttributeValue::getModifier() const
{ {
return mModifier; return mModifier;
} }
void AttributeValue::setBase(int base) void AttributeValue::setBase(float base)
{ {
mBase = base; mBase = base;
} }
void AttributeValue::setModifier(int mod) void AttributeValue::setModifier(float mod)
{ {
mModifier = mod; mModifier = mod;
} }
void AttributeValue::damage(float damage) void AttributeValue::damage(float damage)
{ {
mDamage += std::min(damage, (float)getModified()); float threshold = mBase + mModifier;
if (mDamage + damage > threshold)
mDamage = threshold;
else
mDamage += damage;
} }
void AttributeValue::restore(float amount) void AttributeValue::restore(float amount)
{ {
if (mDamage <= 0) return;
mDamage -= std::min(mDamage, amount); mDamage -= std::min(mDamage, amount);
} }
@ -268,14 +275,14 @@ namespace MWMechanics
return mDamage; return mDamage;
} }
void AttributeValue::writeState (ESM::StatState<int>& state) const void AttributeValue::writeState (ESM::StatState<float>& state) const
{ {
state.mBase = mBase; state.mBase = mBase;
state.mMod = mModifier; state.mMod = mModifier;
state.mDamage = mDamage; state.mDamage = mDamage;
} }
void AttributeValue::readState (const ESM::StatState<int>& state) void AttributeValue::readState (const ESM::StatState<float>& state)
{ {
mBase = state.mBase; mBase = state.mBase;
mModifier = state.mMod; mModifier = state.mMod;
@ -296,13 +303,13 @@ namespace MWMechanics
mProgress = progress; mProgress = progress;
} }
void SkillValue::writeState (ESM::StatState<int>& state) const void SkillValue::writeState (ESM::StatState<float>& state) const
{ {
AttributeValue::writeState (state); AttributeValue::writeState (state);
state.mProgress = mProgress; state.mProgress = mProgress;
} }
void SkillValue::readState (const ESM::StatState<int>& state) void SkillValue::readState (const ESM::StatState<float>& state)
{ {
AttributeValue::readState (state); AttributeValue::readState (state);
mProgress = state.mProgress; mProgress = state.mProgress;

View file

@ -122,20 +122,20 @@ namespace MWMechanics
class AttributeValue class AttributeValue
{ {
int mBase; float mBase;
int mModifier; float mModifier;
float mDamage; // needs to be float to allow continuous damage float mDamage; // needs to be float to allow continuous damage
public: public:
AttributeValue(); AttributeValue();
int getModified() const; float getModified() const;
int getBase() const; float getBase() const;
int getModifier() const; float getModifier() const;
void setBase(int base); void setBase(float base);
void setModifier(int mod); void setModifier(float mod);
// Maximum attribute damage is limited to the modified value. // Maximum attribute damage is limited to the modified value.
// Note: I think MW applies damage directly to mModified, since you can also // Note: I think MW applies damage directly to mModified, since you can also
@ -145,8 +145,8 @@ namespace MWMechanics
float getDamage() const; float getDamage() const;
void writeState (ESM::StatState<int>& state) const; void writeState (ESM::StatState<float>& state) const;
void readState (const ESM::StatState<int>& state); void readState (const ESM::StatState<float>& state);
}; };
class SkillValue : public AttributeValue class SkillValue : public AttributeValue
@ -157,8 +157,8 @@ namespace MWMechanics
float getProgress() const; float getProgress() const;
void setProgress(float progress); void setProgress(float progress);
void writeState (ESM::StatState<int>& state) const; void writeState (ESM::StatState<float>& state) const;
void readState (const ESM::StatState<int>& state); void readState (const ESM::StatState<float>& state);
}; };
inline bool operator== (const AttributeValue& left, const AttributeValue& right) inline bool operator== (const AttributeValue& left, const AttributeValue& right)

View file

@ -0,0 +1,28 @@
#ifndef GAME_MWMECHANICS_TYPEDAIPACKAGE_H
#define GAME_MWMECHANICS_TYPEDAIPACKAGE_H
#include "aipackage.hpp"
namespace MWMechanics
{
template <class T>
struct TypedAiPackage : public AiPackage
{
TypedAiPackage() :
AiPackage(T::getTypeId(), T::makeDefaultOptions()) {}
TypedAiPackage(const Options& options) :
AiPackage(T::getTypeId(), options) {}
template <class Derived>
TypedAiPackage(Derived*) :
AiPackage(Derived::getTypeId(), Derived::makeDefaultOptions()) {}
virtual std::unique_ptr<AiPackage> clone() const override
{
return std::make_unique<T>(*static_cast<const T*>(this));
}
};
}
#endif

View file

@ -20,6 +20,7 @@ namespace mwmp
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
/*
if (worldstate.time.hour != -1) if (worldstate.time.hour != -1)
world->setHour(worldstate.time.hour); world->setHour(worldstate.time.hour);
@ -37,6 +38,7 @@ namespace mwmp
if (worldstate.time.timeScale != -1) if (worldstate.time.timeScale != -1)
world->setTimeScale(worldstate.time.timeScale); world->setTimeScale(worldstate.time.timeScale);
*/
} }
}; };
} }

View file

@ -0,0 +1,104 @@
#include "fogmanager.hpp"
#include <algorithm>
#include <components/esm/loadcell.hpp>
#include <components/fallback/fallback.hpp>
#include <components/sceneutil/util.hpp>
#include <components/settings/settings.hpp>
namespace
{
float DLLandFogStart;
float DLLandFogEnd;
float DLUnderwaterFogStart;
float DLUnderwaterFogEnd;
float DLInteriorFogStart;
float DLInteriorFogEnd;
}
namespace MWRender
{
FogManager::FogManager()
: mLandFogStart(0.f)
, mLandFogEnd(std::numeric_limits<float>::max())
, mUnderwaterFogStart(0.f)
, mUnderwaterFogEnd(std::numeric_limits<float>::max())
, mFogColor(osg::Vec4f())
, mDistantFog(Settings::Manager::getBool("use distant fog", "Fog"))
, mUnderwaterColor(Fallback::Map::getColour("Water_UnderwaterColor"))
, mUnderwaterWeight(Fallback::Map::getFloat("Water_UnderwaterColorWeight"))
, mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog"))
{
DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog");
DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog");
DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog");
DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog");
DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog");
DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog");
}
void FogManager::configure(float viewDistance, const ESM::Cell *cell)
{
osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog);
if (mDistantFog)
{
float density = std::max(0.2f, cell->mAmbi.mFogDensity);
mLandFogStart = DLInteriorFogEnd * (1.0f - density) + DLInteriorFogStart*density;
mLandFogEnd = DLInteriorFogEnd;
mUnderwaterFogStart = DLUnderwaterFogStart;
mUnderwaterFogEnd = DLUnderwaterFogEnd;
mFogColor = color;
}
else
configure(viewDistance, cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color);
}
void FogManager::configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color)
{
if (mDistantFog)
{
mLandFogStart = dlFactor * (DLLandFogStart - dlOffset * DLLandFogEnd);
mLandFogEnd = dlFactor * (1.0f - dlOffset) * DLLandFogEnd;
mUnderwaterFogStart = DLUnderwaterFogStart;
mUnderwaterFogEnd = DLUnderwaterFogEnd;
}
else
{
if (fogDepth == 0.0)
{
mLandFogStart = 0.0f;
mLandFogEnd = std::numeric_limits<float>::max();
}
else
{
mLandFogStart = viewDistance * (1 - fogDepth);
mLandFogEnd = viewDistance;
}
mUnderwaterFogStart = std::min(viewDistance, 6666.f) * (1 - underwaterFog);
mUnderwaterFogEnd = std::min(viewDistance, 6666.f);
}
mFogColor = color;
}
float FogManager::getFogStart(bool isUnderwater) const
{
return isUnderwater ? mUnderwaterFogStart : mLandFogStart;
}
float FogManager::getFogEnd(bool isUnderwater) const
{
return isUnderwater ? mUnderwaterFogEnd : mLandFogEnd;
}
osg::Vec4f FogManager::getFogColor(bool isUnderwater) const
{
if (isUnderwater)
{
return mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight);
}
return mFogColor;
}
}

View file

@ -0,0 +1,39 @@
#ifndef OPENMW_MWRENDER_FOGMANAGER_H
#define OPENMW_MWRENDER_FOGMANAGER_H
#include <osg/Vec4f>
namespace ESM
{
struct Cell;
}
namespace MWRender
{
class FogManager
{
public:
FogManager();
void configure(float viewDistance, const ESM::Cell *cell);
void configure(float viewDistance, float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color);
osg::Vec4f getFogColor(bool isUnderwater) const;
float getFogStart(bool isUnderwater) const;
float getFogEnd(bool isUnderwater) const;
private:
float mLandFogStart;
float mLandFogEnd;
float mUnderwaterFogStart;
float mUnderwaterFogEnd;
osg::Vec4f mFogColor;
bool mDistantFog;
osg::Vec4f mUnderwaterColor;
float mUnderwaterWeight;
float mUnderwaterIndoorFog;
};
}
#endif

View file

@ -69,16 +69,7 @@
#include "navmesh.hpp" #include "navmesh.hpp"
#include "actorspaths.hpp" #include "actorspaths.hpp"
#include "recastmesh.hpp" #include "recastmesh.hpp"
#include "fogmanager.hpp"
namespace
{
float DLLandFogStart;
float DLLandFogEnd;
float DLUnderwaterFogStart;
float DLUnderwaterFogEnd;
float DLInteriorFogStart;
float DLInteriorFogEnd;
}
namespace MWRender namespace MWRender
{ {
@ -204,19 +195,9 @@ namespace MWRender
, mWorkQueue(workQueue) , mWorkQueue(workQueue)
, mUnrefQueue(new SceneUtil::UnrefQueue) , mUnrefQueue(new SceneUtil::UnrefQueue)
, mNavigator(navigator) , mNavigator(navigator)
, mLandFogStart(0.f)
, mLandFogEnd(std::numeric_limits<float>::max())
, mUnderwaterFogStart(0.f)
, mUnderwaterFogEnd(std::numeric_limits<float>::max())
, mUnderwaterColor(Fallback::Map::getColour("Water_UnderwaterColor"))
, mUnderwaterWeight(Fallback::Map::getFloat("Water_UnderwaterColorWeight"))
, mUnderwaterIndoorFog(Fallback::Map::getFloat("Water_UnderwaterIndoorFog"))
, mNightEyeFactor(0.f) , mNightEyeFactor(0.f)
, mDistantFog(false)
, mDistantTerrain(false)
, mFieldOfViewOverridden(false) , mFieldOfViewOverridden(false)
, mFieldOfViewOverride(0.f) , mFieldOfViewOverride(0.f)
, mBorders(false)
{ {
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem); resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders"); resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
@ -284,16 +265,6 @@ namespace MWRender
mEffectManager.reset(new EffectManager(sceneRoot, mResourceSystem)); mEffectManager.reset(new EffectManager(sceneRoot, mResourceSystem));
DLLandFogStart = Settings::Manager::getFloat("distant land fog start", "Fog");
DLLandFogEnd = Settings::Manager::getFloat("distant land fog end", "Fog");
DLUnderwaterFogStart = Settings::Manager::getFloat("distant underwater fog start", "Fog");
DLUnderwaterFogEnd = Settings::Manager::getFloat("distant underwater fog end", "Fog");
DLInteriorFogStart = Settings::Manager::getFloat("distant interior fog start", "Fog");
DLInteriorFogEnd = Settings::Manager::getFloat("distant interior fog end", "Fog");
mDistantFog = Settings::Manager::getBool("use distant fog", "Fog");
mDistantTerrain = Settings::Manager::getBool("distant terrain", "Terrain");
const std::string normalMapPattern = Settings::Manager::getString("normal map pattern", "Shaders"); const std::string normalMapPattern = Settings::Manager::getString("normal map pattern", "Shaders");
const std::string heightMapPattern = Settings::Manager::getString("normal height map pattern", "Shaders"); const std::string heightMapPattern = Settings::Manager::getString("normal height map pattern", "Shaders");
const std::string specularMapPattern = Settings::Manager::getString("terrain specular map pattern", "Shaders"); const std::string specularMapPattern = Settings::Manager::getString("terrain specular map pattern", "Shaders");
@ -302,7 +273,7 @@ namespace MWRender
mTerrainStorage = new TerrainStorage(mResourceSystem, normalMapPattern, heightMapPattern, useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps); mTerrainStorage = new TerrainStorage(mResourceSystem, normalMapPattern, heightMapPattern, useTerrainNormalMaps, specularMapPattern, useTerrainSpecularMaps);
if (mDistantTerrain) if (Settings::Manager::getBool("distant terrain", "Terrain"))
{ {
const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain"); const int compMapResolution = Settings::Manager::getInt("composite map resolution", "Terrain");
int compMapPower = Settings::Manager::getInt("composite map level", "Terrain"); int compMapPower = Settings::Manager::getInt("composite map level", "Terrain");
@ -349,8 +320,9 @@ namespace MWRender
defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f)); defaultMat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.f, 0.f, 0.f, 0.f));
sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat); sceneRoot->getOrCreateStateSet()->setAttribute(defaultMat);
mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager())); mFog.reset(new FogManager());
mSky.reset(new SkyManager(sceneRoot, resourceSystem->getSceneManager()));
mSky->setCamera(mViewer->getCamera()); mSky->setCamera(mViewer->getCamera());
mSky->setRainIntensityUniform(mWater->getRainIntensityUniform()); mSky->setRainIntensityUniform(mWater->getRainIntensityUniform());
@ -558,9 +530,9 @@ namespace MWRender
bool RenderingManager::toggleBorders() bool RenderingManager::toggleBorders()
{ {
mBorders = !mBorders; bool borders = !mTerrain->getBordersVisible();
mTerrain->setBordersVisible(mBorders); mTerrain->setBordersVisible(borders);
return mBorders; return borders;
} }
bool RenderingManager::toggleRenderMode(RenderMode mode) bool RenderingManager::toggleRenderMode(RenderMode mode)
@ -606,46 +578,12 @@ namespace MWRender
void RenderingManager::configureFog(const ESM::Cell *cell) void RenderingManager::configureFog(const ESM::Cell *cell)
{ {
osg::Vec4f color = SceneUtil::colourFromRGB(cell->mAmbi.mFog); mFog->configure(mViewDistance, cell);
if(mDistantFog)
{
float density = std::max(0.2f, cell->mAmbi.mFogDensity);
mLandFogStart = (DLInteriorFogEnd*(1.0f-density) + DLInteriorFogStart*density);
mLandFogEnd = DLInteriorFogEnd;
mUnderwaterFogStart = DLUnderwaterFogStart;
mUnderwaterFogEnd = DLUnderwaterFogEnd;
mFogColor = color;
}
else
configureFog(cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color);
} }
void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color) void RenderingManager::configureFog(float fogDepth, float underwaterFog, float dlFactor, float dlOffset, const osg::Vec4f &color)
{ {
if(mDistantFog) mFog->configure(mViewDistance, fogDepth, underwaterFog, dlFactor, dlOffset, color);
{
mLandFogStart = dlFactor * (DLLandFogStart - dlOffset*DLLandFogEnd);
mLandFogEnd = dlFactor * (1.0f-dlOffset) * DLLandFogEnd;
mUnderwaterFogStart = DLUnderwaterFogStart;
mUnderwaterFogEnd = DLUnderwaterFogEnd;
}
else
{
if(fogDepth == 0.0)
{
mLandFogStart = 0.0f;
mLandFogEnd = std::numeric_limits<float>::max();
}
else
{
mLandFogStart = mViewDistance * (1 - fogDepth);
mLandFogEnd = mViewDistance;
}
mUnderwaterFogStart = std::min(mViewDistance, 6666.f) * (1 - underwaterFog);
mUnderwaterFogEnd = std::min(mViewDistance, 6666.f);
}
mFogColor = color;
} }
SkyManager* RenderingManager::getSkyManager() SkyManager* RenderingManager::getSkyManager()
@ -674,19 +612,11 @@ namespace MWRender
osg::Vec3f focal, cameraPos; osg::Vec3f focal, cameraPos;
mCamera->getPosition(focal, cameraPos); mCamera->getPosition(focal, cameraPos);
mCurrentCameraPos = cameraPos; mCurrentCameraPos = cameraPos;
if (mWater->isUnderwater(cameraPos))
{
setFogColor(mUnderwaterColor * mUnderwaterWeight + mFogColor * (1.f-mUnderwaterWeight));
mStateUpdater->setFogStart(mUnderwaterFogStart);
mStateUpdater->setFogEnd(mUnderwaterFogEnd);
}
else
{
setFogColor(mFogColor);
mStateUpdater->setFogStart(mLandFogStart); bool isUnderwater = mWater->isUnderwater(cameraPos);
mStateUpdater->setFogEnd(mLandFogEnd); mStateUpdater->setFogStart(mFog->getFogStart(isUnderwater));
} mStateUpdater->setFogEnd(mFog->getFogEnd(isUnderwater));
setFogColor(mFog->getFogColor(isUnderwater));
} }
void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr) void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr)
@ -1335,7 +1265,7 @@ namespace MWRender
else if (it->first == "Camera" && it->second == "viewing distance") else if (it->first == "Camera" && it->second == "viewing distance")
{ {
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera"); mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
if(!mDistantFog) if(!Settings::Manager::getBool("use distant fog", "Fog"))
mStateUpdater->setFogEnd(mViewDistance); mStateUpdater->setFogEnd(mViewDistance);
updateProjectionMatrix(); updateProjectionMatrix();
} }

View file

@ -73,6 +73,7 @@ namespace MWRender
class StateUpdater; class StateUpdater;
class EffectManager; class EffectManager;
class FogManager;
class SkyManager; class SkyManager;
class NpcAnimation; class NpcAnimation;
class Pathgrid; class Pathgrid;
@ -275,6 +276,7 @@ namespace MWRender
std::unique_ptr<Terrain::World> mTerrain; std::unique_ptr<Terrain::World> mTerrain;
TerrainStorage* mTerrainStorage; TerrainStorage* mTerrainStorage;
std::unique_ptr<SkyManager> mSky; std::unique_ptr<SkyManager> mSky;
std::unique_ptr<FogManager> mFog;
std::unique_ptr<EffectManager> mEffectManager; std::unique_ptr<EffectManager> mEffectManager;
std::unique_ptr<SceneUtil::ShadowManager> mShadowManager; std::unique_ptr<SceneUtil::ShadowManager> mShadowManager;
osg::ref_ptr<NpcAnimation> mPlayerAnimation; osg::ref_ptr<NpcAnimation> mPlayerAnimation;
@ -284,27 +286,15 @@ namespace MWRender
osg::ref_ptr<StateUpdater> mStateUpdater; osg::ref_ptr<StateUpdater> mStateUpdater;
float mLandFogStart;
float mLandFogEnd;
float mUnderwaterFogStart;
float mUnderwaterFogEnd;
osg::Vec4f mUnderwaterColor;
float mUnderwaterWeight;
float mUnderwaterIndoorFog;
osg::Vec4f mFogColor;
osg::Vec4f mAmbientColor; osg::Vec4f mAmbientColor;
float mNightEyeFactor; float mNightEyeFactor;
float mNearClip; float mNearClip;
float mViewDistance; float mViewDistance;
bool mDistantFog : 1; bool mFieldOfViewOverridden;
bool mDistantTerrain : 1;
bool mFieldOfViewOverridden : 1;
float mFieldOfViewOverride; float mFieldOfViewOverride;
float mFieldOfView; float mFieldOfView;
float mFirstPersonFieldOfView; float mFirstPersonFieldOfView;
bool mBorders;
void operator = (const RenderingManager&); void operator = (const RenderingManager&);
RenderingManager(const RenderingManager&); RenderingManager(const RenderingManager&);

View file

@ -271,8 +271,10 @@ namespace MWScript
Interpreter::Type_Integer value = runtime[0].mInteger; Interpreter::Type_Integer value = runtime[0].mInteger;
runtime.pop(); runtime.pop();
ptr.getClass().getCreatureStats (ptr).setAiSetting (mIndex, int modified = ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getBase() + value;
ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getBase() + value);
ptr.getClass().getCreatureStats (ptr).setAiSetting (mIndex, modified);
ptr.getClass().setBaseAISetting(ptr.getCellRef().getRefId(), mIndex, modified);
} }
}; };
template<class R> template<class R>
@ -302,7 +304,7 @@ namespace MWScript
*/ */
stat.setModified(value, 0); stat.setModified(value, 0);
ptr.getClass().getCreatureStats(ptr).setAiSetting(mIndex, stat); ptr.getClass().setBaseAISetting(ptr.getCellRef().getRefId(), mIndex, value);
/* /*
Start of tes3mp addition Start of tes3mp addition

View file

@ -106,7 +106,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = Interpreter::Type_Float value =
ptr.getClass() ptr.getClass()
.getCreatureStats (ptr) .getCreatureStats (ptr)
.getAttribute(mIndex) .getAttribute(mIndex)
@ -129,7 +129,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = runtime[0].mInteger; Interpreter::Type_Float value = runtime[0].mFloat;
runtime.pop(); runtime.pop();
MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex);
@ -151,7 +151,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = runtime[0].mInteger; Interpreter::Type_Float value = runtime[0].mFloat;
runtime.pop(); runtime.pop();
MWMechanics::AttributeValue attribute = ptr.getClass() MWMechanics::AttributeValue attribute = ptr.getClass()
@ -166,9 +166,9 @@ namespace MWScript
return; return;
if (value < 0) if (value < 0)
attribute.setBase(std::max(0, attribute.getBase() + value)); attribute.setBase(std::max(0.f, attribute.getBase() + value));
else else
attribute.setBase(std::min(100, attribute.getBase() + value)); attribute.setBase(std::min(100.f, attribute.getBase() + value));
ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute);
} }
@ -356,7 +356,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = ptr.getClass().getSkill(ptr, mIndex); Interpreter::Type_Float value = ptr.getClass().getSkill(ptr, mIndex);
runtime.push (value); runtime.push (value);
} }
@ -375,7 +375,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = runtime[0].mInteger; Interpreter::Type_Float value = runtime[0].mFloat;
runtime.pop(); runtime.pop();
MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr); MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr);
@ -397,7 +397,7 @@ namespace MWScript
{ {
MWWorld::Ptr ptr = R()(runtime); MWWorld::Ptr ptr = R()(runtime);
Interpreter::Type_Integer value = runtime[0].mInteger; Interpreter::Type_Float value = runtime[0].mFloat;
runtime.pop(); runtime.pop();
MWMechanics::SkillValue &skill = ptr.getClass() MWMechanics::SkillValue &skill = ptr.getClass()
@ -407,14 +407,14 @@ namespace MWScript
if (value == 0) if (value == 0)
return; return;
if (((skill.getBase() <= 0) && (value < 0)) if (((skill.getBase() <= 0.f) && (value < 0.f))
|| ((skill.getBase() >= 100) && (value > 0))) || ((skill.getBase() >= 100.f) && (value > 0.f)))
return; return;
if (value < 0) if (value < 0)
skill.setBase(std::max(0, skill.getBase() + value)); skill.setBase(std::max(0.f, skill.getBase() + value));
else else
skill.setBase(std::min(100, skill.getBase() + value)); skill.setBase(std::min(100.f, skill.getBase() + value));
} }
}; };
@ -519,18 +519,20 @@ namespace MWScript
std::string id = runtime.getStringLiteral (runtime[0].mInteger); std::string id = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);
/* /*
Start of tes3mp change (major) Start of tes3mp change (major)
Only remove the spell if the target has it Only remove the spell if the target has it
Send an ID_PLAYER_SPELLBOOK packet every time a player loses a spell here
*/ */
MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);
MWMechanics::Spells& spells = creatureStats.getSpells(); MWMechanics::Spells& spells = creatureStats.getSpells();
if (spells.hasSpell(id)) if (!spells.hasSpell(id)) return;
{ /*
End of tes3mp change (major)
*/
// The spell may have an instant effect which must be handled before the spell's removal. // The spell may have an instant effect which must be handled before the spell's removal.
for (const auto& effect : creatureStats.getSpells().getMagicEffects()) for (const auto& effect : creatureStats.getSpells().getMagicEffects())
{ {
@ -541,19 +543,24 @@ namespace MWScript
creatureStats.getSpells().purgeEffect(effect.first.mId); creatureStats.getSpells().purgeEffect(effect.first.mId);
} }
MWBase::Environment::get().getMechanicsManager()->restoreStatsAfterCorprus(ptr, id);
creatureStats.getSpells().remove (id); creatureStats.getSpells().remove (id);
if (ptr == MWMechanics::getPlayer())
{
MWBase::WindowManager* wm = MWBase::Environment::get().getWindowManager(); MWBase::WindowManager* wm = MWBase::Environment::get().getWindowManager();
if (id == wm->getSelectedSpell()) if (ptr == MWMechanics::getPlayer() &&
id == wm->getSelectedSpell())
{
wm->unsetSelectedSpell(); wm->unsetSelectedSpell();
}
/*
Start of tes3mp change (major)
Send an ID_PLAYER_SPELLBOOK packet every time a player loses a spell here
*/
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn()) if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
mwmp::Main::get().getLocalPlayer()->sendSpellChange(id, mwmp::SpellbookChanges::REMOVE); mwmp::Main::get().getLocalPlayer()->sendSpellChange(id, mwmp::SpellbookChanges::REMOVE);
}
}
/* /*
End of tes3mp change (major) End of tes3mp change (major)
*/ */

View file

@ -147,10 +147,11 @@ namespace MWSound
volume = static_cast<float>(pow(10.0, (sound->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0)); volume = static_cast<float>(pow(10.0, (sound->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0));
min = sound->mData.mMinRange; min = sound->mData.mMinRange;
max = sound->mData.mMaxRange; max = sound->mData.mMaxRange;
if (min == 0) if (min == 0 && max == 0)
{
min = fAudioDefaultMinDistance; min = fAudioDefaultMinDistance;
if (max == 0)
max = fAudioDefaultMaxDistance; max = fAudioDefaultMaxDistance;
}
min *= fAudioMinDistanceMult; min *= fAudioMinDistanceMult;
max *= fAudioMaxDistanceMult; max *= fAudioMaxDistanceMult;

View file

@ -213,11 +213,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
profile.mPlayerClassId = classId; profile.mPlayerClassId = classId;
profile.mPlayerCell = world.getCellName(); profile.mPlayerCell = world.getCellName();
profile.mInGameTime = world.getEpochTimeStamp();
profile.mInGameTime.mGameHour = world.getTimeStamp().getHour();
profile.mInGameTime.mDay = world.getDay();
profile.mInGameTime.mMonth = world.getMonth();
profile.mInGameTime.mYear = world.getYear();
profile.mTimePlayed = mTimePlayed; profile.mTimePlayed = mTimePlayed;
profile.mDescription = description; profile.mDescription = description;
@ -464,6 +460,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
case ESM::REC_ENAB: case ESM::REC_ENAB:
case ESM::REC_LEVC: case ESM::REC_LEVC:
case ESM::REC_LEVI: case ESM::REC_LEVI:
case ESM::REC_CREA:
MWBase::Environment::get().getWorld()->readRecord(reader, n.intval, contentFileMap); MWBase::Environment::get().getWorld()->readRecord(reader, n.intval, contentFileMap);
break; break;

View file

@ -434,7 +434,7 @@ namespace MWWorld
return canSwim(ptr) || canWalk(ptr) || canFly(ptr); return canSwim(ptr) || canWalk(ptr) || canFly(ptr);
} }
int Class::getSkill(const MWWorld::Ptr& ptr, int skill) const float Class::getSkill(const MWWorld::Ptr& ptr, int skill) const
{ {
throw std::runtime_error("class does not support skills"); throw std::runtime_error("class does not support skills");
} }
@ -529,4 +529,9 @@ namespace MWWorld
result.z() = magicEffect->mData.mBlue / 255.f; result.z() = magicEffect->mData.mBlue / 255.f;
return result; return result;
} }
void Class::setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const
{
throw std::runtime_error ("class does not have creature stats");
}
} }

View file

@ -10,6 +10,7 @@
#include "ptr.hpp" #include "ptr.hpp"
#include "doorstate.hpp" #include "doorstate.hpp"
#include "../mwmechanics/creaturestats.hpp"
namespace ESM namespace ESM
{ {
@ -28,7 +29,6 @@ namespace MWPhysics
namespace MWMechanics namespace MWMechanics
{ {
class CreatureStats;
class NpcStats; class NpcStats;
struct Movement; struct Movement;
} }
@ -332,7 +332,7 @@ namespace MWWorld
bool isPureLandCreature(const MWWorld::Ptr& ptr) const; bool isPureLandCreature(const MWWorld::Ptr& ptr) const;
bool isMobile(const MWWorld::Ptr& ptr) const; bool isMobile(const MWWorld::Ptr& ptr) const;
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const; const;
@ -371,6 +371,8 @@ namespace MWWorld
virtual float getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const; virtual float getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const;
virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const; virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const;
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
}; };
} }

View file

@ -0,0 +1,266 @@
#include "datetimemanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "esmstore.hpp"
#include "globals.hpp"
#include "timestamp.hpp"
namespace
{
static int getDaysPerMonth(int month)
{
switch (month)
{
case 0: return 31;
case 1: return 28;
case 2: return 31;
case 3: return 30;
case 4: return 31;
case 5: return 30;
case 6: return 31;
case 7: return 31;
case 8: return 30;
case 9: return 31;
case 10: return 30;
case 11: return 31;
}
throw std::runtime_error ("month out of range");
}
}
namespace MWWorld
{
void DateTimeManager::setup(Globals& globalVariables)
{
mGameHour = globalVariables["gamehour"].getFloat();
mDaysPassed = globalVariables["dayspassed"].getInteger();
mDay = globalVariables["day"].getInteger();
mMonth = globalVariables["month"].getInteger();
mYear = globalVariables["year"].getInteger();
mTimeScale = globalVariables["timescale"].getFloat();
}
void DateTimeManager::setHour(double hour)
{
if (hour < 0)
hour = 0;
int days = static_cast<int>(hour / 24);
hour = std::fmod(hour, 24);
mGameHour = static_cast<float>(hour);
if (days > 0)
setDay(days + mDay);
}
void DateTimeManager::setDay(int day)
{
if (day < 1)
day = 1;
int month = mMonth;
while (true)
{
int days = getDaysPerMonth(month);
if (day <= days)
break;
if (month < 11)
{
++month;
}
else
{
month = 0;
mYear++;
}
day -= days;
}
mDay = day;
mMonth = month;
}
/*
Start of tes3mp addition
Make it possible to set the year from elsewhere
*/
void DateTimeManager::setYear(int year)
{
mYear = year;
}
/*
End of tes3mp addition
*/
/*
Start of tes3mp addition
Make it possible to set the number of days passed from elsewhere
*/
void DateTimeManager::setDaysPassed(int days)
{
mDaysPassed = days;
}
/*
End of tes3mp addition
*/
/*
Start of tes3mp addition
Make it possible to set a custom timeScale from elsewhere
*/
void DateTimeManager::setTimeScale(float timeScale)
{
mTimeScale = timeScale;
}
/*
End of tes3mp addition
*/
TimeStamp DateTimeManager::getTimeStamp() const
{
return TimeStamp(mGameHour, mDaysPassed);
}
float DateTimeManager::getTimeScaleFactor() const
{
return mTimeScale;
}
ESM::EpochTimeStamp DateTimeManager::getEpochTimeStamp() const
{
ESM::EpochTimeStamp timeStamp;
timeStamp.mGameHour = mGameHour;
timeStamp.mDay = mDay;
timeStamp.mMonth = mMonth;
timeStamp.mYear = mYear;
return timeStamp;
}
void DateTimeManager::setMonth(int month)
{
if (month < 0)
month = 0;
int years = month / 12;
month = month % 12;
int days = getDaysPerMonth(month);
if (mDay > days)
mDay = days;
mMonth = month;
if (years > 0)
mYear += years;
}
void DateTimeManager::advanceTime(double hours, Globals& globalVariables)
{
hours += mGameHour;
setHour(hours);
int days = static_cast<int>(hours / 24);
if (days > 0)
mDaysPassed += days;
globalVariables["gamehour"].setFloat(mGameHour);
globalVariables["dayspassed"].setInteger(mDaysPassed);
globalVariables["day"].setInteger(mDay);
globalVariables["month"].setInteger(mMonth);
globalVariables["year"].setInteger(mYear);
}
std::string DateTimeManager::getMonthName(int month) const
{
if (month == -1)
month = mMonth;
const int months = 12;
if (month < 0 || month >= months)
return std::string();
static const char *monthNames[months] =
{
"sMonthMorningstar", "sMonthSunsdawn", "sMonthFirstseed", "sMonthRainshand",
"sMonthSecondseed", "sMonthMidyear", "sMonthSunsheight", "sMonthLastseed",
"sMonthHeartfire", "sMonthFrostfall", "sMonthSunsdusk", "sMonthEveningstar"
};
const ESM::GameSetting *setting = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(monthNames[month]);
return setting->mValue.getString();
}
bool DateTimeManager::updateGlobalFloat(const std::string& name, float value)
{
if (name=="gamehour")
{
setHour(value);
return true;
}
else if (name=="day")
{
setDay(static_cast<int>(value));
return true;
}
else if (name=="month")
{
setMonth(static_cast<int>(value));
return true;
}
else if (name=="year")
{
mYear = static_cast<int>(value);
}
else if (name=="timescale")
{
mTimeScale = value;
}
else if (name=="dayspassed")
{
mDaysPassed = static_cast<int>(value);
}
return false;
}
bool DateTimeManager::updateGlobalInt(const std::string& name, int value)
{
if (name=="gamehour")
{
setHour(static_cast<float>(value));
return true;
}
else if (name=="day")
{
setDay(value);
return true;
}
else if (name=="month")
{
setMonth(value);
return true;
}
else if (name=="year")
{
mYear = value;
}
else if (name=="timescale")
{
mTimeScale = static_cast<float>(value);
}
else if (name=="dayspassed")
{
mDaysPassed = value;
}
return false;
}
}

View file

@ -0,0 +1,73 @@
#ifndef GAME_MWWORLD_DATETIMEMANAGER_H
#define GAME_MWWORLD_DATETIMEMANAGER_H
#include <string>
namespace ESM
{
struct EpochTimeStamp;
}
namespace MWWorld
{
class Globals;
class TimeStamp;
class DateTimeManager
{
int mDaysPassed = 0;
int mDay = 0;
int mMonth = 0;
int mYear = 0;
float mGameHour = 0.f;
float mTimeScale = 0.f;
void setHour(double hour);
void setDay(int day);
void setMonth(int month);
/*
Start of tes3mp addition
Make it possible to set the year from elsewhere
*/
void setYear(int year);
/*
End of tes3mp addition
*/
/*
Start of tes3mp addition
Make it possible to set the number of days passed from elsewhere
*/
void setDaysPassed(int daysPassed);
/*
End of tes3mp addition
*/
/*
Start of tes3mp addition
Make it possible to set a custom timeScale from elsewhere
*/
void setTimeScale(float timeScale);
/*
End of tes3mp addition
*/
public:
std::string getMonthName(int month) const;
TimeStamp getTimeStamp() const;
ESM::EpochTimeStamp getEpochTimeStamp() const;
float getTimeScaleFactor() const;
void advanceTime(double hours, Globals& globalVariables);
void setup(Globals& globalVariables);
bool updateGlobalInt(const std::string& name, int value);
bool updateGlobalFloat(const std::string& name, float value);
};
}
#endif

View file

@ -273,7 +273,8 @@ void ESMStore::validate()
+mSpells.getDynamicSize() +mSpells.getDynamicSize()
+mWeapons.getDynamicSize() +mWeapons.getDynamicSize()
+mCreatureLists.getDynamicSize() +mCreatureLists.getDynamicSize()
+mItemLists.getDynamicSize(); +mItemLists.getDynamicSize()
+mCreatures.getDynamicSize();
} }
void ESMStore::write (ESM::ESMWriter& writer, Loading::Listener& progress) const void ESMStore::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
@ -295,6 +296,7 @@ void ESMStore::validate()
mNpcs.write (writer, progress); mNpcs.write (writer, progress);
mItemLists.write (writer, progress); mItemLists.write (writer, progress);
mCreatureLists.write (writer, progress); mCreatureLists.write (writer, progress);
mCreatures.write (writer, progress);
} }
bool ESMStore::readRecord (ESM::ESMReader& reader, uint32_t type) bool ESMStore::readRecord (ESM::ESMReader& reader, uint32_t type)
@ -312,24 +314,8 @@ void ESMStore::validate()
case ESM::REC_NPC_: case ESM::REC_NPC_:
case ESM::REC_LEVI: case ESM::REC_LEVI:
case ESM::REC_LEVC: case ESM::REC_LEVC:
case ESM::REC_CREA:
{
mStores[type]->read (reader); mStores[type]->read (reader);
}
if (type==ESM::REC_NPC_)
{
// NPC record will always be last and we know that there can be only one
// dynamic NPC record (player) -> We are done here with dynamic record loading
setUp();
const ESM::NPC *player = mNpcs.find ("player");
if (!mRaces.find (player->mRace) ||
!mClasses.find (player->mClass))
throw std::runtime_error ("Invalid player record (race or class unavailable");
}
return true; return true;
case ESM::REC_DYNA: case ESM::REC_DYNA:
@ -343,4 +329,15 @@ void ESMStore::validate()
} }
} }
void ESMStore::checkPlayer()
{
setUp();
const ESM::NPC *player = mNpcs.find ("player");
if (!mRaces.find (player->mRace) ||
!mClasses.find (player->mClass))
throw std::runtime_error ("Invalid player record (race or class unavailable");
}
} // end namespace } // end namespace

View file

@ -239,6 +239,9 @@ namespace MWWorld
bool readRecord (ESM::ESMReader& reader, uint32_t type); bool readRecord (ESM::ESMReader& reader, uint32_t type);
///< \return Known type? ///< \return Known type?
// To be called when we are done with dynamic record loading
void checkPlayer();
}; };
/* /*

View file

@ -2,10 +2,9 @@
#include <stdexcept> #include <stdexcept>
#include <components/misc/stringops.hpp>
#include <components/esm/esmwriter.hpp> #include <components/esm/esmwriter.hpp>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include <components/misc/stringops.hpp>
#include "esmstore.hpp" #include "esmstore.hpp"

View file

@ -317,12 +317,12 @@ void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots
// rate weapon // rate weapon
for (int i = 0; i < static_cast<int>(weaponSkillsLength); ++i) for (int i = 0; i < static_cast<int>(weaponSkillsLength); ++i)
{ {
int max = 0; float max = 0;
int maxWeaponSkill = -1; int maxWeaponSkill = -1;
for (int j = 0; j < static_cast<int>(weaponSkillsLength); ++j) for (int j = 0; j < static_cast<int>(weaponSkillsLength); ++j)
{ {
int skillValue = actor.getClass().getSkill(actor, static_cast<int>(weaponSkills[j])); float skillValue = actor.getClass().getSkill(actor, static_cast<int>(weaponSkills[j]));
if (skillValue > max && !weaponSkillVisited[j]) if (skillValue > max && !weaponSkillVisited[j])
{ {
max = skillValue; max = skillValue;
@ -432,7 +432,7 @@ void MWWorld::InventoryStore::autoEquipArmor (const MWWorld::Ptr& actor, TSlots&
static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat();
int unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); float unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored);
float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill);
for (ContainerStoreIterator iter (begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter!=end(); ++iter) for (ContainerStoreIterator iter (begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter!=end(); ++iter)
@ -617,7 +617,8 @@ void MWWorld::InventoryStore::updateMagicEffects(const Ptr& actor)
mMagicEffects = MWMechanics::MagicEffects(); mMagicEffects = MWMechanics::MagicEffects();
if (actor.getClass().getCreatureStats(actor).isDead()) const auto& stats = actor.getClass().getCreatureStats(actor);
if (stats.isDead() && stats.isDeathAnimationFinished())
return; return;
for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter) for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter)
@ -982,16 +983,16 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito
} }
} }
void MWWorld::InventoryStore::purgeEffect(short effectId) void MWWorld::InventoryStore::purgeEffect(short effectId, bool wholeSpell)
{ {
for (TSlots::const_iterator it = mSlots.begin(); it != mSlots.end(); ++it) for (TSlots::const_iterator it = mSlots.begin(); it != mSlots.end(); ++it)
{ {
if (*it != end()) if (*it != end())
purgeEffect(effectId, (*it)->getCellRef().getRefId()); purgeEffect(effectId, (*it)->getCellRef().getRefId(), wholeSpell);
} }
} }
void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sourceId) void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sourceId, bool wholeSpell)
{ {
TEffectMagnitudes::iterator effectMagnitudeIt = mPermanentMagicEffectMagnitudes.find(sourceId); TEffectMagnitudes::iterator effectMagnitudeIt = mPermanentMagicEffectMagnitudes.find(sourceId);
if (effectMagnitudeIt == mPermanentMagicEffectMagnitudes.end()) if (effectMagnitudeIt == mPermanentMagicEffectMagnitudes.end())
@ -1024,6 +1025,12 @@ void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sou
if (effectIt->mEffectID != effectId) if (effectIt->mEffectID != effectId)
continue; continue;
if (wholeSpell)
{
mPermanentMagicEffectMagnitudes.erase(sourceId);
return;
}
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom;
magnitude *= params[i].mMultiplier; magnitude *= params[i].mMultiplier;

View file

@ -203,10 +203,10 @@ namespace MWWorld
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor); void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor);
void purgeEffect (short effectId); void purgeEffect (short effectId, bool wholeSpell = false);
///< Remove a magic effect ///< Remove a magic effect
void purgeEffect (short effectId, const std::string& sourceId); void purgeEffect (short effectId, const std::string& sourceId, bool wholeSpell = false);
///< Remove a magic effect ///< Remove a magic effect
virtual void clear(); virtual void clear();

View file

@ -391,8 +391,6 @@ namespace MWWorld
else else
player.mHasMark = false; player.mHasMark = false;
player.mAutoMove = mAutoMove ? 1 : 0;
for (int i=0; i<ESM::Attribute::Length; ++i) for (int i=0; i<ESM::Attribute::Length; ++i)
mSaveAttributes[i].writeState(player.mSaveAttributes[i]); mSaveAttributes[i].writeState(player.mSaveAttributes[i]);
for (int i=0; i<ESM::Skill::Length; ++i) for (int i=0; i<ESM::Skill::Length; ++i)
@ -487,8 +485,6 @@ namespace MWWorld
mMarkedCell = 0; mMarkedCell = 0;
} }
mAutoMove = player.mAutoMove!=0;
mForwardBackward = 0; mForwardBackward = 0;
mTeleported = false; mTeleported = false;

Some files were not shown because too many files have changed in this diff Show more