mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 03:15:32 +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:
commit
e5b1843089
138 changed files with 2108 additions and 1543 deletions
|
@ -4,6 +4,9 @@
|
|||
Bug #1952: Incorrect particle lighting
|
||||
Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs
|
||||
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 #5108: Savegame bloating due to inefficient fog textures format
|
||||
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 #5397: NPC greeting does not reset if you leave + reenter area
|
||||
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 #5416: Junk non-node records before the root node are not handled gracefully
|
||||
Bug #5424: Creatures do not headtrack player
|
||||
|
@ -22,7 +26,10 @@
|
|||
Bug #5427: GetDistance unknown ID error is misleading
|
||||
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 #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 #5445: Handle NiLines
|
||||
|
||||
0.46.0
|
||||
------
|
||||
|
@ -294,7 +301,7 @@
|
|||
Feature #5147: Show spell magicka cost in spell buying window
|
||||
Feature #5170: Editor: Land shape editing, land selection
|
||||
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 #5219: Impelement TestCells console command
|
||||
Feature #5224: Handle NiKeyframeController for NiTriShape
|
||||
|
|
|
@ -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 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
|
||||
wrappedExit 1
|
||||
|
@ -745,6 +754,11 @@ fi
|
|||
if [ -d 'Qt/5.15.0' ]; then
|
||||
printf "Exists. "
|
||||
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
|
||||
if ! [ -d 'aqt-venv' ]; then
|
||||
echo " Creating Virtualenv for aqt..."
|
||||
|
@ -988,34 +1002,36 @@ RET=$?
|
|||
if [ -z $VERBOSE ]; then
|
||||
if [ $RET -eq 0 ]; then
|
||||
echo Done.
|
||||
if [ -n $ACTIVATE_MSVC ]; then
|
||||
echo
|
||||
echo "Note: you must manually activate MSVC for the shell in which you want to do the build."
|
||||
echo
|
||||
echo "Some scripts have been created in the build directory to do so in an existing shell."
|
||||
echo "Bash: source activate_msvc.sh"
|
||||
echo "CMD: ActivateMSVC.bat"
|
||||
echo "PowerShell: ActivateMSVC.ps1"
|
||||
echo
|
||||
echo "You may find options to launch a Development/Native Tools/Cross Tools shell in your start menu or Visual Studio."
|
||||
echo
|
||||
if [ $(uname -m) == 'x86_64' ]; then
|
||||
if [ $BITS -eq 64 ]; then
|
||||
inheritEnvironments=msvc_x64_x64
|
||||
else
|
||||
inheritEnvironments=msvc_x64
|
||||
fi
|
||||
else
|
||||
if [ $BITS -eq 64 ]; then
|
||||
inheritEnvironments=msvc_x86_x64
|
||||
else
|
||||
inheritEnvironments=msvc_x86
|
||||
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."
|
||||
fi
|
||||
else
|
||||
echo Failed.
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n $ACTIVATE_MSVC ]; then
|
||||
echo
|
||||
echo "Note: you must manually activate MSVC for the shell in which you want to do the build."
|
||||
echo
|
||||
echo "Some scripts have been created in the build directory to do so in an existing shell."
|
||||
echo "Bash: source activate_msvc.sh"
|
||||
echo "CMD: ActivateMSVC.bat"
|
||||
echo "PowerShell: ActivateMSVC.ps1"
|
||||
echo
|
||||
echo "You may find options to launch a Development/Native Tools/Cross Tools shell in your start menu or Visual Studio."
|
||||
echo
|
||||
if [ $(uname -m) == 'x86_64' ]; then
|
||||
if [ $BITS -eq 64 ]; then
|
||||
inheritEnvironments=msvc_x64_x64
|
||||
else
|
||||
inheritEnvironments=msvc_x64
|
||||
fi
|
||||
else
|
||||
if [ $BITS -eq 64 ]; then
|
||||
inheritEnvironments=msvc_x86_x64
|
||||
else
|
||||
inheritEnvironments=msvc_x86
|
||||
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."
|
||||
fi
|
||||
|
||||
wrappedExit $RET
|
||||
|
|
|
@ -183,19 +183,19 @@ int list(Bsa::BSAFile& bsa, Arguments& info)
|
|||
{
|
||||
// List all files
|
||||
const Bsa::BSAFile::FileList &files = bsa.getList();
|
||||
for(unsigned int i=0; i<files.size(); i++)
|
||||
for (const auto& file : files)
|
||||
{
|
||||
if(info.longformat)
|
||||
{
|
||||
// Long format
|
||||
std::ios::fmtflags f(std::cout.flags());
|
||||
std::cout << std::setw(50) << std::left << files[i].name;
|
||||
std::cout << std::setw(8) << std::left << std::dec << files[i].fileSize;
|
||||
std::cout << "@ 0x" << std::hex << files[i].offset << std::endl;
|
||||
std::cout << std::setw(50) << std::left << file.name;
|
||||
std::cout << std::setw(8) << std::left << std::dec << file.fileSize;
|
||||
std::cout << "@ 0x" << std::hex << file.offset << std::endl;
|
||||
std::cout.flags(f);
|
||||
}
|
||||
else
|
||||
std::cout << files[i].name << std::endl;
|
||||
std::cout << file.name << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -252,14 +252,9 @@ int extract(Bsa::BSAFile& bsa, Arguments& info)
|
|||
|
||||
int extractAll(Bsa::BSAFile& bsa, Arguments& info)
|
||||
{
|
||||
// Get the list of files present in the archive
|
||||
Bsa::BSAFile::FileList list = bsa.getList();
|
||||
|
||||
// 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);
|
||||
for (const auto &file : bsa.getList())
|
||||
{
|
||||
std::string extractPath(file.name);
|
||||
replaceAll(extractPath, "\\", "/");
|
||||
|
||||
// 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
|
||||
// (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);
|
||||
|
||||
// Write the file to disk
|
||||
|
|
|
@ -352,12 +352,12 @@ int load(Arguments& info)
|
|||
std::cout << "Author: " << esm.getAuthor() << std::endl
|
||||
<< "Description: " << esm.getDesc() << std::endl
|
||||
<< "File format version: " << esm.getFVer() << std::endl;
|
||||
std::vector<ESM::Header::MasterData> m = esm.getGameFiles();
|
||||
if (!m.empty())
|
||||
std::vector<ESM::Header::MasterData> masterData = esm.getGameFiles();
|
||||
if (!masterData.empty())
|
||||
{
|
||||
std::cout << "Masters:" << std::endl;
|
||||
for(unsigned int i=0;i<m.size();i++)
|
||||
std::cout << " " << m[i].name << ", " << m[i].size << " bytes" << std::endl;
|
||||
for(const auto& master : masterData)
|
||||
std::cout << " " << master.name << ", " << master.size << " bytes" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,7 +369,7 @@ int load(Arguments& info)
|
|||
esm.getRecHeader(flags);
|
||||
|
||||
EsmTool::RecordBase *record = EsmTool::RecordBase::create(n);
|
||||
if (record == 0)
|
||||
if (record == nullptr)
|
||||
{
|
||||
if (std::find(skipped.begin(), skipped.end(), n.intval) == skipped.end())
|
||||
{
|
||||
|
@ -538,8 +538,8 @@ int comp(Arguments& info)
|
|||
Arguments fileOne;
|
||||
Arguments fileTwo;
|
||||
|
||||
fileOne.raw_given = 0;
|
||||
fileTwo.raw_given = 0;
|
||||
fileOne.raw_given = false;
|
||||
fileTwo.raw_given = false;
|
||||
|
||||
fileOne.mode = "clone";
|
||||
fileTwo.mode = "clone";
|
||||
|
|
|
@ -779,7 +779,7 @@ std::string creatureListFlags(int flags)
|
|||
|
||||
std::string lightFlags(int flags)
|
||||
{
|
||||
std::string properties = "";
|
||||
std::string properties;
|
||||
if (flags == 0) properties += "[None] ";
|
||||
if (flags & ESM::Light::Dynamic) properties += "Dynamic ";
|
||||
if (flags & ESM::Light::Fire) properties += "Fire ";
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
namespace
|
||||
{
|
||||
|
||||
void printAIPackage(ESM::AIPackage p)
|
||||
void printAIPackage(const ESM::AIPackage& p)
|
||||
{
|
||||
std::cout << " AI Type: " << aiTypeLabel(p.mType)
|
||||
<< " (" << 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::string ruleString(ESM::DialInfo::SelectStruct ss)
|
||||
std::string ruleString(const ESM::DialInfo::SelectStruct& ss)
|
||||
{
|
||||
std::string rule = ss.mSelectRule;
|
||||
|
||||
|
@ -126,7 +126,7 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss)
|
|||
return result;
|
||||
}
|
||||
|
||||
void printEffectList(ESM::EffectList effects)
|
||||
void printEffectList(const ESM::EffectList& effects)
|
||||
{
|
||||
int i = 0;
|
||||
for (const ESM::ENAMstruct& effect : effects.mList)
|
||||
|
@ -174,7 +174,7 @@ namespace EsmTool {
|
|||
RecordBase *
|
||||
RecordBase::create(ESM::NAME type)
|
||||
{
|
||||
RecordBase *record = 0;
|
||||
RecordBase *record = nullptr;
|
||||
|
||||
switch (type.intval) {
|
||||
case ESM::REC_ACTI:
|
||||
|
@ -388,7 +388,7 @@ RecordBase::create(ESM::NAME type)
|
|||
break;
|
||||
}
|
||||
default:
|
||||
record = 0;
|
||||
record = nullptr;
|
||||
}
|
||||
if (record) {
|
||||
record->mType = type;
|
||||
|
@ -728,10 +728,9 @@ void Record<ESM::Faction>::print()
|
|||
<< " (" << mData.mData.mAttribute[0] << ")" << std::endl;
|
||||
std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1])
|
||||
<< " (" << mData.mData.mAttribute[1] << ")" << std::endl;
|
||||
for (int i = 0; i < 7; i++)
|
||||
if (mData.mData.mSkills[i] != -1)
|
||||
std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i])
|
||||
<< " (" << mData.mData.mSkills[i] << ")" << std::endl;
|
||||
for (int skill : mData.mData.mSkills)
|
||||
if (skill != -1)
|
||||
std::cout << " Skill: " << skillLabel(skill) << " (" << skill << ")" << std::endl;
|
||||
for (int i = 0; i != 10; i++)
|
||||
if (!mData.mRanks[i].empty())
|
||||
{
|
||||
|
|
|
@ -74,7 +74,7 @@ namespace EsmTool
|
|||
: mIsDeleted(false)
|
||||
{}
|
||||
|
||||
std::string getId() const {
|
||||
std::string getId() const override {
|
||||
return mData.mId;
|
||||
}
|
||||
|
||||
|
@ -82,15 +82,15 @@ namespace EsmTool
|
|||
return mData;
|
||||
}
|
||||
|
||||
void save(ESM::ESMWriter &esm) {
|
||||
void save(ESM::ESMWriter &esm) override {
|
||||
mData.save(esm, mIsDeleted);
|
||||
}
|
||||
|
||||
void load(ESM::ESMReader &esm) {
|
||||
void load(ESM::ESMReader &esm) override {
|
||||
mData.load(esm, mIsDeleted);
|
||||
}
|
||||
|
||||
void print();
|
||||
void print() override;
|
||||
};
|
||||
|
||||
template<> std::string Record<ESM::Cell>::getId() const;
|
||||
|
|
|
@ -52,9 +52,7 @@ namespace
|
|||
// a dynamically created record e.g. player-enchanted weapon
|
||||
|
||||
std::string index = indexedRefId.substr(indexedRefId.size()-8);
|
||||
if(index.find_first_not_of("0123456789ABCDEF") == std::string::npos )
|
||||
return true;
|
||||
return false;
|
||||
return index.find_first_not_of("0123456789ABCDEF") == std::string::npos;
|
||||
}
|
||||
|
||||
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);
|
||||
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
|
||||
|| it->first < mContext->mGlobalMapState.mBounds.mMinX
|
||||
|| it->second > mContext->mGlobalMapState.mBounds.mMaxY
|
||||
|| it->second < mContext->mGlobalMapState.mBounds.mMinY)
|
||||
if (exploredCell.first > mContext->mGlobalMapState.mBounds.mMaxX
|
||||
|| exploredCell.first < mContext->mGlobalMapState.mBounds.mMinX
|
||||
|| exploredCell.second > mContext->mGlobalMapState.mBounds.mMaxY
|
||||
|| exploredCell.second < mContext->mGlobalMapState.mBounds.mMinY)
|
||||
{
|
||||
// out of bounds, I think this could happen, since the original engine had a fixed-size map
|
||||
continue;
|
||||
|
@ -152,12 +150,12 @@ namespace ESSImport
|
|||
|
||||
int imageLeftSrc = mGlobalMapImage->s()/2;
|
||||
int imageTopSrc = mGlobalMapImage->t()/2;
|
||||
imageLeftSrc += it->first * cellSize;
|
||||
imageTopSrc -= it->second * cellSize;
|
||||
imageLeftSrc += exploredCell.first * cellSize;
|
||||
imageTopSrc -= exploredCell.second * cellSize;
|
||||
int imageLeftDst = width/2;
|
||||
int imageTopDst = height/2;
|
||||
imageLeftDst += it->first * cellSize;
|
||||
imageTopDst -= it->second * cellSize;
|
||||
imageLeftDst += exploredCell.first * cellSize;
|
||||
imageTopDst -= exploredCell.second * cellSize;
|
||||
for (int x=0; x<cellSize; ++x)
|
||||
for (int y=0; y<cellSize; ++y)
|
||||
{
|
||||
|
@ -329,9 +327,8 @@ namespace ESSImport
|
|||
csta.mWaterLevel = esmcell.mWater;
|
||||
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);
|
||||
|
||||
// TODO: use mContext->mCreatures/mNpcs
|
||||
|
@ -437,16 +434,16 @@ namespace ESSImport
|
|||
|
||||
void ConvertCell::write(ESM::ESMWriter &esm)
|
||||
{
|
||||
for (std::map<std::string, Cell>::const_iterator it = mIntCells.begin(); it != mIntCells.end(); ++it)
|
||||
writeCell(it->second, esm);
|
||||
for (const auto & cell : mIntCells)
|
||||
writeCell(cell.second, esm);
|
||||
|
||||
for (std::map<std::pair<int, int>, Cell>::const_iterator it = mExtCells.begin(); it != mExtCells.end(); ++it)
|
||||
writeCell(it->second, esm);
|
||||
for (const auto & cell : mExtCells)
|
||||
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);
|
||||
it->save(esm);
|
||||
marker.save(esm);
|
||||
esm.endRecord(ESM::REC_MARK);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,9 +79,9 @@ template <typename T>
|
|||
class DefaultConverter : public Converter
|
||||
{
|
||||
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;
|
||||
bool isDeleted = false;
|
||||
|
@ -90,7 +90,7 @@ public:
|
|||
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)
|
||||
{
|
||||
|
@ -107,7 +107,7 @@ protected:
|
|||
class ConvertNPC : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
void read(ESM::ESMReader &esm) override
|
||||
{
|
||||
ESM::NPC npc;
|
||||
bool isDeleted = false;
|
||||
|
@ -127,8 +127,8 @@ public:
|
|||
ESM::SpellState::SpellParams empty;
|
||||
// FIXME: player start spells and birthsign spells aren't listed here,
|
||||
// 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)
|
||||
mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty;
|
||||
for (const auto & spell : npc.mSpells.mList)
|
||||
mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[spell] = empty;
|
||||
|
||||
// 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.
|
||||
|
@ -144,7 +144,7 @@ public:
|
|||
class ConvertCREA : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
void read(ESM::ESMReader &esm) override
|
||||
{
|
||||
// See comment in ConvertNPC
|
||||
ESM::Creature creature;
|
||||
|
@ -162,7 +162,7 @@ public:
|
|||
class ConvertGlobal : public DefaultConverter<ESM::Global>
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
void read(ESM::ESMReader &esm) override
|
||||
{
|
||||
ESM::Global global;
|
||||
bool isDeleted = false;
|
||||
|
@ -183,7 +183,7 @@ public:
|
|||
class ConvertClass : public DefaultConverter<ESM::Class>
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
void read(ESM::ESMReader &esm) override
|
||||
{
|
||||
ESM::Class class_;
|
||||
bool isDeleted = false;
|
||||
|
@ -199,7 +199,7 @@ public:
|
|||
class ConvertBook : public DefaultConverter<ESM::Book>
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
void read(ESM::ESMReader &esm) override
|
||||
{
|
||||
ESM::Book book;
|
||||
bool isDeleted = false;
|
||||
|
@ -215,7 +215,7 @@ public:
|
|||
class ConvertNPCC : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
void read(ESM::ESMReader &esm) override
|
||||
{
|
||||
std::string id = esm.getHNString("NAME");
|
||||
NPCC npcc;
|
||||
|
@ -235,7 +235,7 @@ public:
|
|||
class ConvertREFR : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
void read(ESM::ESMReader &esm) override
|
||||
{
|
||||
REFR refr;
|
||||
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.writeHNString("ID__", mSelectedSpell);
|
||||
|
@ -280,14 +280,14 @@ public:
|
|||
mLevitationEnabled(true)
|
||||
{}
|
||||
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
void read(ESM::ESMReader &esm) override
|
||||
{
|
||||
PCDT pcdt;
|
||||
pcdt.load(esm);
|
||||
|
||||
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.writeHNT("TELE", mTeleportingEnabled);
|
||||
|
@ -306,7 +306,7 @@ private:
|
|||
|
||||
class ConvertCNTC : public Converter
|
||||
{
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
void read(ESM::ESMReader &esm) override
|
||||
{
|
||||
std::string id = esm.getHNString("NAME");
|
||||
CNTC cntc;
|
||||
|
@ -318,7 +318,7 @@ class ConvertCNTC : public Converter
|
|||
class ConvertCREC : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
void read(ESM::ESMReader &esm) override
|
||||
{
|
||||
std::string id = esm.getHNString("NAME");
|
||||
CREC crec;
|
||||
|
@ -330,8 +330,8 @@ public:
|
|||
class ConvertFMAP : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm);
|
||||
virtual void write(ESM::ESMWriter &esm);
|
||||
void read(ESM::ESMReader &esm) override;
|
||||
void write(ESM::ESMWriter &esm) override;
|
||||
|
||||
private:
|
||||
osg::ref_ptr<osg::Image> mGlobalMapImage;
|
||||
|
@ -340,8 +340,8 @@ private:
|
|||
class ConvertCell : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm);
|
||||
virtual void write(ESM::ESMWriter& esm);
|
||||
void read(ESM::ESMReader& esm) override;
|
||||
void write(ESM::ESMWriter& esm) override;
|
||||
|
||||
private:
|
||||
struct Cell
|
||||
|
@ -362,7 +362,7 @@ private:
|
|||
class ConvertKLST : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
void read(ESM::ESMReader& esm) override
|
||||
{
|
||||
KLST klst;
|
||||
klst.load(esm);
|
||||
|
@ -371,7 +371,7 @@ public:
|
|||
mContext->mPlayer.mObject.mNpcStats.mWerewolfKills = klst.mWerewolfKills;
|
||||
}
|
||||
|
||||
virtual void write(ESM::ESMWriter &esm)
|
||||
void write(ESM::ESMWriter &esm) override
|
||||
{
|
||||
esm.startRecord(ESM::REC_DCOU);
|
||||
for (std::map<std::string, int>::const_iterator it = mKillCounter.begin(); it != mKillCounter.end(); ++it)
|
||||
|
@ -389,7 +389,7 @@ private:
|
|||
class ConvertFACT : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
void read(ESM::ESMReader& esm) override
|
||||
{
|
||||
ESM::Faction faction;
|
||||
bool isDeleted = false;
|
||||
|
@ -409,7 +409,7 @@ public:
|
|||
class ConvertSTLN : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
void read(ESM::ESMReader &esm) override
|
||||
{
|
||||
std::string itemid = esm.getHNString("NAME");
|
||||
Misc::StringUtils::lowerCaseInPlace(itemid);
|
||||
|
@ -428,15 +428,15 @@ public:
|
|||
}
|
||||
}
|
||||
}
|
||||
virtual void write(ESM::ESMWriter &esm)
|
||||
void write(ESM::ESMWriter &esm) override
|
||||
{
|
||||
ESM::StolenItems items;
|
||||
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;
|
||||
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,
|
||||
// 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,
|
||||
|
@ -467,7 +467,7 @@ private:
|
|||
class ConvertINFO : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
void read(ESM::ESMReader& esm) override
|
||||
{
|
||||
INFO info;
|
||||
info.load(esm);
|
||||
|
@ -477,7 +477,7 @@ public:
|
|||
class ConvertDIAL : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
void read(ESM::ESMReader& esm) override
|
||||
{
|
||||
std::string id = esm.getHNString("NAME");
|
||||
DIAL dial;
|
||||
|
@ -485,7 +485,7 @@ public:
|
|||
if (dial.mIndex > 0)
|
||||
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)
|
||||
{
|
||||
|
@ -505,7 +505,7 @@ private:
|
|||
class ConvertQUES : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
void read(ESM::ESMReader& esm) override
|
||||
{
|
||||
std::string id = esm.getHNString("NAME");
|
||||
QUES quest;
|
||||
|
@ -516,7 +516,7 @@ public:
|
|||
class ConvertJOUR : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm)
|
||||
void read(ESM::ESMReader& esm) override
|
||||
{
|
||||
JOUR journal;
|
||||
journal.load(esm);
|
||||
|
@ -531,7 +531,7 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
void read(ESM::ESMReader &esm) override
|
||||
{
|
||||
mGame.load(esm);
|
||||
mHasGame = true;
|
||||
|
@ -551,7 +551,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
virtual void write(ESM::ESMWriter &esm)
|
||||
void write(ESM::ESMWriter &esm) override
|
||||
{
|
||||
if (!mHasGame)
|
||||
return;
|
||||
|
@ -578,7 +578,7 @@ private:
|
|||
class ConvertSCPT : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader &esm)
|
||||
void read(ESM::ESMReader &esm) override
|
||||
{
|
||||
SCPT script;
|
||||
script.load(esm);
|
||||
|
@ -586,12 +586,12 @@ public:
|
|||
convertSCPT(script, 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);
|
||||
it->save(esm);
|
||||
script.save(esm);
|
||||
esm.endRecord(ESM::REC_GSCR);
|
||||
}
|
||||
}
|
||||
|
@ -603,9 +603,9 @@ private:
|
|||
class ConvertPROJ : public Converter
|
||||
{
|
||||
public:
|
||||
virtual int getStage() override { return 2; }
|
||||
virtual void read(ESM::ESMReader& esm) override;
|
||||
virtual void write(ESM::ESMWriter& esm) override;
|
||||
int getStage() override { return 2; }
|
||||
void read(ESM::ESMReader& esm) override;
|
||||
void write(ESM::ESMWriter& esm) override;
|
||||
private:
|
||||
void convertBaseState(ESM::BaseProjectileState& base, const PROJ::PNAM& pnam);
|
||||
PROJ mProj;
|
||||
|
@ -614,8 +614,8 @@ private:
|
|||
class ConvertSPLM : public Converter
|
||||
{
|
||||
public:
|
||||
virtual void read(ESM::ESMReader& esm) override;
|
||||
virtual void write(ESM::ESMWriter& esm) override;
|
||||
void read(ESM::ESMReader& esm) override;
|
||||
void write(ESM::ESMWriter& esm) override;
|
||||
private:
|
||||
SPLM mSPLM;
|
||||
};
|
||||
|
|
|
@ -9,21 +9,20 @@ namespace ESSImport
|
|||
void convertInventory(const Inventory &inventory, ESM::InventoryState &state)
|
||||
{
|
||||
int index = 0;
|
||||
for (std::vector<Inventory::InventoryItem>::const_iterator it = inventory.mItems.begin();
|
||||
it != inventory.mItems.end(); ++it)
|
||||
for (const auto & item : inventory.mItems)
|
||||
{
|
||||
ESM::ObjectState objstate;
|
||||
objstate.blank();
|
||||
objstate.mRef = *it;
|
||||
objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId);
|
||||
objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile
|
||||
objstate.mRef = item;
|
||||
objstate.mRef.mRefID = Misc::StringUtils::lowerCase(item.mId);
|
||||
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
|
||||
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
|
||||
// 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)
|
||||
state.mEquipmentSlots[index] = it->mRelativeEquipmentSlot;
|
||||
state.mEquipmentSlots[index] = item.mRelativeEquipmentSlot;
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,13 +10,13 @@ namespace ESSImport
|
|||
{
|
||||
out.mBirthsign = pcdt.mBirthsign;
|
||||
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;
|
||||
faction.mExpelled = (it->mFlags & 0x2) != 0;
|
||||
faction.mRank = it->mRank;
|
||||
faction.mReputation = it->mReputation;
|
||||
out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(it->mFactionName.toString())] = faction;
|
||||
faction.mExpelled = (essFaction.mFlags & 0x2) != 0;
|
||||
faction.mRank = essFaction.mRank;
|
||||
faction.mReputation = essFaction.mReputation;
|
||||
out.mObject.mNpcStats.mFactions[Misc::StringUtils::lowerCase(essFaction.mFactionName.toString())] = faction;
|
||||
}
|
||||
for (int i=0; i<3; ++i)
|
||||
out.mObject.mNpcStats.mSpecIncreases[i] = pcdt.mPNAM.mSpecIncreases[i];
|
||||
|
@ -35,10 +35,9 @@ namespace ESSImport
|
|||
teleportingEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_TeleportingDisabled);
|
||||
levitationEnabled = !(pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_LevitationDisabled);
|
||||
|
||||
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin();
|
||||
it != pcdt.mKnownDialogueTopics.end(); ++it)
|
||||
for (const auto & knownDialogueTopic : pcdt.mKnownDialogueTopics)
|
||||
{
|
||||
outDialogueTopics.push_back(Misc::StringUtils::lowerCase(*it));
|
||||
outDialogueTopics.push_back(Misc::StringUtils::lowerCase(knownDialogueTopic));
|
||||
}
|
||||
|
||||
controls.mViewSwitchDisabled = pcdt.mPNAM.mPlayerFlags & PCDT::PlayerFlags_ViewSwitchDisabled;
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
#include "convertscri.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <typename T, ESM::VarType VariantType>
|
||||
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);
|
||||
locals.mVariables.push_back(std::make_pair(std::string(), val));
|
||||
locals.mVariables.emplace_back(std::string(), val);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,9 @@ namespace ESSImport
|
|||
bool mHasANIS;
|
||||
ANIS mANIS; // scripted animation state
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
virtual void load(ESM::ESMReader& esm);
|
||||
|
||||
virtual ~ActorData() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -25,7 +25,9 @@ namespace ESSImport
|
|||
|
||||
bool mDeleted;
|
||||
|
||||
void load(ESM::ESMReader& esm);
|
||||
void load(ESM::ESMReader& esm) override;
|
||||
|
||||
virtual ~CellRef() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -16,15 +16,12 @@
|
|||
#include <components/esm/player.hpp>
|
||||
|
||||
#include <components/esm/loadalch.hpp>
|
||||
#include <components/esm/loadclas.hpp>
|
||||
#include <components/esm/loadspel.hpp>
|
||||
#include <components/esm/loadarmo.hpp>
|
||||
#include <components/esm/loadweap.hpp>
|
||||
#include <components/esm/loadclot.hpp>
|
||||
#include <components/esm/loadench.hpp>
|
||||
#include <components/esm/loadweap.hpp>
|
||||
#include <components/esm/loadlevlist.hpp>
|
||||
#include <components/esm/loadglob.hpp>
|
||||
|
||||
#include <components/misc/constants.hpp>
|
||||
|
||||
|
@ -49,7 +46,7 @@ namespace
|
|||
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
|
||||
std::vector<unsigned char>::const_iterator it = fileHeader.mSCRS.begin();
|
||||
auto it = fileHeader.mSCRS.begin();
|
||||
for (int y=0; y<128; ++y)
|
||||
{
|
||||
for (int x=0; x<128; ++x)
|
||||
|
@ -317,10 +314,9 @@ namespace ESSImport
|
|||
|
||||
std::set<unsigned int> unknownRecords;
|
||||
|
||||
for (std::map<unsigned int, std::shared_ptr<Converter> >::const_iterator it = converters.begin();
|
||||
it != converters.end(); ++it)
|
||||
for (const auto & converter : converters)
|
||||
{
|
||||
it->second->setContext(context);
|
||||
converter.second->setContext(context);
|
||||
}
|
||||
|
||||
while (esm.hasMoreRecs())
|
||||
|
@ -328,7 +324,7 @@ namespace ESSImport
|
|||
ESM::NAME n = esm.getRecName();
|
||||
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())
|
||||
{
|
||||
it->second->read(esm);
|
||||
|
@ -358,17 +354,15 @@ namespace ESSImport
|
|||
writer.setDescription("");
|
||||
writer.setRecordCount (0);
|
||||
|
||||
for (std::vector<ESM::Header::MasterData>::const_iterator it = header.mMaster.begin();
|
||||
it != header.mMaster.end(); ++it)
|
||||
writer.addMaster (it->name, 0); // not using the size information anyway -> use value of 0
|
||||
for (const auto & master : header.mMaster)
|
||||
writer.addMaster(master.name, 0); // not using the size information anyway -> use value of 0
|
||||
|
||||
writer.save (stream);
|
||||
|
||||
ESM::SavedGame profile;
|
||||
for (std::vector<ESM::Header::MasterData>::const_iterator it = header.mMaster.begin();
|
||||
it != header.mMaster.end(); ++it)
|
||||
for (const auto & master : header.mMaster)
|
||||
{
|
||||
profile.mContentFiles.push_back(it->name);
|
||||
profile.mContentFiles.push_back(master.name);
|
||||
}
|
||||
profile.mDescription = esm.getDesc();
|
||||
profile.mInGameTime.mDay = context.mDay;
|
||||
|
|
|
@ -63,7 +63,6 @@ namespace ESSImport
|
|||
, mHour(0.f)
|
||||
, mNextActorId(0)
|
||||
{
|
||||
mPlayer.mAutoMove = 0;
|
||||
ESM::CellId playerCellId;
|
||||
playerCellId.mPaged = true;
|
||||
playerCellId.mIndex.mX = playerCellId.mIndex.mY = 0;
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
|
||||
#include <components/esm/loadcont.hpp>
|
||||
|
||||
namespace ESSImport
|
||||
{
|
||||
|
||||
|
|
|
@ -15,17 +15,13 @@ set(GAME_HEADER
|
|||
engine.hpp
|
||||
)
|
||||
|
||||
if (BULLET_USE_DOUBLES)
|
||||
add_definitions(-DBT_USE_DOUBLE_PRECISION)
|
||||
endif()
|
||||
|
||||
source_group(game FILES ${GAME} ${GAME_HEADER})
|
||||
|
||||
add_openmw_dir (mwrender
|
||||
actors objects renderingmanager animation rotatecontroller sky npcanimation vismask
|
||||
creatureanimation effectmanager util renderinginterface pathgrid rendermode weaponanimation
|
||||
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
|
||||
|
@ -71,7 +67,7 @@ add_openmw_dir (mwworld
|
|||
actionequip timestamp actionalchemy cellstore actionapply actioneat
|
||||
store esmstore recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
|
||||
contentloader esmloader actiontrap cellreflist cellref physicssystem weather projectilemanager
|
||||
cellpreloader
|
||||
cellpreloader datetimemanager
|
||||
)
|
||||
|
||||
add_openmw_dir (mwphysics
|
||||
|
|
|
@ -311,6 +311,8 @@ namespace MWBase
|
|||
virtual float getAngleToPlayer(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 void restoreStatsAfterCorprus(const MWWorld::Ptr& actor, const std::string& sourceId) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ namespace ESM
|
|||
struct Position;
|
||||
struct Cell;
|
||||
struct Class;
|
||||
struct Creature;
|
||||
struct Potion;
|
||||
struct Spell;
|
||||
struct NPC;
|
||||
|
@ -47,7 +48,7 @@ namespace ESM
|
|||
struct EffectList;
|
||||
struct CreatureLevList;
|
||||
struct ItemLevList;
|
||||
struct Creature;
|
||||
struct TimeStamp;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
|
@ -234,54 +235,14 @@ namespace MWBase
|
|||
virtual void advanceTime (double hours, bool incremental = false) = 0;
|
||||
///< 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;
|
||||
///< Return name of month (-1: current month)
|
||||
|
||||
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;
|
||||
///< \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.
|
||||
/// \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 updatePhysics (float duration, bool paused) = 0;
|
||||
|
||||
|
|
|
@ -322,7 +322,7 @@ namespace MWClass
|
|||
const MWWorld::LiveCellRef<ESM::Armor> *ref = ptr.get<ESM::Armor>();
|
||||
|
||||
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();
|
||||
int iBaseArmorSkill = world->getStore().get<ESM::GameSetting>().find("iBaseArmorSkill")->mValue.getInteger();
|
||||
|
|
|
@ -793,7 +793,7 @@ namespace MWClass
|
|||
float Creature::getCapacity (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
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
|
||||
|
@ -933,7 +933,7 @@ namespace MWClass
|
|||
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 =
|
||||
ptr.get<ESM::Creature>();
|
||||
|
@ -997,6 +997,12 @@ namespace MWClass
|
|||
return;
|
||||
}
|
||||
|
||||
if (ptr.getRefData().getCount() <= 0)
|
||||
{
|
||||
state.mHasCustomState = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const CreatureCustomData& customData = ptr.getRefData().getCustomData()->asCreatureCustomData();
|
||||
ESM::CreatureState& creatureState = state.asCreatureState();
|
||||
customData.mContainerStore->writeState (creatureState.mInventory);
|
||||
|
@ -1066,4 +1072,9 @@ namespace MWClass
|
|||
const MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ namespace MWClass
|
|||
virtual bool canSwim (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)
|
||||
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;
|
||||
/// @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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ namespace MWClass
|
|||
}
|
||||
|
||||
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 =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat();
|
||||
|
|
|
@ -144,8 +144,8 @@ namespace
|
|||
}
|
||||
|
||||
// initial health
|
||||
int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
|
||||
int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();
|
||||
float strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
|
||||
float endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();
|
||||
|
||||
int multiplier = 3;
|
||||
|
||||
|
@ -1234,7 +1234,7 @@ namespace MWClass
|
|||
gmst.fJumpEncumbranceMultiplier->mValue.getFloat() *
|
||||
(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;
|
||||
if(a > 50.0f)
|
||||
{
|
||||
|
@ -1359,7 +1359,7 @@ namespace MWClass
|
|||
|
||||
float fUnarmoredBase1 = store.find("fUnarmoredBase1")->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];
|
||||
for(int i = 0;i < MWWorld::InventoryStore::Slots;i++)
|
||||
|
@ -1506,7 +1506,7 @@ namespace MWClass
|
|||
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();
|
||||
}
|
||||
|
@ -1550,6 +1550,12 @@ namespace MWClass
|
|||
return;
|
||||
}
|
||||
|
||||
if (ptr.getRefData().getCount() <= 0)
|
||||
{
|
||||
state.mHasCustomState = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const NpcCustomData& customData = ptr.getRefData().getCustomData()->asNpcCustomData();
|
||||
ESM::NpcState& npcState = state.asNpcState();
|
||||
customData.mInventoryStore.writeState (npcState.mInventory);
|
||||
|
@ -1660,4 +1666,9 @@ namespace MWClass
|
|||
const MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ namespace MWClass
|
|||
|
||||
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)
|
||||
virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const;
|
||||
|
@ -174,6 +174,8 @@ namespace MWClass
|
|||
|
||||
virtual std::string getPrimaryFaction(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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -134,6 +134,12 @@ namespace MWGui
|
|||
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;
|
||||
for (int day=0; day<mDays; ++day)
|
||||
{
|
||||
|
@ -148,14 +154,14 @@ namespace MWGui
|
|||
Disable increases for Security and Sneak when using 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)
|
||||
/*
|
||||
End of tes3mp change (minor)
|
||||
*/
|
||||
value.setBase(std::min(100, value.getBase()+1));
|
||||
value.setBase(std::min(100.f, value.getBase() + 1));
|
||||
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>();
|
||||
|
|
|
@ -157,7 +157,7 @@ namespace MWGui
|
|||
mAttributeValues[i]->setEnabled(true);
|
||||
availableAttributes++;
|
||||
|
||||
int mult = pcStats.getLevelupAttributeMultiplier (i);
|
||||
float mult = pcStats.getLevelupAttributeMultiplier (i);
|
||||
mult = std::min(mult, 100-pcStats.getAttribute(i).getBase());
|
||||
text->setCaption(mult <= 1 ? "" : "x" + MyGUI::utility::toString(mult));
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace MWGui
|
|||
{
|
||||
MWWorld::Ptr player = MWMechanics::getPlayer();
|
||||
mSourceModel = sourceModel;
|
||||
int chance = player.getClass().getSkill(player, ESM::Skill::Sneak);
|
||||
float chance = player.getClass().getSkill(player, ESM::Skill::Sneak);
|
||||
|
||||
mSourceModel->update();
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ namespace MWGui
|
|||
for (int i=0; ids[i]; ++i)
|
||||
if (ids[i]==id)
|
||||
{
|
||||
setText (id, std::to_string(value.getModified()));
|
||||
setText (id, std::to_string(static_cast<int>(value.getModified())));
|
||||
|
||||
MyGUI::TextBox* box;
|
||||
getWidget(box, id);
|
||||
|
|
|
@ -86,11 +86,11 @@ namespace MWGui
|
|||
mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold));
|
||||
|
||||
// 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)
|
||||
{
|
||||
int value = actor.getClass().getSkill(actor, i);
|
||||
float value = actor.getClass().getSkill(actor, i);
|
||||
|
||||
skills.push_back(std::make_pair(i, value));
|
||||
}
|
||||
|
|
|
@ -180,11 +180,10 @@ namespace MWGui
|
|||
if (hour >= 13) hour -= 12;
|
||||
if (hour == 0) hour = 12;
|
||||
|
||||
std::string dateTimeText =
|
||||
MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getDay ()) + " "
|
||||
+ month + " (#{sDay} " + MyGUI::utility::toString(MWBase::Environment::get().getWorld ()->getTimeStamp ().getDay())
|
||||
+ ") " + MyGUI::utility::toString(hour) + " " + (pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}");
|
||||
|
||||
ESM::EpochTimeStamp currentDate = MWBase::Environment::get().getWorld()->getEpochTimeStamp();
|
||||
int daysPassed = MWBase::Environment::get().getWorld()->getTimeStamp().getDay();
|
||||
std::string formattedHour = pm ? "#{sSaveMenuHelp05}" : "#{sSaveMenuHelp04}";
|
||||
std::string dateTimeText = Misc::StringUtils::format("%i %s (#{sDay} %i) %i %s", currentDate.mDay, month, daysPassed, hour, formattedHour);
|
||||
mDateTimeText->setCaptionWithReplacing (dateTimeText);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,13 +29,23 @@ namespace MWMechanics
|
|||
}
|
||||
else
|
||||
{
|
||||
bool interrupt = false;
|
||||
std::vector<ActiveEffect>& effects = iter->second.mEffects;
|
||||
for (std::vector<ActiveEffect>::iterator effectIt = effects.begin(); effectIt != effects.end();)
|
||||
{
|
||||
if (effectIt->mTimeLeft <= 0)
|
||||
{
|
||||
effectIt = effects.erase(effectIt);
|
||||
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
|
||||
{
|
||||
|
@ -43,7 +53,9 @@ namespace MWMechanics
|
|||
++effectIt;
|
||||
}
|
||||
}
|
||||
++iter;
|
||||
|
||||
if (!interrupt)
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -327,6 +339,31 @@ namespace MWMechanics
|
|||
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()
|
||||
{
|
||||
mSpells.clear();
|
||||
|
|
|
@ -119,6 +119,8 @@ namespace MWMechanics
|
|||
bool isSpellActive (const std::string& id) const;
|
||||
///< case insensitive
|
||||
|
||||
void purgeCorprusDisease();
|
||||
|
||||
const MagicEffects& getMagicEffects() const;
|
||||
|
||||
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor) const;
|
||||
|
|
|
@ -131,11 +131,11 @@ void adjustCommandedActor (const MWWorld::Ptr& actor)
|
|||
|
||||
bool hasCommandPackage = false;
|
||||
|
||||
std::list<MWMechanics::AiPackage*>::const_iterator it;
|
||||
for (it = stats.getAiSequence().begin(); it != stats.getAiSequence().end(); ++it)
|
||||
auto it = stats.getAiSequence().begin();
|
||||
for (; it != stats.getAiSequence().end(); ++it)
|
||||
{
|
||||
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow &&
|
||||
static_cast<MWMechanics::AiFollow*>(*it)->isCommanded())
|
||||
static_cast<const MWMechanics::AiFollow*>(it->get())->isCommanded())
|
||||
{
|
||||
hasCommandPackage = true;
|
||||
break;
|
||||
|
@ -151,7 +151,7 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
|
|||
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
|
||||
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;
|
||||
|
||||
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
|
||||
{
|
||||
MWWorld::Ptr mCreature;
|
||||
|
@ -452,7 +495,7 @@ namespace MWMechanics
|
|||
if (world->isSwimming(actor) || (playerPos - actorPos).length2() >= 3000 * 3000)
|
||||
return;
|
||||
|
||||
// Our implementation is not FPS-dependent unlike Morrowind's so it needs to be recalibrated.
|
||||
// Our implementation is not FPS-dependent unlike Morrowind's so it needs to be recalibrated.
|
||||
// We chose to use the chance MW would have when run at 60 FPS with the default value of the GMST.
|
||||
const float delta = MWBase::Environment::get().getFrameDuration() * 6.f;
|
||||
static const float fVoiceIdleOdds = world->getStore().get<ESM::GameSetting>().find("fVoiceIdleOdds")->mValue.getFloat();
|
||||
|
@ -468,9 +511,9 @@ namespace MWMechanics
|
|||
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
||||
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();
|
||||
float distance = (targetPos - actorPos).length();
|
||||
if (distance < DECELERATE_DISTANCE)
|
||||
|
@ -637,7 +680,7 @@ namespace MWMechanics
|
|||
bool isPlayerFollowerOrEscorter = playerAllies.find(actor1) != playerAllies.end();
|
||||
|
||||
// If actor2 and at least one actor2 are in combat with actor1, actor1 and its allies start combat with them
|
||||
// Doesn't apply for player followers/escorters
|
||||
// Doesn't apply for player followers/escorters
|
||||
if (!aggressive && !isPlayerFollowerOrEscorter)
|
||||
{
|
||||
// Check that actor2 is in combat with actor1
|
||||
|
@ -718,7 +761,7 @@ namespace MWMechanics
|
|||
return;
|
||||
|
||||
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
|
||||
if (package->sideWithTarget())
|
||||
|
@ -767,7 +810,7 @@ namespace MWMechanics
|
|||
{
|
||||
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;
|
||||
if (ptr == getPlayer())
|
||||
|
@ -846,7 +889,7 @@ namespace MWMechanics
|
|||
float fFatigueReturnMult = settings.find("fFatigueReturnMult")->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);
|
||||
if (normalizedEncumbrance > 1)
|
||||
|
@ -873,7 +916,7 @@ namespace MWMechanics
|
|||
return;
|
||||
|
||||
// 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>();
|
||||
static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat ();
|
||||
static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat ();
|
||||
|
@ -987,20 +1030,74 @@ namespace MWMechanics
|
|||
if (creatureStats.needToRecalcDynamicStats())
|
||||
calculateDynamicStats(ptr);
|
||||
|
||||
if (ptr == getPlayer())
|
||||
{
|
||||
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);
|
||||
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;
|
||||
|
||||
if (ptr == getPlayer())
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}");
|
||||
}
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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())
|
||||
{
|
||||
// They can be added during the death animation
|
||||
if (!iter->first.getClass().getCreatureStats(iter->first).isDeathAnimationFinished())
|
||||
adjustMagicEffects(iter->first);
|
||||
ctrl->updateContinuousVfx();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool cellChanged = world->hasCellChanged();
|
||||
|
@ -1913,7 +2015,7 @@ namespace MWMechanics
|
|||
if (!isPlayer && isConscious(iter->first) && !stats.isParalyzed())
|
||||
{
|
||||
MWMechanics::AiSequence& seq = stats.getAiSequence();
|
||||
alwaysActive = !seq.isEmpty() && seq.getActivePackage()->alwaysActive();
|
||||
alwaysActive = !seq.isEmpty() && seq.getActivePackage().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)
|
||||
|
@ -2179,6 +2281,8 @@ namespace MWMechanics
|
|||
|
||||
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())
|
||||
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
|
||||
// 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())
|
||||
{
|
||||
|
@ -2468,9 +2572,9 @@ namespace MWMechanics
|
|||
if (stats.isDead())
|
||||
continue;
|
||||
|
||||
// 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
|
||||
for (const AiPackage* package : stats.getAiSequence())
|
||||
for (const auto& package : stats.getAiSequence())
|
||||
{
|
||||
if (package->followTargetThroughDoors() && package->getTarget() == actor)
|
||||
list.push_back(iteratedActor);
|
||||
|
@ -2533,11 +2637,11 @@ namespace MWMechanics
|
|||
|
||||
// An actor counts as following if AiFollow is the current AiPackage,
|
||||
// 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)
|
||||
{
|
||||
list.push_back(static_cast<AiFollow*>(package)->getFollowIndex());
|
||||
list.push_back(static_cast<const AiFollow*>(package.get())->getFollowIndex());
|
||||
break;
|
||||
}
|
||||
else if (package->getTypeId() != AiPackage::TypeIdCombat && package->getTypeId() != AiPackage::TypeIdWander)
|
||||
|
|
|
@ -1,6 +1,16 @@
|
|||
#ifndef 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
|
||||
{
|
||||
class Ptr;
|
||||
|
@ -18,6 +28,33 @@ namespace MWMechanics
|
|||
MWWorld::Ptr getPlayer();
|
||||
bool isPlayerInCombat();
|
||||
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
|
||||
|
|
|
@ -44,11 +44,6 @@ namespace MWMechanics
|
|||
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)
|
||||
{
|
||||
/*
|
||||
|
@ -109,11 +104,6 @@ namespace MWMechanics
|
|||
return false;
|
||||
}
|
||||
|
||||
int AiActivate::getTypeId() const
|
||||
{
|
||||
return TypeIdActivate;
|
||||
}
|
||||
|
||||
void AiActivate::writeState(ESM::AiSequence::AiSequence &sequence) const
|
||||
{
|
||||
std::unique_ptr<ESM::AiSequence::AiActivate> activate(new ESM::AiSequence::AiActivate());
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef GAME_MWMECHANICS_AIACTIVATE_H
|
||||
#define GAME_MWMECHANICS_AIACTIVATE_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include "typedaipackage.hpp"
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
@ -29,7 +29,7 @@ namespace MWMechanics
|
|||
{
|
||||
/// \brief Causes actor to walk to activatable object and activate it
|
||||
/** Will activate when close to object **/
|
||||
class AiActivate final : public AiPackage
|
||||
class AiActivate final : public TypedAiPackage<AiActivate>
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
|
@ -49,14 +49,14 @@ namespace MWMechanics
|
|||
|
||||
AiActivate(const ESM::AiSequence::AiActivate* activate);
|
||||
|
||||
AiActivate *clone() const 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;
|
||||
|
||||
private:
|
||||
std::string mObjectId;
|
||||
const std::string mObjectId;
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
static const int MAX_DIRECTIONS = 4;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return (actorPos - mLastPos).length2() < 10 * 10;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef GAME_MWMECHANICS_AIAVOIDDOOR_H
|
||||
#define GAME_MWMECHANICS_AIAVOIDDOOR_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include "typedaipackage.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -16,26 +16,28 @@ namespace MWMechanics
|
|||
/// \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
|
||||
**/
|
||||
class AiAvoidDoor final : public AiPackage
|
||||
class AiAvoidDoor final : public TypedAiPackage<AiAvoidDoor>
|
||||
{
|
||||
public:
|
||||
/// Avoid door until the door is fully open
|
||||
AiAvoidDoor(const MWWorld::ConstPtr& doorPtr);
|
||||
|
||||
AiAvoidDoor *clone() const 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;
|
||||
|
||||
bool canCancel() const final { return false; }
|
||||
bool shouldCancelPreviousAi() const final { return false; }
|
||||
static constexpr Options makeDefaultOptions()
|
||||
{
|
||||
AiPackage::Options options;
|
||||
options.mPriority = 2;
|
||||
options.mCanCancel = false;
|
||||
options.mShouldCancelPreviousAi = false;
|
||||
return options;
|
||||
}
|
||||
|
||||
private:
|
||||
float mDuration;
|
||||
MWWorld::ConstPtr mDoorPtr;
|
||||
const MWWorld::ConstPtr mDoorPtr;
|
||||
osg::Vec3f mLastPos;
|
||||
int mDirection;
|
||||
|
||||
|
|
|
@ -11,12 +11,6 @@
|
|||
#include "movement.hpp"
|
||||
#include "steering.hpp"
|
||||
|
||||
MWMechanics::AiBreathe::AiBreathe()
|
||||
: AiPackage()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -37,18 +31,3 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro
|
|||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
#ifndef GAME_MWMECHANICS_AIBREATHE_H
|
||||
#define GAME_MWMECHANICS_AIBREATHE_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include "typedaipackage.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
/// \brief AiPackage to have an actor resurface to breathe
|
||||
// The AI will go up if lesser than half breath left
|
||||
class AiBreathe final : public AiPackage
|
||||
class AiBreathe final : public TypedAiPackage<AiBreathe>
|
||||
{
|
||||
public:
|
||||
AiBreathe();
|
||||
|
||||
AiBreathe *clone() const 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;
|
||||
|
||||
bool canCancel() const final { return false; }
|
||||
bool shouldCancelPreviousAi() const final { return false; }
|
||||
static constexpr Options makeDefaultOptions()
|
||||
{
|
||||
AiPackage::Options options;
|
||||
options.mPriority = 2;
|
||||
options.mCanCancel = false;
|
||||
options.mShouldCancelPreviousAi = false;
|
||||
return options;
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -10,17 +10,22 @@
|
|||
#include "creaturestats.hpp"
|
||||
#include "steering.hpp"
|
||||
|
||||
MWMechanics::AiCast::AiCast(const std::string& targetId, const std::string& spellId, bool manualSpell)
|
||||
: mTargetId(targetId), mSpellId(spellId), mCasting(false), mManual(manualSpell), mDistance(0)
|
||||
namespace MWMechanics
|
||||
{
|
||||
ActionSpell action = ActionSpell(spellId);
|
||||
bool isRanged;
|
||||
mDistance = action.getCombatRange(isRanged);
|
||||
namespace
|
||||
{
|
||||
float getInitialDistance(const std::string& spellId)
|
||||
{
|
||||
ActionSpell action = ActionSpell(spellId);
|
||||
bool 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)
|
||||
|
@ -84,13 +89,3 @@ MWWorld::Ptr MWMechanics::AiCast::getTarget() const
|
|||
|
||||
return target;
|
||||
}
|
||||
|
||||
int MWMechanics::AiCast::getTypeId() const
|
||||
{
|
||||
return AiPackage::TypeIdCast;
|
||||
}
|
||||
|
||||
unsigned int MWMechanics::AiCast::getPriority() const
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef GAME_MWMECHANICS_AICAST_H
|
||||
#define GAME_MWMECHANICS_AICAST_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include "typedaipackage.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
@ -11,29 +11,31 @@ namespace MWWorld
|
|||
namespace MWMechanics
|
||||
{
|
||||
/// AiPackage which makes an actor to cast given spell.
|
||||
class AiCast final : public AiPackage {
|
||||
class AiCast final : public TypedAiPackage<AiCast> {
|
||||
public:
|
||||
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;
|
||||
|
||||
int getTypeId() const final;
|
||||
static constexpr TypeId getTypeId() { return TypeIdCast; }
|
||||
|
||||
MWWorld::Ptr getTarget() const final;
|
||||
|
||||
unsigned int getPriority() const final;
|
||||
|
||||
bool canCancel() const final { return false; }
|
||||
bool shouldCancelPreviousAi() const final { return false; }
|
||||
static constexpr Options makeDefaultOptions()
|
||||
{
|
||||
AiPackage::Options options;
|
||||
options.mPriority = 3;
|
||||
options.mCanCancel = false;
|
||||
options.mShouldCancelPreviousAi = false;
|
||||
return options;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string mTargetId;
|
||||
std::string mSpellId;
|
||||
const std::string mTargetId;
|
||||
const std::string mSpellId;
|
||||
bool mCasting;
|
||||
bool mManual;
|
||||
float mDistance;
|
||||
const bool mManual;
|
||||
const float mDistance;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -474,26 +474,11 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
int AiCombat::getTypeId() const
|
||||
{
|
||||
return TypeIdCombat;
|
||||
}
|
||||
|
||||
unsigned int AiCombat::getPriority() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
MWWorld::Ptr AiCombat::getTarget() const
|
||||
{
|
||||
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
|
||||
}
|
||||
|
||||
AiCombat *MWMechanics::AiCombat::clone() const
|
||||
{
|
||||
return new AiCombat(*this);
|
||||
}
|
||||
|
||||
void AiCombat::writeState(ESM::AiSequence::AiSequence &sequence) const
|
||||
{
|
||||
std::unique_ptr<ESM::AiSequence::AiCombat> combat(new ESM::AiSequence::AiCombat());
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef GAME_MWMECHANICS_AICOMBAT_H
|
||||
#define GAME_MWMECHANICS_AICOMBAT_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include "typedaipackage.hpp"
|
||||
|
||||
#include "../mwworld/cellstore.hpp" // for Doors
|
||||
|
||||
|
@ -91,7 +91,7 @@ namespace MWMechanics
|
|||
};
|
||||
|
||||
/// \brief Causes the actor to fight another actor
|
||||
class AiCombat final : public AiPackage
|
||||
class AiCombat final : public TypedAiPackage<AiCombat>
|
||||
{
|
||||
public:
|
||||
///Constructor
|
||||
|
@ -102,22 +102,24 @@ namespace MWMechanics
|
|||
|
||||
void init();
|
||||
|
||||
AiCombat *clone() const 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
|
||||
MWWorld::Ptr getTarget() const final;
|
||||
|
||||
void writeState(ESM::AiSequence::AiSequence &sequence) const final;
|
||||
|
||||
bool canCancel() const final { return false; }
|
||||
bool shouldCancelPreviousAi() const final { return false; }
|
||||
|
||||
private:
|
||||
/// Returns true if combat should end
|
||||
bool attack(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, AiCombatStorage& storage, CharacterController& characterController);
|
||||
|
|
|
@ -26,7 +26,6 @@ namespace MWMechanics
|
|||
, mCellY(std::numeric_limits<int>::max())
|
||||
{
|
||||
mTargetActorRefId = actorId;
|
||||
mMaxDist = 450;
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
mTargetActorRefId = actorId;
|
||||
mMaxDist = 450;
|
||||
}
|
||||
|
||||
AiEscort::AiEscort(const ESM::AiSequence::AiEscort *escort)
|
||||
: 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)
|
||||
, mCellX(std::numeric_limits<int>::max())
|
||||
, mCellY(std::numeric_limits<int>::max())
|
||||
{
|
||||
mTargetActorRefId = escort->mTargetId;
|
||||
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)
|
||||
|
@ -106,11 +95,6 @@ namespace MWMechanics
|
|||
return false;
|
||||
}
|
||||
|
||||
int AiEscort::getTypeId() const
|
||||
{
|
||||
return TypeIdEscort;
|
||||
}
|
||||
|
||||
void AiEscort::writeState(ESM::AiSequence::AiSequence &sequence) const
|
||||
{
|
||||
std::unique_ptr<ESM::AiSequence::AiEscort> escort(new ESM::AiSequence::AiEscort());
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef GAME_MWMECHANICS_AIESCORT_H
|
||||
#define GAME_MWMECHANICS_AIESCORT_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include "typedaipackage.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -16,7 +16,7 @@ namespace AiSequence
|
|||
namespace MWMechanics
|
||||
{
|
||||
/// \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:
|
||||
/// Implementation of AiEscort
|
||||
|
@ -30,15 +30,17 @@ namespace MWMechanics
|
|||
|
||||
AiEscort(const ESM::AiSequence::AiEscort* escort);
|
||||
|
||||
AiEscort *clone() const 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; }
|
||||
|
||||
bool sideWithTarget() const final { return true; }
|
||||
static constexpr Options makeDefaultOptions()
|
||||
{
|
||||
AiPackage::Options options;
|
||||
options.mUseVariableSpeed = true;
|
||||
options.mSideWithTarget = true;
|
||||
return options;
|
||||
}
|
||||
|
||||
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); }
|
||||
|
||||
private:
|
||||
std::string mCellId;
|
||||
float mX;
|
||||
float mY;
|
||||
float mZ;
|
||||
float mMaxDist;
|
||||
float mDuration; // In hours
|
||||
const std::string mCellId;
|
||||
const float mX;
|
||||
const float mY;
|
||||
const float mZ;
|
||||
float mMaxDist = 450;
|
||||
const float mDuration; // In hours
|
||||
float mRemainingDuration; // In hours
|
||||
|
||||
int mCellX;
|
||||
int mCellY;
|
||||
const int mCellX;
|
||||
const int mCellY;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -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*/)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
int MWMechanics::AiFace::getTypeId() const
|
||||
{
|
||||
return AiPackage::TypeIdFace;
|
||||
}
|
||||
|
||||
unsigned int MWMechanics::AiFace::getPriority() const
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
|
|
@ -1,28 +1,31 @@
|
|||
#ifndef GAME_MWMECHANICS_AIFACE_H
|
||||
#define GAME_MWMECHANICS_AIFACE_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include "typedaipackage.hpp"
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
/// AiPackage which makes an actor face a certain direction.
|
||||
class AiFace final : public AiPackage {
|
||||
class AiFace final : public TypedAiPackage<AiFace> {
|
||||
public:
|
||||
AiFace(float targetX, float targetY);
|
||||
|
||||
AiPackage *clone() const 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;
|
||||
|
||||
bool canCancel() const final { return false; }
|
||||
bool shouldCancelPreviousAi() const final { return false; }
|
||||
static constexpr Options makeDefaultOptions()
|
||||
{
|
||||
AiPackage::Options options;
|
||||
options.mPriority = 2;
|
||||
options.mCanCancel = false;
|
||||
options.mShouldCancelPreviousAi = false;
|
||||
return options;
|
||||
}
|
||||
|
||||
private:
|
||||
float mTargetX, mTargetY;
|
||||
const float mTargetX;
|
||||
const float mTargetY;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -16,25 +16,24 @@
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
int AiFollow::mFollowIndexCounter = 0;
|
||||
|
||||
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++)
|
||||
{
|
||||
mTargetActorRefId = actorId;
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
mTargetActorRefId = actorId;
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
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)
|
||||
: 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++)
|
||||
{
|
||||
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)
|
||||
: 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++)
|
||||
{
|
||||
mTargetActorRefId = actor.getCellRef().getRefId();
|
||||
|
@ -58,18 +58,18 @@ AiFollow::AiFollow(const MWWorld::Ptr& actor, bool commanded)
|
|||
}
|
||||
|
||||
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)
|
||||
, mCellId(follow->mCellId), mActive(follow->mActive), mFollowIndex(mFollowIndexCounter++)
|
||||
{
|
||||
mTargetActorRefId = follow->mTargetId;
|
||||
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)
|
||||
|
@ -212,19 +212,9 @@ std::string AiFollow::getFollowedActor()
|
|||
return mTargetActorRefId;
|
||||
}
|
||||
|
||||
AiFollow *MWMechanics::AiFollow::clone() const
|
||||
{
|
||||
return new AiFollow(*this);
|
||||
}
|
||||
|
||||
int AiFollow::getTypeId() const
|
||||
{
|
||||
return TypeIdFollow;
|
||||
}
|
||||
|
||||
bool AiFollow::isCommanded() const
|
||||
{
|
||||
return mCommanded;
|
||||
return !mOptions.mShouldCancelPreviousAi;
|
||||
}
|
||||
|
||||
void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
|
||||
|
@ -238,7 +228,7 @@ void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
|
|||
follow->mRemainingDuration = mRemainingDuration;
|
||||
follow->mCellId = mCellId;
|
||||
follow->mAlwaysFollow = mAlwaysFollow;
|
||||
follow->mCommanded = mCommanded;
|
||||
follow->mCommanded = isCommanded();
|
||||
follow->mActive = mActive;
|
||||
|
||||
ESM::AiSequence::AiPackageContainer package;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef GAME_MWMECHANICS_AIFOLLOW_H
|
||||
#define GAME_MWMECHANICS_AIFOLLOW_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include "typedaipackage.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -39,7 +39,7 @@ namespace MWMechanics
|
|||
/// \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
|
||||
**/
|
||||
class AiFollow final : public AiPackage
|
||||
class AiFollow final : public TypedAiPackage<AiFollow>
|
||||
{
|
||||
public:
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
std::string getFollowedActor();
|
||||
|
@ -98,16 +99,15 @@ namespace MWMechanics
|
|||
private:
|
||||
/// This will make the actor always follow.
|
||||
/** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/
|
||||
bool mAlwaysFollow;
|
||||
bool mCommanded;
|
||||
float mDuration; // Hours
|
||||
const bool mAlwaysFollow;
|
||||
const float mDuration; // Hours
|
||||
float mRemainingDuration; // Hours
|
||||
float mX;
|
||||
float mY;
|
||||
float mZ;
|
||||
std::string mCellId;
|
||||
const float mX;
|
||||
const float mY;
|
||||
const float mZ;
|
||||
const std::string mCellId;
|
||||
bool mActive; // have we spotted the target?
|
||||
int mFollowIndex;
|
||||
const int mFollowIndex;
|
||||
|
||||
static int mFollowIndexCounter;
|
||||
|
||||
|
|
|
@ -24,7 +24,9 @@
|
|||
|
||||
#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
|
||||
mTargetActorRefId(""),
|
||||
mTargetActorId(-1),
|
||||
|
@ -58,31 +60,6 @@ MWWorld::Ptr MWMechanics::AiPackage::getTarget() const
|
|||
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()
|
||||
{
|
||||
// reset all members
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef GAME_MWMECHANICS_AIPACKAGE_H
|
||||
#define GAME_MWMECHANICS_AIPACKAGE_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <components/esm/defs.hpp>
|
||||
|
||||
#include "pathfinding.hpp"
|
||||
|
@ -53,13 +55,41 @@ namespace MWMechanics
|
|||
TypeIdCast = 11
|
||||
};
|
||||
|
||||
///Default constructor
|
||||
AiPackage();
|
||||
struct Options
|
||||
{
|
||||
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;
|
||||
|
||||
static constexpr Options makeDefaultOptions()
|
||||
{
|
||||
return Options{};
|
||||
}
|
||||
|
||||
///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)
|
||||
/// \return Package completed?
|
||||
|
@ -67,13 +97,13 @@ namespace MWMechanics
|
|||
|
||||
/// Returns the TypeID of the AiPackage
|
||||
/// \see enum TypeId
|
||||
virtual int getTypeId() const = 0;
|
||||
TypeId getTypeId() const { return mTypeId; }
|
||||
|
||||
/// 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
|
||||
virtual bool useVariableSpeed() const { return false;}
|
||||
bool useVariableSpeed() const { return mOptions.mUseVariableSpeed; }
|
||||
|
||||
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); };
|
||||
|
||||
/// 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)
|
||||
virtual bool followTargetThroughDoors() const;
|
||||
bool followTargetThroughDoors() const { return mOptions.mFollowTargetThroughDoors; }
|
||||
|
||||
/// 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)?
|
||||
virtual bool shouldCancelPreviousAi() const;
|
||||
bool shouldCancelPreviousAi() const { return mOptions.mShouldCancelPreviousAi; }
|
||||
|
||||
/// 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); }
|
||||
|
||||
// Return true if any loaded actor with this AI package must be active.
|
||||
virtual bool alwaysActive() const { return false; }
|
||||
/// Return true if any loaded actor with this AI package must be active.
|
||||
bool alwaysActive() const { return mOptions.mAlwaysActive; }
|
||||
|
||||
/// Reset pathfinding state
|
||||
void reset();
|
||||
|
@ -137,6 +167,9 @@ namespace MWMechanics
|
|||
|
||||
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
|
||||
PathFinder mPathFinder;
|
||||
ObstacleCheck mObstacleCheck;
|
||||
|
|
|
@ -40,10 +40,6 @@ AiPursue::AiPursue(const ESM::AiSequence::AiPursue *pursue)
|
|||
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)
|
||||
{
|
||||
if(actor.getClass().getCreatureStats(actor).isDead())
|
||||
|
@ -116,11 +112,6 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, CharacterController& characte
|
|||
return false;
|
||||
}
|
||||
|
||||
int AiPursue::getTypeId() const
|
||||
{
|
||||
return TypeIdPursue;
|
||||
}
|
||||
|
||||
MWWorld::Ptr AiPursue::getTarget() const
|
||||
{
|
||||
return MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef GAME_MWMECHANICS_AIPURSUE_H
|
||||
#define GAME_MWMECHANICS_AIPURSUE_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include "typedaipackage.hpp"
|
||||
|
||||
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.
|
||||
Note that while very similar to AiActivate, it will ONLY activate when evry close to target (Not also when the
|
||||
path is completed). **/
|
||||
class AiPursue final : public AiPackage
|
||||
class AiPursue final : public TypedAiPackage<AiPursue>
|
||||
{
|
||||
public:
|
||||
///Constructor
|
||||
|
@ -26,16 +26,21 @@ namespace MWMechanics
|
|||
|
||||
AiPursue(const ESM::AiSequence::AiPursue* pursue);
|
||||
|
||||
AiPursue *clone() const 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;
|
||||
|
||||
void writeState (ESM::AiSequence::AiSequence& sequence) const final;
|
||||
|
||||
bool canCancel() const final { return false; }
|
||||
bool shouldCancelPreviousAi() const final { return false; }
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -25,9 +25,8 @@ namespace MWMechanics
|
|||
|
||||
void AiSequence::copy (const AiSequence& sequence)
|
||||
{
|
||||
for (std::list<AiPackage *>::const_iterator iter (sequence.mPackages.begin());
|
||||
iter!=sequence.mPackages.end(); ++iter)
|
||||
mPackages.push_back ((*iter)->clone());
|
||||
for (const auto& package : sequence.mPackages)
|
||||
mPackages.push_back(package->clone());
|
||||
|
||||
// We need to keep an AiWander storage, if present - it has a state machine.
|
||||
// Not sure about another temporary storages
|
||||
|
@ -74,7 +73,7 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const
|
|||
{
|
||||
if (getTypeId() != AiPackage::TypeIdCombat)
|
||||
return false;
|
||||
|
||||
|
||||
targetActor = mPackages.front()->getTarget();
|
||||
|
||||
return !targetActor.isEmpty();
|
||||
|
@ -82,7 +81,7 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) 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)
|
||||
targetActors.push_back((*it)->getTarget());
|
||||
|
@ -91,24 +90,23 @@ bool AiSequence::getCombatTargets(std::vector<MWWorld::Ptr> &targetActors) const
|
|||
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();
|
||||
}
|
||||
|
||||
std::list<AiPackage*>::const_iterator AiSequence::end() const
|
||||
std::list<std::unique_ptr<AiPackage>>::const_iterator AiSequence::end() const
|
||||
{
|
||||
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?
|
||||
for(std::list<AiPackage*>::iterator it = mPackages.begin(); it != mPackages.end(); ++it)
|
||||
for(auto it = mPackages.begin(); it != mPackages.end(); ++it)
|
||||
{
|
||||
if (package == it)
|
||||
{
|
||||
delete *it;
|
||||
mPackages.erase(it);
|
||||
return;
|
||||
}
|
||||
|
@ -118,7 +116,7 @@ void AiSequence::erase(std::list<AiPackage*>::const_iterator package)
|
|||
|
||||
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)
|
||||
return true;
|
||||
|
@ -128,7 +126,7 @@ bool AiSequence::isInCombat() 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)
|
||||
{
|
||||
|
@ -142,7 +140,7 @@ bool AiSequence::isEngagedWithActor() 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)
|
||||
return true;
|
||||
|
@ -152,7 +150,7 @@ bool AiSequence::hasPackage(int typeId) 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)
|
||||
{
|
||||
|
@ -165,11 +163,10 @@ bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const
|
|||
|
||||
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)
|
||||
{
|
||||
delete *it;
|
||||
it = mPackages.erase(it);
|
||||
}
|
||||
else
|
||||
|
@ -179,11 +176,10 @@ void AiSequence::stopCombat()
|
|||
|
||||
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)
|
||||
{
|
||||
delete *it;
|
||||
it = mPackages.erase(it);
|
||||
}
|
||||
else
|
||||
|
@ -213,7 +209,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
|||
}
|
||||
|
||||
auto packageIt = mPackages.begin();
|
||||
MWMechanics::AiPackage* package = *packageIt;
|
||||
MWMechanics::AiPackage* package = packageIt->get();
|
||||
if (!package->alwaysActive() && outOfRange)
|
||||
return;
|
||||
|
||||
|
@ -231,7 +227,7 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
|||
|
||||
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;
|
||||
|
||||
|
@ -240,7 +236,6 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
|||
// target disappeared (e.g. summoned creatures)
|
||||
if (target.isEmpty())
|
||||
{
|
||||
delete *it;
|
||||
it = mPackages.erase(it);
|
||||
}
|
||||
else
|
||||
|
@ -276,13 +271,13 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
|||
}
|
||||
|
||||
packageIt = mPackages.begin();
|
||||
package = *packageIt;
|
||||
package = packageIt->get();
|
||||
packageTypeId = package->getTypeId();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (package->execute (actor, characterController, mAiState, duration))
|
||||
if (package->execute(actor, characterController, mAiState, duration))
|
||||
{
|
||||
// Put repeating noncombat AI packages on the end of the stack so they can be used again
|
||||
if (isActualAiPackage(packageTypeId) && (mRepeat || package->getRepeat()))
|
||||
|
@ -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
|
||||
// (e.g. AiPursue executing a dialogue script that uses startCombat)
|
||||
mPackages.erase(packageIt);
|
||||
delete package;
|
||||
if (isActualAiPackage(packageTypeId))
|
||||
mDone = true;
|
||||
}
|
||||
|
@ -311,9 +305,6 @@ void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& charac
|
|||
|
||||
void AiSequence::clear()
|
||||
{
|
||||
for (std::list<AiPackage *>::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter)
|
||||
delete *iter;
|
||||
|
||||
mPackages.clear();
|
||||
}
|
||||
|
||||
|
@ -340,26 +331,24 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
|
|||
osg::Vec3f dest;
|
||||
if (currentTypeId == MWMechanics::AiPackage::TypeIdWander)
|
||||
{
|
||||
AiPackage* activePackage = getActivePackage();
|
||||
dest = activePackage->getDestination(actor);
|
||||
dest = getActivePackage().getDestination(actor);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// remove previous packages if required
|
||||
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())
|
||||
{
|
||||
delete *it;
|
||||
it = mPackages.erase(it);
|
||||
}
|
||||
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
|
||||
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.
|
||||
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCast &&
|
||||
|
@ -380,12 +369,12 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo
|
|||
|
||||
if((*it)->getPriority() <= package.getPriority())
|
||||
{
|
||||
mPackages.insert(it,package.clone());
|
||||
mPackages.insert(it, package.clone());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mPackages.push_back (package.clone());
|
||||
mPackages.push_back(package.clone());
|
||||
|
||||
// Make sure that temporary storage is empty
|
||||
if (cancelOther)
|
||||
|
@ -401,12 +390,11 @@ bool MWMechanics::AiSequence::isEmpty() const
|
|||
return mPackages.empty();
|
||||
}
|
||||
|
||||
AiPackage* MWMechanics::AiSequence::getActivePackage()
|
||||
const AiPackage& MWMechanics::AiSequence::getActivePackage()
|
||||
{
|
||||
if(mPackages.empty())
|
||||
throw std::runtime_error(std::string("No AI Package!"));
|
||||
else
|
||||
return mPackages.front();
|
||||
return *mPackages.front();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
MWMechanics::AiPackage* package;
|
||||
std::unique_ptr<MWMechanics::AiPackage> package;
|
||||
if (it->mType == ESM::AI_Wander)
|
||||
{
|
||||
ESM::AIWander data = it->mWander;
|
||||
|
@ -425,38 +413,36 @@ void AiSequence::fill(const ESM::AIPackageList &list)
|
|||
idles.reserve(8);
|
||||
for (int i=0; i<8; ++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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
for (std::list<AiPackage *>::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter)
|
||||
{
|
||||
(*iter)->writeState(sequence);
|
||||
}
|
||||
for (const auto& package : mPackages)
|
||||
package->writeState(sequence);
|
||||
|
||||
sequence.mLastAiPackage = mLastAiPackage;
|
||||
}
|
||||
|
@ -492,7 +478,11 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
|||
}
|
||||
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;
|
||||
}
|
||||
case ESM::AiSequence::Ai_Escort:
|
||||
|
@ -527,7 +517,7 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
|
|||
if (!package.get())
|
||||
continue;
|
||||
|
||||
mPackages.push_back(package.release());
|
||||
mPackages.push_back(std::move(package));
|
||||
}
|
||||
|
||||
mLastAiPackage = sequence.mLastAiPackage;
|
||||
|
@ -537,8 +527,7 @@ void AiSequence::fastForward(const MWWorld::Ptr& actor)
|
|||
{
|
||||
if (!mPackages.empty())
|
||||
{
|
||||
MWMechanics::AiPackage* package = mPackages.front();
|
||||
package->fastForward(actor, mAiState);
|
||||
mPackages.front()->fastForward(actor, mAiState);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define GAME_MWMECHANICS_AISEQUENCE_H
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include "aistate.hpp"
|
||||
|
||||
|
@ -36,7 +37,7 @@ namespace MWMechanics
|
|||
class AiSequence
|
||||
{
|
||||
///AiPackages to run though
|
||||
std::list<AiPackage *> mPackages;
|
||||
std::list<std::unique_ptr<AiPackage>> mPackages;
|
||||
|
||||
///Finished with top AIPackage, set for one frame
|
||||
bool mDone;
|
||||
|
@ -64,10 +65,10 @@ namespace MWMechanics
|
|||
virtual ~AiSequence();
|
||||
|
||||
/// Iterator may be invalidated by any function calls other than begin() or end().
|
||||
std::list<AiPackage*>::const_iterator begin() const;
|
||||
std::list<AiPackage*>::const_iterator end() const;
|
||||
std::list<std::unique_ptr<AiPackage>>::const_iterator begin() 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
|
||||
/** \see enum AiPackage::TypeId **/
|
||||
|
@ -125,7 +126,7 @@ namespace MWMechanics
|
|||
|
||||
/// Return the current active package.
|
||||
/** If there is no active package, it will throw an exception **/
|
||||
AiPackage* getActivePackage();
|
||||
const AiPackage& getActivePackage();
|
||||
|
||||
/// Fills the AiSequence with packages
|
||||
/** Typically used for loading from the ESM
|
||||
|
|
|
@ -27,19 +27,26 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2)
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
AiTravel::AiTravel(float x, float y, float z, bool hidden)
|
||||
: mX(x),mY(y),mZ(z),mHidden(hidden)
|
||||
AiTravel::AiTravel(float x, float y, float z, AiTravel*)
|
||||
: 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)
|
||||
: 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)
|
||||
{
|
||||
}
|
||||
|
||||
AiTravel *MWMechanics::AiTravel::clone() const
|
||||
{
|
||||
return new AiTravel(*this);
|
||||
// Hidden ESM::AiSequence::AiTravel package should be converted into MWMechanics::AiInternalTravel type
|
||||
assert(!travel->mHidden);
|
||||
}
|
||||
|
||||
bool AiTravel::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)
|
||||
|
@ -83,11 +90,6 @@ namespace MWMechanics
|
|||
return false;
|
||||
}
|
||||
|
||||
int AiTravel::getTypeId() const
|
||||
{
|
||||
return mHidden ? TypeIdInternalTravel : TypeIdTravel;
|
||||
}
|
||||
|
||||
void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state)
|
||||
{
|
||||
if (!isWithinMaxRange(osg::Vec3f(mX, mY, mZ), actor.getRefData().getPosition().asVec3()))
|
||||
|
@ -112,5 +114,20 @@ namespace MWMechanics
|
|||
package.mPackage = travel.release();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef GAME_MWMECHANICS_AITRAVEL_H
|
||||
#define GAME_MWMECHANICS_AITRAVEL_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include "typedaipackage.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
@ -13,12 +13,18 @@ namespace AiSequence
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
struct AiInternalTravel;
|
||||
|
||||
/// \brief Causes the AI to travel to the specified point
|
||||
class AiTravel final : public AiPackage
|
||||
class AiTravel : public TypedAiPackage<AiTravel>
|
||||
{
|
||||
public:
|
||||
/// Default constructor
|
||||
AiTravel(float x, float y, float z, bool hidden = false);
|
||||
AiTravel(float x, float y, float z, AiTravel* derived);
|
||||
|
||||
AiTravel(float x, float y, float z, AiInternalTravel* derived);
|
||||
|
||||
AiTravel(float x, float y, float z);
|
||||
|
||||
AiTravel(const ESM::AiSequence::AiTravel* travel);
|
||||
|
||||
/// Simulates the passing of time
|
||||
|
@ -26,24 +32,37 @@ namespace MWMechanics
|
|||
|
||||
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;
|
||||
|
||||
int getTypeId() const final;
|
||||
static constexpr TypeId getTypeId() { return TypeIdTravel; }
|
||||
|
||||
bool useVariableSpeed() const final { return true; }
|
||||
|
||||
bool alwaysActive() const final { return true; }
|
||||
static constexpr Options makeDefaultOptions()
|
||||
{
|
||||
AiPackage::Options options;
|
||||
options.mUseVariableSpeed = true;
|
||||
options.mAlwaysActive = true;
|
||||
return options;
|
||||
}
|
||||
|
||||
osg::Vec3f getDestination() const final { return osg::Vec3f(mX, mY, mZ); }
|
||||
|
||||
private:
|
||||
float mX;
|
||||
float mY;
|
||||
float mZ;
|
||||
const float mX;
|
||||
const float mY;
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "aiwander.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/misc/rng.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.
|
||||
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] =
|
||||
{
|
||||
std::string("idle2"),
|
||||
|
@ -94,30 +98,29 @@ namespace MWMechanics
|
|||
{
|
||||
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):
|
||||
mDistance(distance), mDuration(duration), mRemainingDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle),
|
||||
mRepeat(repeat), mStoredInitialActorPosition(false), mInitialActorPosition(osg::Vec3f(0, 0, 0)),
|
||||
TypedAiPackage<AiWander>(makeDefaultOptions().withRepeat(repeat)),
|
||||
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)
|
||||
{
|
||||
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);
|
||||
// Reset package so it can be used again
|
||||
mRemainingDuration=mDuration;
|
||||
init();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -308,11 +310,6 @@ namespace MWMechanics
|
|||
return false; // AiWander package not yet completed
|
||||
}
|
||||
|
||||
bool AiWander::getRepeat() const
|
||||
{
|
||||
return mRepeat;
|
||||
}
|
||||
|
||||
osg::Vec3f AiWander::getDestination(const MWWorld::Ptr& actor) const
|
||||
{
|
||||
if (mHasDestination)
|
||||
|
@ -598,11 +595,6 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
int AiWander::getTypeId() const
|
||||
{
|
||||
return TypeIdWander;
|
||||
}
|
||||
|
||||
void AiWander::stopWalking(const MWWorld::Ptr& actor)
|
||||
{
|
||||
mPathFinder.clearPath();
|
||||
|
@ -872,7 +864,7 @@ namespace MWMechanics
|
|||
assert (mIdle.size() == 8);
|
||||
for (int i=0; i<8; ++i)
|
||||
wander->mData.mIdle[i] = mIdle[i];
|
||||
wander->mData.mShouldRepeat = mRepeat;
|
||||
wander->mData.mShouldRepeat = mOptions.mRepeat;
|
||||
wander->mStoredInitialActorPosition = mStoredInitialActorPosition;
|
||||
if (mStoredInitialActorPosition)
|
||||
wander->mInitialActorPosition = mInitialActorPosition;
|
||||
|
@ -884,11 +876,12 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
AiWander::AiWander (const ESM::AiSequence::AiWander* wander)
|
||||
: mDistance(wander->mData.mDistance)
|
||||
, mDuration(wander->mData.mDuration)
|
||||
: TypedAiPackage<AiWander>(makeDefaultOptions().withRepeat(wander->mData.mShouldRepeat != 0))
|
||||
, mDistance(std::max(static_cast<short>(0), wander->mData.mDistance))
|
||||
, mDuration(std::max(static_cast<short>(0), wander->mData.mDuration))
|
||||
, mRemainingDuration(wander->mDurationData.mRemainingDuration)
|
||||
, mTimeOfDay(wander->mData.mTimeOfDay)
|
||||
, mRepeat(wander->mData.mShouldRepeat != 0)
|
||||
, mIdle(getInitialIdle(wander->mData.mIdle))
|
||||
, mStoredInitialActorPosition(wander->mStoredInitialActorPosition)
|
||||
, mHasDestination(false)
|
||||
, mDestination(osg::Vec3f(0, 0, 0))
|
||||
|
@ -896,11 +889,7 @@ namespace MWMechanics
|
|||
{
|
||||
if (mStoredInitialActorPosition)
|
||||
mInitialActorPosition = wander->mInitialActorPosition;
|
||||
for (int i=0; i<8; ++i)
|
||||
mIdle.push_back(wander->mData.mIdle[i]);
|
||||
if (mRemainingDuration <= 0 || mRemainingDuration >= 24)
|
||||
mRemainingDuration = mDuration;
|
||||
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef GAME_MWMECHANICS_AIWANDER_H
|
||||
#define GAME_MWMECHANICS_AIWANDER_H
|
||||
|
||||
#include "aipackage.hpp"
|
||||
#include "typedaipackage.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
@ -78,7 +78,7 @@ namespace MWMechanics
|
|||
};
|
||||
|
||||
/// \brief Causes the Actor to wander within a specified range
|
||||
class AiWander final : public AiPackage
|
||||
class AiWander final : public TypedAiPackage<AiWander>
|
||||
{
|
||||
public:
|
||||
/// Constructor
|
||||
|
@ -91,20 +91,22 @@ namespace MWMechanics
|
|||
|
||||
AiWander (const ESM::AiSequence::AiWander* wander);
|
||||
|
||||
AiPackage *clone() const 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 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 final
|
||||
|
@ -116,8 +118,6 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
private:
|
||||
// NOTE: mDistance and mDuration must be set already
|
||||
void init();
|
||||
void stopWalking(const MWWorld::Ptr& actor);
|
||||
|
||||
/// Have the given actor play an idle animation
|
||||
|
@ -138,12 +138,11 @@ namespace MWMechanics
|
|||
bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);
|
||||
void completeManualWalking(const MWWorld::Ptr &actor, AiWanderStorage &storage);
|
||||
|
||||
int mDistance; // how far the actor can wander from the spawn point
|
||||
int mDuration;
|
||||
const int mDistance; // how far the actor can wander from the spawn point
|
||||
const int mDuration;
|
||||
float mRemainingDuration;
|
||||
int mTimeOfDay;
|
||||
std::vector<unsigned char> mIdle;
|
||||
bool mRepeat;
|
||||
const int mTimeOfDay;
|
||||
const std::vector<unsigned char> mIdle;
|
||||
|
||||
bool mStoredInitialActorPosition;
|
||||
osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell
|
||||
|
@ -178,7 +177,7 @@ namespace MWMechanics
|
|||
static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1];
|
||||
|
||||
static int OffsetToPreventOvercrowding();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -507,7 +507,7 @@ MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const
|
|||
|
||||
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 =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fWortChanceValue")->mValue.getFloat();
|
||||
return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue)
|
||||
|
|
|
@ -2319,7 +2319,7 @@ void CharacterController::update(float duration, bool animationOnly)
|
|||
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 (!godmode)
|
||||
|
|
|
@ -116,7 +116,7 @@ namespace MWMechanics
|
|||
blockerTerm *= gmst.find("fBlockStillBonus")->mValue.getFloat();
|
||||
blockerTerm *= blockerStats.getFatigueTerm();
|
||||
|
||||
int attackerSkill = 0;
|
||||
float attackerSkill = 0;
|
||||
if (weapon.isEmpty())
|
||||
attackerSkill = attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand);
|
||||
else
|
||||
|
|
|
@ -137,7 +137,7 @@ namespace MWMechanics
|
|||
return mMagicEffects;
|
||||
}
|
||||
|
||||
void CreatureStats::setAttribute(int index, int base)
|
||||
void CreatureStats::setAttribute(int index, float base)
|
||||
{
|
||||
AttributeValue current = getAttribute(index);
|
||||
current.setBase(base);
|
||||
|
@ -163,10 +163,10 @@ namespace MWMechanics
|
|||
index == ESM::Attribute::Agility ||
|
||||
index == ESM::Attribute::Endurance)
|
||||
{
|
||||
int strength = getAttribute(ESM::Attribute::Strength).getModified();
|
||||
int willpower = getAttribute(ESM::Attribute::Willpower).getModified();
|
||||
int agility = getAttribute(ESM::Attribute::Agility).getModified();
|
||||
int endurance = getAttribute(ESM::Attribute::Endurance).getModified();
|
||||
float strength = getAttribute(ESM::Attribute::Strength).getModified();
|
||||
float willpower = getAttribute(ESM::Attribute::Willpower).getModified();
|
||||
float agility = getAttribute(ESM::Attribute::Agility).getModified();
|
||||
float endurance = getAttribute(ESM::Attribute::Endurance).getModified();
|
||||
DynamicStat<float> fatigue = getFatigue();
|
||||
float diff = (strength+willpower+agility+endurance) - fatigue.getBase();
|
||||
float currentToBaseRatio = fatigue.getBase() > 0 ? (fatigue.getCurrent() / fatigue.getBase()) : 0;
|
||||
|
@ -575,6 +575,14 @@ namespace MWMechanics
|
|||
state.mHasAiSettings = true;
|
||||
for (int i=0; i<4; ++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)
|
||||
|
@ -613,7 +621,7 @@ namespace MWMechanics
|
|||
mTimeOfDeath = MWWorld::TimeStamp(state.mTimeOfDeath);
|
||||
//mHitAttemptActorId = state.mHitAttemptActorId;
|
||||
|
||||
mSpells.readState(state.mSpells);
|
||||
mSpells.readState(state.mSpells, this);
|
||||
mActiveSpells.readState(state.mActiveSpells);
|
||||
mAiSequence.readState(state.mAiSequence);
|
||||
mMagicEffects.readState(state.mMagicEffects);
|
||||
|
@ -624,6 +632,15 @@ namespace MWMechanics
|
|||
if (state.mHasAiSettings)
|
||||
for (int i=0; i<4; ++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)
|
||||
|
@ -722,4 +739,23 @@ namespace MWMechanics
|
|||
/*
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include "aisequence.hpp"
|
||||
#include "drawstate.hpp"
|
||||
|
||||
#include <components/esm/attr.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct CreatureStats;
|
||||
|
@ -19,6 +21,14 @@ namespace ESM
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
struct CorprusStats
|
||||
{
|
||||
static const int sWorseningPeriod = 24;
|
||||
|
||||
int mWorsenings[ESM::Attribute::Length];
|
||||
MWWorld::TimeStamp mNextWorsening;
|
||||
};
|
||||
|
||||
/// \brief Common creature stats
|
||||
///
|
||||
///
|
||||
|
@ -26,7 +36,7 @@ namespace MWMechanics
|
|||
{
|
||||
static int sActorId;
|
||||
DrawState_ mDrawState;
|
||||
AttributeValue mAttributes[8];
|
||||
AttributeValue mAttributes[ESM::Attribute::Length];
|
||||
DynamicStat<float> mDynamic[3]; // health, magicka, fatigue
|
||||
Spells mSpells;
|
||||
ActiveSpells mActiveSpells;
|
||||
|
@ -79,6 +89,8 @@ namespace MWMechanics
|
|||
// This may be necessary when the creature is in an inactive cell.
|
||||
std::vector<int> mSummonGraveyard;
|
||||
|
||||
std::map<std::string, CorprusStats> mCorprusSpells;
|
||||
|
||||
protected:
|
||||
int mLevel;
|
||||
|
||||
|
@ -126,7 +138,7 @@ namespace MWMechanics
|
|||
|
||||
void setAttribute(int index, const AttributeValue &value);
|
||||
// Shortcut to set only the base
|
||||
void setAttribute(int index, int base);
|
||||
void setAttribute(int index, float base);
|
||||
|
||||
void setHealth(const DynamicStat<float> &value);
|
||||
|
||||
|
@ -301,6 +313,12 @@ namespace MWMechanics
|
|||
/// assigned this function will return false).
|
||||
|
||||
static void cleanup();
|
||||
|
||||
std::map<std::string, CorprusStats> & getCorprusSpells();
|
||||
|
||||
void addCorprusSpell(const std::string& sourceId, CorprusStats& stats);
|
||||
|
||||
void removeCorprusSpell(const std::string& sourceId);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -303,6 +303,24 @@ namespace MWMechanics
|
|||
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)
|
||||
{
|
||||
if(!mWatched.isEmpty())
|
||||
|
@ -684,10 +702,10 @@ namespace MWMechanics
|
|||
// 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...
|
||||
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 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 f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f);
|
||||
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 (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;
|
||||
}
|
||||
if (alarm)
|
||||
|
@ -1743,8 +1761,8 @@ namespace MWMechanics
|
|||
static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat();
|
||||
static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat();
|
||||
float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak));
|
||||
int agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
|
||||
int luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
float agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
|
||||
float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
float bootWeight = 0;
|
||||
if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr))
|
||||
{
|
||||
|
@ -1767,10 +1785,10 @@ namespace MWMechanics
|
|||
float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility;
|
||||
|
||||
CreatureStats& observerStats = observer.getClass().getCreatureStats(observer);
|
||||
int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified();
|
||||
int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
float obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified();
|
||||
float obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
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;
|
||||
|
||||
|
|
|
@ -277,6 +277,8 @@ namespace MWMechanics
|
|||
virtual GreetingState getGreetingState(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:
|
||||
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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
int base = getSkill (skillIndex).getBase();
|
||||
float base = getSkill (skillIndex).getBase();
|
||||
|
||||
if (base >= 100)
|
||||
if (base >= 100.f)
|
||||
return;
|
||||
|
||||
base += 1;
|
||||
|
@ -265,7 +265,7 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas
|
|||
MWBase::Environment::get().getWindowManager()->playSound("skillraise");
|
||||
|
||||
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)
|
||||
message = "#{sBookSkillMessage}\n" + message;
|
||||
|
@ -360,7 +360,7 @@ void MWMechanics::NpcStats::levelUp()
|
|||
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||
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
|
||||
// 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()
|
||||
{
|
||||
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase();
|
||||
const int strength = getAttribute(ESM::Attribute::Strength).getBase();
|
||||
const float endurance = getAttribute(ESM::Attribute::Endurance).getBase();
|
||||
const float strength = getAttribute(ESM::Attribute::Strength).getBase();
|
||||
|
||||
setHealth(floor(0.5f * (strength + endurance)));
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ namespace MWMechanics
|
|||
float Pickpocket::getChanceModifier(const MWWorld::Ptr &ptr, float add)
|
||||
{
|
||||
NpcStats& stats = ptr.getClass().getNpcStats(ptr);
|
||||
float agility = static_cast<float>(stats.getAttribute(ESM::Attribute::Agility).getModified());
|
||||
float luck = static_cast<float>(stats.getAttribute(ESM::Attribute::Luck).getModified());
|
||||
float agility = stats.getAttribute(ESM::Attribute::Agility).getModified();
|
||||
float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
float sneak = static_cast<float>(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak));
|
||||
return (add + 0.2f * agility + 0.1f * luck + sneak) * stats.getFatigueTerm();
|
||||
}
|
||||
|
|
|
@ -45,9 +45,9 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair)
|
|||
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
|
||||
|
||||
float fatigueTerm = stats.getFatigueTerm();
|
||||
int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified();
|
||||
int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
int armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer);
|
||||
float pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified();
|
||||
float pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
float armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer);
|
||||
|
||||
float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
||||
.find("fRepairAmountMult")->mValue.getFloat();
|
||||
|
|
|
@ -33,8 +33,8 @@ namespace MWMechanics
|
|||
: mActor(actor)
|
||||
{
|
||||
CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor);
|
||||
mAgility = static_cast<float>(creatureStats.getAttribute(ESM::Attribute::Agility).getModified());
|
||||
mLuck = static_cast<float>(creatureStats.getAttribute(ESM::Attribute::Luck).getModified());
|
||||
mAgility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified();
|
||||
mLuck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
mSecuritySkill = static_cast<float>(actor.getClass().getSkill(actor, ESM::Skill::Security));
|
||||
mFatigueTerm = creatureStats.getFatigueTerm();
|
||||
}
|
||||
|
|
|
@ -77,8 +77,13 @@ namespace MWMechanics
|
|||
void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster,
|
||||
const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded)
|
||||
{
|
||||
if (!target.isEmpty() && target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead())
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
// If none of the effects need to apply, we can early-out
|
||||
bool found = false;
|
||||
|
@ -218,9 +223,9 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,8 +40,8 @@ namespace MWMechanics
|
|||
|
||||
float resistance = getEffectResistanceAttribute(effectId, magicEffects);
|
||||
|
||||
int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
|
||||
float luck = static_cast<float>(stats.getAttribute(ESM::Attribute::Luck).getModified());
|
||||
float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
|
||||
float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
float x = (willpower + 0.1f * luck) * stats.getFatigueTerm();
|
||||
|
||||
// This makes spells that are easy to cast harder to resist and vice versa
|
||||
|
|
|
@ -18,9 +18,13 @@
|
|||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "actorutil.hpp"
|
||||
#include "creaturestats.hpp"
|
||||
#include "magiceffects.hpp"
|
||||
#include "stat.hpp"
|
||||
|
||||
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
|
||||
|
@ -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;
|
||||
params.mEffectRands = random;
|
||||
mSpells.insert (std::make_pair (spell, params));
|
||||
|
@ -138,27 +127,6 @@ namespace MWMechanics
|
|||
const ESM::Spell* spell = getSpell(spellId);
|
||||
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())
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
const std::map<Spells::SpellKey, Spells::CorprusStats> &Spells::getCorprusSpells() const
|
||||
{
|
||||
return mCorprusSpells;
|
||||
}
|
||||
|
||||
void Spells::purgeEffect(int effectId)
|
||||
{
|
||||
for (TContainer::iterator spellIt = mSpells.begin(); spellIt != mSpells.end(); ++spellIt)
|
||||
|
@ -463,7 +401,7 @@ namespace MWMechanics
|
|||
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)
|
||||
{
|
||||
|
@ -487,6 +425,32 @@ namespace MWMechanics
|
|||
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 =
|
||||
state.mPermanentSpellEffects.begin(); it != state.mPermanentSpellEffects.end(); ++it)
|
||||
{
|
||||
|
@ -494,24 +458,31 @@ namespace MWMechanics
|
|||
if (!spell)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
AttributeValue attr = creatureStats->getAttribute(effectIt->mArg);
|
||||
attr.setModifier(attr.getModifier() - effectIt->mMagnitude);
|
||||
attr.damage(-effectIt->mMagnitude);
|
||||
creatureStats->setAttribute(effectIt->mArg, attr);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (!spell) // Discard unavailable corprus spells
|
||||
continue;
|
||||
mCorprusSpells[spell].mWorsenings = state.mCorprusSpells.at(it->first).mWorsenings;
|
||||
mCorprusSpells[spell].mNextWorsening = MWWorld::TimeStamp(state.mCorprusSpells.at(it->first).mNextWorsening);
|
||||
}
|
||||
|
||||
mSpellsChanged = true;
|
||||
}
|
||||
|
||||
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)
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ namespace ESM
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
class CreatureStats;
|
||||
|
||||
class MagicEffects;
|
||||
|
||||
/// \brief Spell list
|
||||
|
@ -33,7 +35,8 @@ namespace MWMechanics
|
|||
public:
|
||||
|
||||
typedef const ESM::Spell* SpellKey;
|
||||
struct SpellParams {
|
||||
struct SpellParams
|
||||
{
|
||||
std::map<int, float> mEffectRands; // <effect index, normalised random magnitude>
|
||||
std::set<int> mPurgedEffects; // indices of purged effects
|
||||
};
|
||||
|
@ -41,27 +44,14 @@ namespace MWMechanics
|
|||
typedef std::map<SpellKey, SpellParams> TContainer;
|
||||
typedef TContainer::const_iterator TIterator;
|
||||
|
||||
struct CorprusStats
|
||||
{
|
||||
static const int sWorseningPeriod = 24;
|
||||
|
||||
int mWorsenings;
|
||||
MWWorld::TimeStamp mNextWorsening;
|
||||
};
|
||||
|
||||
private:
|
||||
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)
|
||||
std::string mSelectedSpell;
|
||||
|
||||
std::map<SpellKey, MWWorld::TimeStamp> mUsedPowers;
|
||||
|
||||
std::map<SpellKey, CorprusStats> mCorprusSpells;
|
||||
|
||||
mutable bool mSpellsChanged;
|
||||
mutable MagicEffects mEffects;
|
||||
mutable std::map<SpellKey, MagicEffects> mSourcedEffects;
|
||||
|
@ -73,9 +63,7 @@ namespace MWMechanics
|
|||
public:
|
||||
Spells();
|
||||
|
||||
void worsenCorprus(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, const std::string & sourceId);
|
||||
|
@ -128,7 +116,7 @@ namespace MWMechanics
|
|||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -94,8 +94,8 @@ namespace MWMechanics
|
|||
|
||||
CreatureStats& stats = actor.getClass().getCreatureStats(actor);
|
||||
|
||||
int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
|
||||
int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
float actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
|
||||
float actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();
|
||||
|
||||
float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck);
|
||||
|
||||
|
|
|
@ -227,39 +227,46 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
int AttributeValue::getModifier() const
|
||||
float AttributeValue::getModifier() const
|
||||
{
|
||||
return mModifier;
|
||||
}
|
||||
|
||||
void AttributeValue::setBase(int base)
|
||||
void AttributeValue::setBase(float base)
|
||||
{
|
||||
mBase = base;
|
||||
}
|
||||
|
||||
void AttributeValue::setModifier(int mod)
|
||||
void AttributeValue::setModifier(float mod)
|
||||
{
|
||||
mModifier = mod;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (mDamage <= 0) return;
|
||||
|
||||
mDamage -= std::min(mDamage, amount);
|
||||
}
|
||||
|
||||
|
@ -268,14 +275,14 @@ namespace MWMechanics
|
|||
return mDamage;
|
||||
}
|
||||
|
||||
void AttributeValue::writeState (ESM::StatState<int>& state) const
|
||||
void AttributeValue::writeState (ESM::StatState<float>& state) const
|
||||
{
|
||||
state.mBase = mBase;
|
||||
state.mMod = mModifier;
|
||||
state.mDamage = mDamage;
|
||||
}
|
||||
|
||||
void AttributeValue::readState (const ESM::StatState<int>& state)
|
||||
void AttributeValue::readState (const ESM::StatState<float>& state)
|
||||
{
|
||||
mBase = state.mBase;
|
||||
mModifier = state.mMod;
|
||||
|
@ -296,13 +303,13 @@ namespace MWMechanics
|
|||
mProgress = progress;
|
||||
}
|
||||
|
||||
void SkillValue::writeState (ESM::StatState<int>& state) const
|
||||
void SkillValue::writeState (ESM::StatState<float>& state) const
|
||||
{
|
||||
AttributeValue::writeState (state);
|
||||
state.mProgress = mProgress;
|
||||
}
|
||||
|
||||
void SkillValue::readState (const ESM::StatState<int>& state)
|
||||
void SkillValue::readState (const ESM::StatState<float>& state)
|
||||
{
|
||||
AttributeValue::readState (state);
|
||||
mProgress = state.mProgress;
|
||||
|
|
|
@ -122,20 +122,20 @@ namespace MWMechanics
|
|||
|
||||
class AttributeValue
|
||||
{
|
||||
int mBase;
|
||||
int mModifier;
|
||||
float mBase;
|
||||
float mModifier;
|
||||
float mDamage; // needs to be float to allow continuous damage
|
||||
|
||||
public:
|
||||
AttributeValue();
|
||||
|
||||
int getModified() const;
|
||||
int getBase() const;
|
||||
int getModifier() const;
|
||||
float getModified() const;
|
||||
float getBase() 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.
|
||||
// Note: I think MW applies damage directly to mModified, since you can also
|
||||
|
@ -145,8 +145,8 @@ namespace MWMechanics
|
|||
|
||||
float getDamage() const;
|
||||
|
||||
void writeState (ESM::StatState<int>& state) const;
|
||||
void readState (const ESM::StatState<int>& state);
|
||||
void writeState (ESM::StatState<float>& state) const;
|
||||
void readState (const ESM::StatState<float>& state);
|
||||
};
|
||||
|
||||
class SkillValue : public AttributeValue
|
||||
|
@ -157,8 +157,8 @@ namespace MWMechanics
|
|||
float getProgress() const;
|
||||
void setProgress(float progress);
|
||||
|
||||
void writeState (ESM::StatState<int>& state) const;
|
||||
void readState (const ESM::StatState<int>& state);
|
||||
void writeState (ESM::StatState<float>& state) const;
|
||||
void readState (const ESM::StatState<float>& state);
|
||||
};
|
||||
|
||||
inline bool operator== (const AttributeValue& left, const AttributeValue& right)
|
||||
|
|
28
apps/openmw/mwmechanics/typedaipackage.hpp
Normal file
28
apps/openmw/mwmechanics/typedaipackage.hpp
Normal 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
|
|
@ -20,6 +20,7 @@ namespace mwmp
|
|||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
|
||||
/*
|
||||
if (worldstate.time.hour != -1)
|
||||
world->setHour(worldstate.time.hour);
|
||||
|
||||
|
@ -37,6 +38,7 @@ namespace mwmp
|
|||
|
||||
if (worldstate.time.timeScale != -1)
|
||||
world->setTimeScale(worldstate.time.timeScale);
|
||||
*/
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
104
apps/openmw/mwrender/fogmanager.cpp
Normal file
104
apps/openmw/mwrender/fogmanager.cpp
Normal 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;
|
||||
}
|
||||
}
|
39
apps/openmw/mwrender/fogmanager.hpp
Normal file
39
apps/openmw/mwrender/fogmanager.hpp
Normal 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
|
|
@ -69,16 +69,7 @@
|
|||
#include "navmesh.hpp"
|
||||
#include "actorspaths.hpp"
|
||||
#include "recastmesh.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
float DLLandFogStart;
|
||||
float DLLandFogEnd;
|
||||
float DLUnderwaterFogStart;
|
||||
float DLUnderwaterFogEnd;
|
||||
float DLInteriorFogStart;
|
||||
float DLInteriorFogEnd;
|
||||
}
|
||||
#include "fogmanager.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
@ -204,19 +195,9 @@ namespace MWRender
|
|||
, mWorkQueue(workQueue)
|
||||
, mUnrefQueue(new SceneUtil::UnrefQueue)
|
||||
, 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)
|
||||
, mDistantFog(false)
|
||||
, mDistantTerrain(false)
|
||||
, mFieldOfViewOverridden(false)
|
||||
, mFieldOfViewOverride(0.f)
|
||||
, mBorders(false)
|
||||
{
|
||||
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
|
||||
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
|
||||
|
@ -284,16 +265,6 @@ namespace MWRender
|
|||
|
||||
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 heightMapPattern = Settings::Manager::getString("normal height 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);
|
||||
|
||||
if (mDistantTerrain)
|
||||
if (Settings::Manager::getBool("distant terrain", "Terrain"))
|
||||
{
|
||||
const int compMapResolution = Settings::Manager::getInt("composite map resolution", "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));
|
||||
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->setRainIntensityUniform(mWater->getRainIntensityUniform());
|
||||
|
||||
|
@ -558,9 +530,9 @@ namespace MWRender
|
|||
|
||||
bool RenderingManager::toggleBorders()
|
||||
{
|
||||
mBorders = !mBorders;
|
||||
mTerrain->setBordersVisible(mBorders);
|
||||
return mBorders;
|
||||
bool borders = !mTerrain->getBordersVisible();
|
||||
mTerrain->setBordersVisible(borders);
|
||||
return borders;
|
||||
}
|
||||
|
||||
bool RenderingManager::toggleRenderMode(RenderMode mode)
|
||||
|
@ -606,46 +578,12 @@ namespace MWRender
|
|||
|
||||
void RenderingManager::configureFog(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
|
||||
configureFog(cell->mAmbi.mFogDensity, mUnderwaterIndoorFog, 1.0f, 0.0f, color);
|
||||
mFog->configure(mViewDistance, cell);
|
||||
}
|
||||
|
||||
void RenderingManager::configureFog(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 = mViewDistance * (1 - fogDepth);
|
||||
mLandFogEnd = mViewDistance;
|
||||
}
|
||||
mUnderwaterFogStart = std::min(mViewDistance, 6666.f) * (1 - underwaterFog);
|
||||
mUnderwaterFogEnd = std::min(mViewDistance, 6666.f);
|
||||
}
|
||||
mFogColor = color;
|
||||
mFog->configure(mViewDistance, fogDepth, underwaterFog, dlFactor, dlOffset, color);
|
||||
}
|
||||
|
||||
SkyManager* RenderingManager::getSkyManager()
|
||||
|
@ -674,19 +612,11 @@ namespace MWRender
|
|||
osg::Vec3f focal, cameraPos;
|
||||
mCamera->getPosition(focal, 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);
|
||||
mStateUpdater->setFogEnd(mLandFogEnd);
|
||||
}
|
||||
bool isUnderwater = mWater->isUnderwater(cameraPos);
|
||||
mStateUpdater->setFogStart(mFog->getFogStart(isUnderwater));
|
||||
mStateUpdater->setFogEnd(mFog->getFogEnd(isUnderwater));
|
||||
setFogColor(mFog->getFogColor(isUnderwater));
|
||||
}
|
||||
|
||||
void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr)
|
||||
|
@ -1335,7 +1265,7 @@ namespace MWRender
|
|||
else if (it->first == "Camera" && it->second == "viewing distance")
|
||||
{
|
||||
mViewDistance = Settings::Manager::getFloat("viewing distance", "Camera");
|
||||
if(!mDistantFog)
|
||||
if(!Settings::Manager::getBool("use distant fog", "Fog"))
|
||||
mStateUpdater->setFogEnd(mViewDistance);
|
||||
updateProjectionMatrix();
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ namespace MWRender
|
|||
class StateUpdater;
|
||||
|
||||
class EffectManager;
|
||||
class FogManager;
|
||||
class SkyManager;
|
||||
class NpcAnimation;
|
||||
class Pathgrid;
|
||||
|
@ -275,6 +276,7 @@ namespace MWRender
|
|||
std::unique_ptr<Terrain::World> mTerrain;
|
||||
TerrainStorage* mTerrainStorage;
|
||||
std::unique_ptr<SkyManager> mSky;
|
||||
std::unique_ptr<FogManager> mFog;
|
||||
std::unique_ptr<EffectManager> mEffectManager;
|
||||
std::unique_ptr<SceneUtil::ShadowManager> mShadowManager;
|
||||
osg::ref_ptr<NpcAnimation> mPlayerAnimation;
|
||||
|
@ -284,27 +286,15 @@ namespace MWRender
|
|||
|
||||
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;
|
||||
float mNightEyeFactor;
|
||||
|
||||
float mNearClip;
|
||||
float mViewDistance;
|
||||
bool mDistantFog : 1;
|
||||
bool mDistantTerrain : 1;
|
||||
bool mFieldOfViewOverridden : 1;
|
||||
bool mFieldOfViewOverridden;
|
||||
float mFieldOfViewOverride;
|
||||
float mFieldOfView;
|
||||
float mFirstPersonFieldOfView;
|
||||
bool mBorders;
|
||||
|
||||
void operator = (const RenderingManager&);
|
||||
RenderingManager(const RenderingManager&);
|
||||
|
|
|
@ -271,8 +271,10 @@ namespace MWScript
|
|||
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||
runtime.pop();
|
||||
|
||||
ptr.getClass().getCreatureStats (ptr).setAiSetting (mIndex,
|
||||
ptr.getClass().getCreatureStats (ptr).getAiSetting (mIndex).getBase() + value);
|
||||
int modified = 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>
|
||||
|
@ -302,7 +304,7 @@ namespace MWScript
|
|||
*/
|
||||
|
||||
stat.setModified(value, 0);
|
||||
ptr.getClass().getCreatureStats(ptr).setAiSetting(mIndex, stat);
|
||||
ptr.getClass().setBaseAISetting(ptr.getCellRef().getRefId(), mIndex, value);
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
|
|
@ -106,7 +106,7 @@ namespace MWScript
|
|||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
Interpreter::Type_Integer value =
|
||||
Interpreter::Type_Float value =
|
||||
ptr.getClass()
|
||||
.getCreatureStats (ptr)
|
||||
.getAttribute(mIndex)
|
||||
|
@ -129,7 +129,7 @@ namespace MWScript
|
|||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||
Interpreter::Type_Float value = runtime[0].mFloat;
|
||||
runtime.pop();
|
||||
|
||||
MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex);
|
||||
|
@ -151,7 +151,7 @@ namespace MWScript
|
|||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||
Interpreter::Type_Float value = runtime[0].mFloat;
|
||||
runtime.pop();
|
||||
|
||||
MWMechanics::AttributeValue attribute = ptr.getClass()
|
||||
|
@ -166,9 +166,9 @@ namespace MWScript
|
|||
return;
|
||||
|
||||
if (value < 0)
|
||||
attribute.setBase(std::max(0, attribute.getBase() + value));
|
||||
attribute.setBase(std::max(0.f, attribute.getBase() + value));
|
||||
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);
|
||||
}
|
||||
|
@ -356,7 +356,7 @@ namespace MWScript
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
@ -375,7 +375,7 @@ namespace MWScript
|
|||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||
Interpreter::Type_Float value = runtime[0].mFloat;
|
||||
runtime.pop();
|
||||
|
||||
MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr);
|
||||
|
@ -397,7 +397,7 @@ namespace MWScript
|
|||
{
|
||||
MWWorld::Ptr ptr = R()(runtime);
|
||||
|
||||
Interpreter::Type_Integer value = runtime[0].mInteger;
|
||||
Interpreter::Type_Float value = runtime[0].mFloat;
|
||||
runtime.pop();
|
||||
|
||||
MWMechanics::SkillValue &skill = ptr.getClass()
|
||||
|
@ -407,14 +407,14 @@ namespace MWScript
|
|||
if (value == 0)
|
||||
return;
|
||||
|
||||
if (((skill.getBase() <= 0) && (value < 0))
|
||||
|| ((skill.getBase() >= 100) && (value > 0)))
|
||||
if (((skill.getBase() <= 0.f) && (value < 0.f))
|
||||
|| ((skill.getBase() >= 100.f) && (value > 0.f)))
|
||||
return;
|
||||
|
||||
if (value < 0)
|
||||
skill.setBase(std::max(0, skill.getBase() + value));
|
||||
skill.setBase(std::max(0.f, skill.getBase() + value));
|
||||
else
|
||||
skill.setBase(std::min(100, skill.getBase() + value));
|
||||
skill.setBase(std::min(100.f, skill.getBase() + value));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -519,41 +519,48 @@ namespace MWScript
|
|||
std::string id = runtime.getStringLiteral (runtime[0].mInteger);
|
||||
runtime.pop();
|
||||
|
||||
MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);
|
||||
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
Only remove the spell if the target has it
|
||||
*/
|
||||
MWMechanics::Spells& spells = creatureStats.getSpells();
|
||||
|
||||
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.
|
||||
for (const auto& effect : creatureStats.getSpells().getMagicEffects())
|
||||
{
|
||||
if (effect.second.getMagnitude() <= 0)
|
||||
continue;
|
||||
MWMechanics::CastSpell cast(ptr, ptr);
|
||||
if (cast.applyInstantEffect(ptr, ptr, effect.first, effect.second.getMagnitude()))
|
||||
creatureStats.getSpells().purgeEffect(effect.first.mId);
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getMechanicsManager()->restoreStatsAfterCorprus(ptr, id);
|
||||
creatureStats.getSpells().remove (id);
|
||||
|
||||
MWBase::WindowManager* wm = MWBase::Environment::get().getWindowManager();
|
||||
|
||||
if (ptr == MWMechanics::getPlayer() &&
|
||||
id == wm->getSelectedSpell())
|
||||
{
|
||||
wm->unsetSelectedSpell();
|
||||
}
|
||||
|
||||
/*
|
||||
Start of tes3mp change (major)
|
||||
|
||||
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();
|
||||
|
||||
if (spells.hasSpell(id))
|
||||
{
|
||||
// The spell may have an instant effect which must be handled before the spell's removal.
|
||||
for (const auto& effect : creatureStats.getSpells().getMagicEffects())
|
||||
{
|
||||
if (effect.second.getMagnitude() <= 0)
|
||||
continue;
|
||||
MWMechanics::CastSpell cast(ptr, ptr);
|
||||
if (cast.applyInstantEffect(ptr, ptr, effect.first, effect.second.getMagnitude()))
|
||||
creatureStats.getSpells().purgeEffect(effect.first.mId);
|
||||
}
|
||||
|
||||
creatureStats.getSpells().remove (id);
|
||||
|
||||
if (ptr == MWMechanics::getPlayer())
|
||||
{
|
||||
MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();
|
||||
|
||||
if (id == wm->getSelectedSpell())
|
||||
wm->unsetSelectedSpell();
|
||||
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
mwmp::Main::get().getLocalPlayer()->sendSpellChange(id, mwmp::SpellbookChanges::REMOVE);
|
||||
}
|
||||
}
|
||||
if (mwmp::Main::get().getLocalPlayer()->isLoggedIn())
|
||||
mwmp::Main::get().getLocalPlayer()->sendSpellChange(id, mwmp::SpellbookChanges::REMOVE);
|
||||
/*
|
||||
End of tes3mp change (major)
|
||||
*/
|
||||
|
|
|
@ -147,10 +147,11 @@ namespace MWSound
|
|||
volume = static_cast<float>(pow(10.0, (sound->mData.mVolume / 255.0*3348.0 - 3348.0) / 2000.0));
|
||||
min = sound->mData.mMinRange;
|
||||
max = sound->mData.mMaxRange;
|
||||
if (min == 0)
|
||||
if (min == 0 && max == 0)
|
||||
{
|
||||
min = fAudioDefaultMinDistance;
|
||||
if (max == 0)
|
||||
max = fAudioDefaultMaxDistance;
|
||||
}
|
||||
|
||||
min *= fAudioMinDistanceMult;
|
||||
max *= fAudioMaxDistanceMult;
|
||||
|
|
|
@ -213,11 +213,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
|
|||
profile.mPlayerClassId = classId;
|
||||
|
||||
profile.mPlayerCell = world.getCellName();
|
||||
|
||||
profile.mInGameTime.mGameHour = world.getTimeStamp().getHour();
|
||||
profile.mInGameTime.mDay = world.getDay();
|
||||
profile.mInGameTime.mMonth = world.getMonth();
|
||||
profile.mInGameTime.mYear = world.getYear();
|
||||
profile.mInGameTime = world.getEpochTimeStamp();
|
||||
profile.mTimePlayed = mTimePlayed;
|
||||
profile.mDescription = description;
|
||||
|
||||
|
@ -464,6 +460,7 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
|
|||
case ESM::REC_ENAB:
|
||||
case ESM::REC_LEVC:
|
||||
case ESM::REC_LEVI:
|
||||
case ESM::REC_CREA:
|
||||
MWBase::Environment::get().getWorld()->readRecord(reader, n.intval, contentFileMap);
|
||||
break;
|
||||
|
||||
|
|
|
@ -434,7 +434,7 @@ namespace MWWorld
|
|||
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");
|
||||
}
|
||||
|
@ -529,4 +529,9 @@ namespace MWWorld
|
|||
result.z() = magicEffect->mData.mBlue / 255.f;
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "ptr.hpp"
|
||||
#include "doorstate.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
|
@ -28,7 +29,6 @@ namespace MWPhysics
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
class CreatureStats;
|
||||
class NpcStats;
|
||||
struct Movement;
|
||||
}
|
||||
|
@ -332,7 +332,7 @@ namespace MWWorld
|
|||
bool isPureLandCreature(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)
|
||||
const;
|
||||
|
@ -371,6 +371,8 @@ namespace MWWorld
|
|||
virtual float getEffectiveArmorRating(const MWWorld::ConstPtr& armor, const MWWorld::Ptr& actor) const;
|
||||
|
||||
virtual osg::Vec4f getEnchantmentColor(const MWWorld::ConstPtr& item) const;
|
||||
|
||||
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
266
apps/openmw/mwworld/datetimemanager.cpp
Normal file
266
apps/openmw/mwworld/datetimemanager.cpp
Normal 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;
|
||||
}
|
||||
}
|
73
apps/openmw/mwworld/datetimemanager.hpp
Normal file
73
apps/openmw/mwworld/datetimemanager.hpp
Normal 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
|
|
@ -273,7 +273,8 @@ void ESMStore::validate()
|
|||
+mSpells.getDynamicSize()
|
||||
+mWeapons.getDynamicSize()
|
||||
+mCreatureLists.getDynamicSize()
|
||||
+mItemLists.getDynamicSize();
|
||||
+mItemLists.getDynamicSize()
|
||||
+mCreatures.getDynamicSize();
|
||||
}
|
||||
|
||||
void ESMStore::write (ESM::ESMWriter& writer, Loading::Listener& progress) const
|
||||
|
@ -295,6 +296,7 @@ void ESMStore::validate()
|
|||
mNpcs.write (writer, progress);
|
||||
mItemLists.write (writer, progress);
|
||||
mCreatureLists.write (writer, progress);
|
||||
mCreatures.write (writer, progress);
|
||||
}
|
||||
|
||||
bool ESMStore::readRecord (ESM::ESMReader& reader, uint32_t type)
|
||||
|
@ -312,24 +314,8 @@ void ESMStore::validate()
|
|||
case ESM::REC_NPC_:
|
||||
case ESM::REC_LEVI:
|
||||
case ESM::REC_LEVC:
|
||||
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
case ESM::REC_CREA:
|
||||
mStores[type]->read (reader);
|
||||
return true;
|
||||
|
||||
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
|
||||
|
|
|
@ -239,6 +239,9 @@ namespace MWWorld
|
|||
|
||||
bool readRecord (ESM::ESMReader& reader, uint32_t type);
|
||||
///< \return Known type?
|
||||
|
||||
// To be called when we are done with dynamic record loading
|
||||
void checkPlayer();
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/misc/stringops.hpp>
|
||||
|
||||
#include "esmstore.hpp"
|
||||
|
||||
|
|
|
@ -317,12 +317,12 @@ void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots
|
|||
// rate weapon
|
||||
for (int i = 0; i < static_cast<int>(weaponSkillsLength); ++i)
|
||||
{
|
||||
int max = 0;
|
||||
float max = 0;
|
||||
int maxWeaponSkill = -1;
|
||||
|
||||
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])
|
||||
{
|
||||
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 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);
|
||||
|
||||
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();
|
||||
|
||||
if (actor.getClass().getCreatureStats(actor).isDead())
|
||||
const auto& stats = actor.getClass().getCreatureStats(actor);
|
||||
if (stats.isDead() && stats.isDeathAnimationFinished())
|
||||
return;
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
if (effectMagnitudeIt == mPermanentMagicEffectMagnitudes.end())
|
||||
|
@ -1024,6 +1025,12 @@ void MWWorld::InventoryStore::purgeEffect(short effectId, const std::string &sou
|
|||
if (effectIt->mEffectID != effectId)
|
||||
continue;
|
||||
|
||||
if (wholeSpell)
|
||||
{
|
||||
mPermanentMagicEffectMagnitudes.erase(sourceId);
|
||||
return;
|
||||
}
|
||||
|
||||
float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params[i].mRandom;
|
||||
magnitude *= params[i].mMultiplier;
|
||||
|
||||
|
|
|
@ -203,10 +203,10 @@ namespace MWWorld
|
|||
|
||||
void visitEffectSources (MWMechanics::EffectSourceVisitor& visitor);
|
||||
|
||||
void purgeEffect (short effectId);
|
||||
void purgeEffect (short effectId, bool wholeSpell = false);
|
||||
///< 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
|
||||
|
||||
virtual void clear();
|
||||
|
|
|
@ -391,8 +391,6 @@ namespace MWWorld
|
|||
else
|
||||
player.mHasMark = false;
|
||||
|
||||
player.mAutoMove = mAutoMove ? 1 : 0;
|
||||
|
||||
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||
mSaveAttributes[i].writeState(player.mSaveAttributes[i]);
|
||||
for (int i=0; i<ESM::Skill::Length; ++i)
|
||||
|
@ -487,8 +485,6 @@ namespace MWWorld
|
|||
mMarkedCell = 0;
|
||||
}
|
||||
|
||||
mAutoMove = player.mAutoMove!=0;
|
||||
|
||||
mForwardBackward = 0;
|
||||
mTeleported = false;
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue