mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-07-21 03:44:05 +00:00
Add OpenMW commits up to 4 Feb 2020
# Conflicts: # .travis.yml # CI/before_script.linux.sh
This commit is contained in:
commit
f0f76516d8
39 changed files with 328 additions and 277 deletions
11
.travis.yml
11
.travis.yml
|
@ -10,10 +10,7 @@ env:
|
||||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||||
# via the "travis encrypt" command using the project repo's public key
|
# via the "travis encrypt" command using the project repo's public key
|
||||||
- secure: "1QK0yVyoOB+gf2I7XzvhXu9w/5lq4stBXIwJbVCTjz4Q4XVHCosURaW1MAgKzMrPnbFEwjyn5uQ8BwsvvfkuN1AZD0YXITgc7gyI+J1wQ/p/ljxRxglakU6WEgsTs2J5z9UmGac4YTXg+quK7YP3rv+zuGim2I2rhzImejyzp0Ym3kRCnNcy+SGBsiRaevRJMe00Ch8zGAbEhduQGeSoS6W0rcu02DNlQKiq5NktWsXR+TWWWVfIeIlQR/lbPsCd0pdxMaMv2QCY0rVbwrYxWJwr/Qe45dAdWp+8/C3PbXpeMSGxlLa33nJNX4Lf/djxbjm8KWk6edaXPajrjR/0iwcpwq0jg2Jt6XfEdnJt35F1gpXlc04sxStjG45uloOKCFYT0wdhIO1Lq+hDP54wypQl+JInd5qC001O7pwhVxO36EgKWqo8HD+BqGDBwsNj2engy9Qcp3wO6G0rLBPB3CrZsk9wrHVv5cSiQSLMhId3Xviu3ZI2qEDA+kgTvxrKrsnMj4bILVCyG5Ka2Mj22wIDW9e8oIab9oTdujax3DTN1GkD6QuOAGzwDsNwGASsgfoeZ+FUhgM75RlBWGMilgkmnF7EJ0oAXLEpjtABnEr2d4qHv+y08kOuTDBLB9ExzCIj024dYYYNLZrqPKx0ncHuCMG2QNj2aJAJEZtj1rQ="
|
- secure: "1QK0yVyoOB+gf2I7XzvhXu9w/5lq4stBXIwJbVCTjz4Q4XVHCosURaW1MAgKzMrPnbFEwjyn5uQ8BwsvvfkuN1AZD0YXITgc7gyI+J1wQ/p/ljxRxglakU6WEgsTs2J5z9UmGac4YTXg+quK7YP3rv+zuGim2I2rhzImejyzp0Ym3kRCnNcy+SGBsiRaevRJMe00Ch8zGAbEhduQGeSoS6W0rcu02DNlQKiq5NktWsXR+TWWWVfIeIlQR/lbPsCd0pdxMaMv2QCY0rVbwrYxWJwr/Qe45dAdWp+8/C3PbXpeMSGxlLa33nJNX4Lf/djxbjm8KWk6edaXPajrjR/0iwcpwq0jg2Jt6XfEdnJt35F1gpXlc04sxStjG45uloOKCFYT0wdhIO1Lq+hDP54wypQl+JInd5qC001O7pwhVxO36EgKWqo8HD+BqGDBwsNj2engy9Qcp3wO6G0rLBPB3CrZsk9wrHVv5cSiQSLMhId3Xviu3ZI2qEDA+kgTvxrKrsnMj4bILVCyG5Ka2Mj22wIDW9e8oIab9oTdujax3DTN1GkD6QuOAGzwDsNwGASsgfoeZ+FUhgM75RlBWGMilgkmnF7EJ0oAXLEpjtABnEr2d4qHv+y08kOuTDBLB9ExzCIj024dYYYNLZrqPKx0ncHuCMG2QNj2aJAJEZtj1rQ="
|
||||||
cache:
|
cache: ccache
|
||||||
ccache: true
|
|
||||||
directories:
|
|
||||||
- ${HOME}/.ccache
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
sources:
|
sources:
|
||||||
|
@ -71,7 +68,9 @@ matrix:
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- ./CI/before_install.${TRAVIS_OS_NAME}.sh
|
- ./CI/before_install.${TRAVIS_OS_NAME}.sh
|
||||||
before_script: ./CI/before_script.${TRAVIS_OS_NAME}.sh
|
before_script:
|
||||||
|
- ccache -z
|
||||||
|
- ./CI/before_script.${TRAVIS_OS_NAME}.sh
|
||||||
script:
|
script:
|
||||||
- cd ./build
|
- cd ./build
|
||||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j3; fi
|
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then ${ANALYZE}make -j3; fi
|
||||||
|
@ -79,6 +78,8 @@ script:
|
||||||
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi
|
- if [ "${COVERITY_SCAN_BRANCH}" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then ../CI/check_package.osx.sh; fi
|
||||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
|
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
|
||||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "linux" ]; then cd .. && ./CI/check_tabs.sh; fi
|
||||||
|
- cd "${TRAVIS_BUILD_DIR}"
|
||||||
|
- ccache -s
|
||||||
#deploy:
|
#deploy:
|
||||||
# provider: script
|
# provider: script
|
||||||
# script: ./CI/deploy.osx.sh
|
# script: ./CI/deploy.osx.sh
|
||||||
|
|
|
@ -194,6 +194,8 @@
|
||||||
Bug #5242: ExplodeSpell behavior differs from Cast behavior
|
Bug #5242: ExplodeSpell behavior differs from Cast behavior
|
||||||
Bug #5249: Wandering NPCs start walking too soon after they hello
|
Bug #5249: Wandering NPCs start walking too soon after they hello
|
||||||
Bug #5250: Creatures display shield ground mesh instead of shield body part
|
Bug #5250: Creatures display shield ground mesh instead of shield body part
|
||||||
|
Bug #5255: "GetTarget, player" doesn't return 1 during NPC hello
|
||||||
|
Bug #5261: Creatures can sometimes become stuck playing idles and never wander again
|
||||||
Feature #1774: Handle AvoidNode
|
Feature #1774: Handle AvoidNode
|
||||||
Feature #2229: Improve pathfinding AI
|
Feature #2229: Improve pathfinding AI
|
||||||
Feature #3025: Analogue gamepad movement controls
|
Feature #3025: Analogue gamepad movement controls
|
||||||
|
|
3
CI/before_script.linux.sh
Executable file → Normal file
3
CI/before_script.linux.sh
Executable file → Normal file
|
@ -15,9 +15,7 @@ fi
|
||||||
|
|
||||||
export RAKNET_ROOT=~/CrabNet
|
export RAKNET_ROOT=~/CrabNet
|
||||||
|
|
||||||
export CODE_COVERAGE=1
|
|
||||||
|
|
||||||
if [[ "${CC}" =~ "clang" ]]; then export CODE_COVERAGE=0; fi
|
|
||||||
if [[ -z "${BUILD_OPENMW}" ]]; then export BUILD_OPENMW=ON; fi
|
if [[ -z "${BUILD_OPENMW}" ]]; then export BUILD_OPENMW=ON; fi
|
||||||
if [[ -z "${BUILD_OPENMW_CS}" ]]; then export BUILD_OPENMW_CS=ON; fi
|
if [[ -z "${BUILD_OPENMW_CS}" ]]; then export BUILD_OPENMW_CS=ON; fi
|
||||||
|
|
||||||
|
@ -39,7 +37,6 @@ ${ANALYZE} cmake .. \
|
||||||
-DBUILD_OPENMW_MP=ON \
|
-DBUILD_OPENMW_MP=ON \
|
||||||
-DBUILD_BROWSER=ON \
|
-DBUILD_BROWSER=ON \
|
||||||
-DBUILD_MASTER=ON \
|
-DBUILD_MASTER=ON \
|
||||||
-DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} \
|
|
||||||
-DBUILD_UNITTESTS=1 \
|
-DBUILD_UNITTESTS=1 \
|
||||||
-DUSE_SYSTEM_TINYXML=1 \
|
-DUSE_SYSTEM_TINYXML=1 \
|
||||||
-DDESIRED_QT_VERSION=5 \
|
-DDESIRED_QT_VERSION=5 \
|
||||||
|
|
|
@ -76,53 +76,6 @@ namespace CSMWorld
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* LandMapLodColumn */
|
|
||||||
LandMapLodColumn::LandMapLodColumn()
|
|
||||||
: Column<Land>(Columns::ColumnId_LandMapLodIndex, ColumnBase::Display_String, 0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant LandMapLodColumn::get(const Record<Land>& record) const
|
|
||||||
{
|
|
||||||
const int Size = Land::LAND_GLOBAL_MAP_LOD_SIZE;
|
|
||||||
const Land& land = record.get();
|
|
||||||
|
|
||||||
DataType values(Size, 0);
|
|
||||||
|
|
||||||
if (land.mDataTypes & Land::DATA_WNAM)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < Size; ++i)
|
|
||||||
values[i] = land.mWnam[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant variant;
|
|
||||||
variant.setValue(values);
|
|
||||||
return variant;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LandMapLodColumn::set(Record<Land>& record, const QVariant& data)
|
|
||||||
{
|
|
||||||
DataType values = data.value<DataType>();
|
|
||||||
|
|
||||||
if (values.size() != Land::LAND_GLOBAL_MAP_LOD_SIZE)
|
|
||||||
throw std::runtime_error("invalid land map LOD data");
|
|
||||||
|
|
||||||
Land copy = record.get();
|
|
||||||
copy.add(Land::DATA_WNAM);
|
|
||||||
|
|
||||||
for (int i = 0; i < values.size(); ++i)
|
|
||||||
{
|
|
||||||
copy.mWnam[i] = values[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
record.setModified(copy);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LandMapLodColumn::isEditable() const
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* LandNormalsColumn */
|
/* LandNormalsColumn */
|
||||||
LandNormalsColumn::LandNormalsColumn()
|
LandNormalsColumn::LandNormalsColumn()
|
||||||
: Column<Land>(Columns::ColumnId_LandNormalsIndex, ColumnBase::Display_String, 0)
|
: Column<Land>(Columns::ColumnId_LandNormalsIndex, ColumnBase::Display_String, 0)
|
||||||
|
|
|
@ -2461,17 +2461,6 @@ namespace CSMWorld
|
||||||
bool isEditable() const override;
|
bool isEditable() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LandMapLodColumn : public Column<Land>
|
|
||||||
{
|
|
||||||
using DataType = QVector<signed char>;
|
|
||||||
|
|
||||||
LandMapLodColumn();
|
|
||||||
|
|
||||||
QVariant get(const Record<Land>& record) const override;
|
|
||||||
void set(Record<Land>& record, const QVariant& data) override;
|
|
||||||
bool isEditable() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LandNormalsColumn : public Column<Land>
|
struct LandNormalsColumn : public Column<Land>
|
||||||
{
|
{
|
||||||
using DataType = QVector<signed char>;
|
using DataType = QVector<signed char>;
|
||||||
|
@ -2529,8 +2518,7 @@ namespace CSMWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is required to access the type as a QVariant.
|
// This is required to access the type as a QVariant.
|
||||||
Q_DECLARE_METATYPE(CSMWorld::LandMapLodColumn::DataType)
|
Q_DECLARE_METATYPE(CSMWorld::LandNormalsColumn::DataType)
|
||||||
//Q_DECLARE_METATYPE(CSMWorld::LandNormalsColumn::DataType) // Same as LandMapLodColumn::DataType
|
|
||||||
Q_DECLARE_METATYPE(CSMWorld::LandHeightsColumn::DataType)
|
Q_DECLARE_METATYPE(CSMWorld::LandHeightsColumn::DataType)
|
||||||
Q_DECLARE_METATYPE(CSMWorld::LandColoursColumn::DataType)
|
Q_DECLARE_METATYPE(CSMWorld::LandColoursColumn::DataType)
|
||||||
Q_DECLARE_METATYPE(CSMWorld::LandTexturesColumn::DataType)
|
Q_DECLARE_METATYPE(CSMWorld::LandTexturesColumn::DataType)
|
||||||
|
|
|
@ -443,7 +443,6 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, bool fsStrict, const Files::Pat
|
||||||
mLand.addColumn (new RecordStateColumn<Land>);
|
mLand.addColumn (new RecordStateColumn<Land>);
|
||||||
mLand.addColumn (new FixedRecordTypeColumn<Land>(UniversalId::Type_Land));
|
mLand.addColumn (new FixedRecordTypeColumn<Land>(UniversalId::Type_Land));
|
||||||
mLand.addColumn (new LandPluginIndexColumn);
|
mLand.addColumn (new LandPluginIndexColumn);
|
||||||
mLand.addColumn (new LandMapLodColumn);
|
|
||||||
mLand.addColumn (new LandNormalsColumn);
|
mLand.addColumn (new LandNormalsColumn);
|
||||||
mLand.addColumn (new LandHeightsColumn);
|
mLand.addColumn (new LandHeightsColumn);
|
||||||
mLand.addColumn (new LandColoursColumn);
|
mLand.addColumn (new LandColoursColumn);
|
||||||
|
|
|
@ -705,7 +705,7 @@ void CSVRender::Object::apply (CSMWorld::CommandMacro& commands)
|
||||||
CSMWorld::Columns::ColumnId_PositionXRot+i));
|
CSMWorld::Columns::ColumnId_PositionXRot+i));
|
||||||
|
|
||||||
commands.push (new CSMWorld::ModifyCommand (*model,
|
commands.push (new CSMWorld::ModifyCommand (*model,
|
||||||
model->index (recordIndex, column), mPositionOverride.rot[i]));
|
model->index (recordIndex, column), osg::RadiansToDegrees(mPositionOverride.rot[i])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -273,7 +273,6 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges()
|
||||||
*document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
|
*document.getData().getTableModel (CSMWorld::UniversalId::Type_LandTextures));
|
||||||
|
|
||||||
int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);
|
int landshapeColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandHeightsIndex);
|
||||||
int landMapLodColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandMapLodIndex);
|
|
||||||
int landnormalsColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandNormalsIndex);
|
int landnormalsColumn = landTable.findColumnIndex(CSMWorld::Columns::ColumnId_LandNormalsIndex);
|
||||||
|
|
||||||
QUndoStack& undoStack = document.getUndoStack();
|
QUndoStack& undoStack = document.getUndoStack();
|
||||||
|
@ -287,9 +286,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges()
|
||||||
std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY());
|
std::string cellId = CSMWorld::CellCoordinates::generateId(cellCoordinates.getX(), cellCoordinates.getY());
|
||||||
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
|
undoStack.push (new CSMWorld::TouchLandCommand(landTable, ltexTable, cellId));
|
||||||
const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();
|
const CSMWorld::LandHeightsColumn::DataType landShapePointer = landTable.data(landTable.getModelIndex(cellId, landshapeColumn)).value<CSMWorld::LandHeightsColumn::DataType>();
|
||||||
const CSMWorld::LandMapLodColumn::DataType landMapLodPointer = landTable.data(landTable.getModelIndex(cellId, landMapLodColumn)).value<CSMWorld::LandMapLodColumn::DataType>();
|
|
||||||
CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer);
|
CSMWorld::LandHeightsColumn::DataType landShapeNew(landShapePointer);
|
||||||
CSMWorld::LandMapLodColumn::DataType mapLodShapeNew(landMapLodPointer);
|
|
||||||
CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget());
|
CSVRender::PagedWorldspaceWidget *paged = dynamic_cast<CSVRender::PagedWorldspaceWidget *> (&getWorldspaceWidget());
|
||||||
|
|
||||||
// Generate land height record
|
// Generate land height record
|
||||||
|
@ -304,26 +301,7 @@ void CSVRender::TerrainShapeMode::applyTerrainEditChanges()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate WNAM record
|
|
||||||
int sqrtLandGlobalMapLodSize = sqrt(ESM::Land::LAND_GLOBAL_MAP_LOD_SIZE);
|
|
||||||
for(int i = 0; i < sqrtLandGlobalMapLodSize; ++i)
|
|
||||||
{
|
|
||||||
for(int j = 0; j < sqrtLandGlobalMapLodSize; ++j)
|
|
||||||
{
|
|
||||||
int col = (static_cast<float>(j) / sqrtLandGlobalMapLodSize) * (ESM::Land::LAND_SIZE - 1);
|
|
||||||
int row = (static_cast<float>(i) / sqrtLandGlobalMapLodSize) * (ESM::Land::LAND_SIZE - 1);
|
|
||||||
signed char lodHeight = 0;
|
|
||||||
float floatLodHeight = 0;
|
|
||||||
if (landShapeNew[col * ESM::Land::LAND_SIZE + row] > 0) floatLodHeight = landShapeNew[col * ESM::Land::LAND_SIZE + row] / 128;
|
|
||||||
if (landShapeNew[col * ESM::Land::LAND_SIZE + row] <= 0) floatLodHeight = landShapeNew[col * ESM::Land::LAND_SIZE + row] / 16;
|
|
||||||
if (floatLodHeight > std::numeric_limits<signed char>::max()) lodHeight = std::numeric_limits<signed char>::max();
|
|
||||||
else if (floatLodHeight < std::numeric_limits<signed char>::min()) lodHeight = std::numeric_limits<signed char>::min();
|
|
||||||
else lodHeight = static_cast<signed char>(floatLodHeight);
|
|
||||||
mapLodShapeNew[j * sqrtLandGlobalMapLodSize + i] = lodHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pushEditToCommand(landShapeNew, document, landTable, cellId);
|
pushEditToCommand(landShapeNew, document, landTable, cellId);
|
||||||
pushLodToCommand(mapLodShapeNew, document, landTable, cellId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells)
|
for(CSMWorld::CellCoordinates cellCoordinates: mAlteredCells)
|
||||||
|
@ -1136,18 +1114,6 @@ void CSVRender::TerrainShapeMode::pushNormalsEditToCommand(const CSMWorld::LandN
|
||||||
undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand));
|
undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLand));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSVRender::TerrainShapeMode::pushLodToCommand(const CSMWorld::LandMapLodColumn::DataType& newLandMapLod, CSMDoc::Document& document,
|
|
||||||
CSMWorld::IdTable& landTable, const std::string& cellId)
|
|
||||||
{
|
|
||||||
QVariant changedLod;
|
|
||||||
changedLod.setValue(newLandMapLod);
|
|
||||||
|
|
||||||
QModelIndex index(landTable.getModelIndex (cellId, landTable.findColumnIndex (CSMWorld::Columns::ColumnId_LandMapLodIndex)));
|
|
||||||
|
|
||||||
QUndoStack& undoStack = document.getUndoStack();
|
|
||||||
undoStack.push (new CSMWorld::ModifyCommand(landTable, index, changedLod));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CSVRender::TerrainShapeMode::noCell(const std::string& cellId)
|
bool CSVRender::TerrainShapeMode::noCell(const std::string& cellId)
|
||||||
{
|
{
|
||||||
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
|
CSMDoc::Document& document = getWorldspaceWidget().getDocument();
|
||||||
|
|
|
@ -148,10 +148,6 @@ namespace CSVRender
|
||||||
void pushNormalsEditToCommand(const CSMWorld::LandNormalsColumn::DataType& newLandGrid, CSMDoc::Document& document,
|
void pushNormalsEditToCommand(const CSMWorld::LandNormalsColumn::DataType& newLandGrid, CSMDoc::Document& document,
|
||||||
CSMWorld::IdTable& landTable, const std::string& cellId);
|
CSMWorld::IdTable& landTable, const std::string& cellId);
|
||||||
|
|
||||||
/// Generate new land map LOD
|
|
||||||
void pushLodToCommand(const CSMWorld::LandMapLodColumn::DataType& newLandMapLod, CSMDoc::Document& document,
|
|
||||||
CSMWorld::IdTable& landTable, const std::string& cellId);
|
|
||||||
|
|
||||||
bool noCell(const std::string& cellId);
|
bool noCell(const std::string& cellId);
|
||||||
|
|
||||||
bool noLand(const std::string& cellId);
|
bool noLand(const std::string& cellId);
|
||||||
|
|
|
@ -485,22 +485,12 @@ namespace MWMechanics
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
||||||
int hello = stats.getAiSetting(CreatureStats::AI_Hello).getModified();
|
|
||||||
if (hello == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (MWBase::Environment::get().getWorld()->isSwimming(actor))
|
|
||||||
return;
|
|
||||||
|
|
||||||
MWWorld::Ptr player = getPlayer();
|
|
||||||
osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
|
|
||||||
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
|
||||||
osg::Vec3f dir = playerPos - actorPos;
|
|
||||||
|
|
||||||
const MWMechanics::AiSequence& seq = stats.getAiSequence();
|
const MWMechanics::AiSequence& seq = stats.getAiSequence();
|
||||||
int packageId = seq.getTypeId();
|
int packageId = seq.getTypeId();
|
||||||
|
|
||||||
if (seq.isInCombat() || (packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1))
|
if (seq.isInCombat() ||
|
||||||
|
MWBase::Environment::get().getWorld()->isSwimming(actor) ||
|
||||||
|
(packageId != AiPackage::TypeIdWander && packageId != AiPackage::TypeIdTravel && packageId != -1))
|
||||||
{
|
{
|
||||||
stats.setTurningToPlayer(false);
|
stats.setTurningToPlayer(false);
|
||||||
stats.setGreetingTimer(0);
|
stats.setGreetingTimer(0);
|
||||||
|
@ -508,6 +498,11 @@ namespace MWMechanics
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MWWorld::Ptr player = getPlayer();
|
||||||
|
osg::Vec3f playerPos(player.getRefData().getPosition().asVec3());
|
||||||
|
osg::Vec3f actorPos(actor.getRefData().getPosition().asVec3());
|
||||||
|
osg::Vec3f dir = playerPos - actorPos;
|
||||||
|
|
||||||
if (stats.isTurningToPlayer())
|
if (stats.isTurningToPlayer())
|
||||||
{
|
{
|
||||||
// Reduce the turning animation glitch by using a *HUGE* value of
|
// Reduce the turning animation glitch by using a *HUGE* value of
|
||||||
|
@ -525,11 +520,10 @@ namespace MWMechanics
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Play a random voice greeting if the player gets too close
|
// Play a random voice greeting if the player gets too close
|
||||||
float helloDistance = static_cast<float>(hello);
|
|
||||||
static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
|
static int iGreetDistanceMultiplier = MWBase::Environment::get().getWorld()->getStore()
|
||||||
.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->mValue.getInteger();
|
.get<ESM::GameSetting>().find("iGreetDistanceMultiplier")->mValue.getInteger();
|
||||||
|
|
||||||
helloDistance *= iGreetDistanceMultiplier;
|
float helloDistance = static_cast<float>(stats.getAiSetting(CreatureStats::AI_Hello).getModified() * iGreetDistanceMultiplier);
|
||||||
|
|
||||||
int greetingTimer = stats.getGreetingTimer();
|
int greetingTimer = stats.getGreetingTimer();
|
||||||
GreetingState greetingState = stats.getGreetingState();
|
GreetingState greetingState = stats.getGreetingState();
|
||||||
|
|
|
@ -197,7 +197,8 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
||||||
zTurn(actor, mPathFinder.getZAngleToNext(position.x(), position.y()));
|
zTurn(actor, mPathFinder.getZAngleToNext(position.x(), position.y()));
|
||||||
smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0);
|
smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0);
|
||||||
|
|
||||||
mObstacleCheck.update(actor, duration);
|
const auto destination = mPathFinder.getPath().empty() ? dest : mPathFinder.getPath().front();
|
||||||
|
mObstacleCheck.update(actor, destination, duration);
|
||||||
|
|
||||||
// handle obstacles on the way
|
// handle obstacles on the way
|
||||||
evadeObstacles(actor);
|
evadeObstacles(actor);
|
||||||
|
|
|
@ -54,8 +54,9 @@ namespace MWMechanics
|
||||||
stats.setMovementFlag(CreatureStats::Flag_Run, false);
|
stats.setMovementFlag(CreatureStats::Flag_Run, false);
|
||||||
stats.setDrawState(DrawState_Nothing);
|
stats.setDrawState(DrawState_Nothing);
|
||||||
|
|
||||||
|
// Note: we should cancel internal "return after combat" package, if original location is too far away
|
||||||
if (!isWithinMaxRange(targetPos, actorPos))
|
if (!isWithinMaxRange(targetPos, actorPos))
|
||||||
return false;
|
return mHidden;
|
||||||
|
|
||||||
// Unfortunately, with vanilla assets destination is sometimes blocked by other actor.
|
// Unfortunately, with vanilla assets destination is sometimes blocked by other actor.
|
||||||
// If we got close to target, check for actors nearby. If they are, finish AI package.
|
// If we got close to target, check for actors nearby. If they are, finish AI package.
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <components/debug/debuglog.hpp>
|
#include <components/debug/debuglog.hpp>
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
#include <components/esm/aisequence.hpp>
|
#include <components/esm/aisequence.hpp>
|
||||||
|
#include <components/detournavigator/navigator.hpp>
|
||||||
|
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
|
@ -52,6 +53,14 @@ namespace MWMechanics
|
||||||
return 1;
|
return 1;
|
||||||
return COUNT_BEFORE_RESET;
|
return COUNT_BEFORE_RESET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
osg::Vec3f getRandomPointAround(const osg::Vec3f& position, const float distance)
|
||||||
|
{
|
||||||
|
const float randomDirection = Misc::Rng::rollClosedProbability() * 2.0f * osg::PI;
|
||||||
|
osg::Matrixf rotation;
|
||||||
|
rotation.makeRotate(randomDirection, osg::Vec3f(0.0, 0.0, 1.0));
|
||||||
|
return position + osg::Vec3f(distance, 0.0, 0.0) * rotation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
|
||||||
|
@ -136,15 +145,6 @@ namespace MWMechanics
|
||||||
|
|
||||||
// get or create temporary storage
|
// get or create temporary storage
|
||||||
AiWanderStorage& storage = state.get<AiWanderStorage>();
|
AiWanderStorage& storage = state.get<AiWanderStorage>();
|
||||||
const MWWorld::CellStore*& currentCell = storage.mCell;
|
|
||||||
bool cellChange = currentCell && (actor.getCell() != currentCell);
|
|
||||||
if(!currentCell || cellChange)
|
|
||||||
{
|
|
||||||
stopWalking(actor, storage);
|
|
||||||
currentCell = actor.getCell();
|
|
||||||
storage.mPopulateAvailableNodes = true;
|
|
||||||
mStoredInitialActorPosition = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mRemainingDuration -= ((duration*MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600);
|
mRemainingDuration -= ((duration*MWBase::Environment::get().getWorld()->getTimeScaleFactor()) / 3600);
|
||||||
|
|
||||||
|
@ -191,14 +191,13 @@ namespace MWMechanics
|
||||||
if (AI_REACTION_TIME <= lastReaction)
|
if (AI_REACTION_TIME <= lastReaction)
|
||||||
{
|
{
|
||||||
lastReaction = 0;
|
lastReaction = 0;
|
||||||
return reactionTimeActions(actor, storage, currentCell, cellChange, pos);
|
return reactionTimeActions(actor, storage, pos);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AiWander::reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage,
|
bool AiWander::reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos)
|
||||||
const MWWorld::CellStore*& currentCell, bool cellChange, ESM::Position& pos)
|
|
||||||
{
|
{
|
||||||
if (mDistance <= 0)
|
if (mDistance <= 0)
|
||||||
storage.mCanWanderAlongPathGrid = false;
|
storage.mCanWanderAlongPathGrid = false;
|
||||||
|
@ -220,7 +219,7 @@ namespace MWMechanics
|
||||||
// Initialization to discover & store allowed node points for this actor.
|
// Initialization to discover & store allowed node points for this actor.
|
||||||
if (storage.mPopulateAvailableNodes)
|
if (storage.mPopulateAvailableNodes)
|
||||||
{
|
{
|
||||||
getAllowedNodes(actor, currentCell->getCell(), storage);
|
getAllowedNodes(actor, actor.getCell()->getCell(), storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canActorMoveByZAxis(actor) && mDistance > 0) {
|
if (canActorMoveByZAxis(actor) && mDistance > 0) {
|
||||||
|
@ -249,10 +248,6 @@ namespace MWMechanics
|
||||||
completeManualWalking(actor, storage);
|
completeManualWalking(actor, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't try to move if you are in a new cell (ie: positioncell command called) but still play idles.
|
|
||||||
if(mDistance && cellChange)
|
|
||||||
mDistance = 0;
|
|
||||||
|
|
||||||
AiWanderStorage::WanderState& wanderState = storage.mState;
|
AiWanderStorage::WanderState& wanderState = storage.mState;
|
||||||
if ((wanderState == AiWanderStorage::Wander_MoveNow) && storage.mCanWanderAlongPathGrid)
|
if ((wanderState == AiWanderStorage::Wander_MoveNow) && storage.mCanWanderAlongPathGrid)
|
||||||
{
|
{
|
||||||
|
@ -310,14 +305,24 @@ namespace MWMechanics
|
||||||
std::size_t attempts = 10; // If a unit can't wander out of water, don't want to hang here
|
std::size_t attempts = 10; // If a unit can't wander out of water, don't want to hang here
|
||||||
const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);
|
const bool isWaterCreature = actor.getClass().isPureWaterCreature(actor);
|
||||||
const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor);
|
const bool isFlyingCreature = actor.getClass().isPureFlyingCreature(actor);
|
||||||
|
const auto world = MWBase::Environment::get().getWorld();
|
||||||
|
const auto halfExtents = world->getPathfindingHalfExtents(actor);
|
||||||
|
const auto navigator = world->getNavigator();
|
||||||
|
const auto navigatorFlags = getNavigatorFlags(actor);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Determine a random location within radius of original position
|
// Determine a random location within radius of original position
|
||||||
const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability() * 0.8f) * wanderDistance;
|
const float wanderRadius = (0.2f + Misc::Rng::rollClosedProbability() * 0.8f) * wanderDistance;
|
||||||
const float randomDirection = Misc::Rng::rollClosedProbability() * 2.0f * osg::PI;
|
if (!isWaterCreature && !isFlyingCreature)
|
||||||
const float destinationX = mInitialActorPosition.x() + wanderRadius * std::cos(randomDirection);
|
{
|
||||||
const float destinationY = mInitialActorPosition.y() + wanderRadius * std::sin(randomDirection);
|
// findRandomPointAroundCircle uses wanderDistance as limit for random and not as exact distance
|
||||||
const float destinationZ = mInitialActorPosition.z();
|
if (const auto destination = navigator->findRandomPointAroundCircle(halfExtents, mInitialActorPosition, wanderDistance, navigatorFlags))
|
||||||
mDestination = osg::Vec3f(destinationX, destinationY, destinationZ);
|
mDestination = *destination;
|
||||||
|
else
|
||||||
|
mDestination = getRandomPointAround(mInitialActorPosition, wanderRadius);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mDestination = getRandomPointAround(mInitialActorPosition, wanderRadius);
|
||||||
|
|
||||||
// Check if land creature will walk onto water or if water creature will swim onto land
|
// Check if land creature will walk onto water or if water creature will swim onto land
|
||||||
if (!isWaterCreature && destinationIsAtWater(actor, mDestination))
|
if (!isWaterCreature && destinationIsAtWater(actor, mDestination))
|
||||||
|
@ -327,15 +332,9 @@ namespace MWMechanics
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (isWaterCreature || isFlyingCreature)
|
if (isWaterCreature || isFlyingCreature)
|
||||||
{
|
|
||||||
mPathFinder.buildStraightPath(mDestination);
|
mPathFinder.buildStraightPath(mDestination);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents, navigatorFlags);
|
||||||
const osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getPathfindingHalfExtents(actor);
|
|
||||||
mPathFinder.buildPathByNavMesh(actor, currentPosition, mDestination, halfExtents,
|
|
||||||
getNavigatorFlags(actor));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mPathFinder.isPathConstructed())
|
if (mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
|
@ -516,7 +515,7 @@ namespace MWMechanics
|
||||||
unsigned int randNode = Misc::Rng::rollDice(storage.mAllowedNodes.size());
|
unsigned int randNode = Misc::Rng::rollDice(storage.mAllowedNodes.size());
|
||||||
ESM::Pathgrid::Point dest(storage.mAllowedNodes[randNode]);
|
ESM::Pathgrid::Point dest(storage.mAllowedNodes[randNode]);
|
||||||
|
|
||||||
ToWorldCoordinates(dest, storage.mCell->getCell());
|
ToWorldCoordinates(dest, actor.getCell()->getCell());
|
||||||
|
|
||||||
// actor position is already in world coordinates
|
// actor position is already in world coordinates
|
||||||
const osg::Vec3f start = actorPos.asVec3();
|
const osg::Vec3f start = actorPos.asVec3();
|
||||||
|
|
|
@ -27,8 +27,6 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
float mReaction; // update some actions infrequently
|
float mReaction; // update some actions infrequently
|
||||||
|
|
||||||
const MWWorld::CellStore* mCell; // for detecting cell change
|
|
||||||
|
|
||||||
// AiWander states
|
// AiWander states
|
||||||
enum WanderState
|
enum WanderState
|
||||||
{
|
{
|
||||||
|
@ -60,7 +58,6 @@ namespace MWMechanics
|
||||||
|
|
||||||
AiWanderStorage():
|
AiWanderStorage():
|
||||||
mReaction(0),
|
mReaction(0),
|
||||||
mCell(nullptr),
|
|
||||||
mState(Wander_ChooseAction),
|
mState(Wander_ChooseAction),
|
||||||
mIsWanderingManually(false),
|
mIsWanderingManually(false),
|
||||||
mCanWanderAlongPathGrid(true),
|
mCanWanderAlongPathGrid(true),
|
||||||
|
@ -125,8 +122,7 @@ namespace MWMechanics
|
||||||
void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
void onIdleStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
||||||
void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
void onWalkingStatePerFrameActions(const MWWorld::Ptr& actor, float duration, AiWanderStorage& storage);
|
||||||
void onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
void onChooseActionStatePerFrameActions(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
||||||
bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage,
|
bool reactionTimeActions(const MWWorld::Ptr& actor, AiWanderStorage& storage, ESM::Position& pos);
|
||||||
const MWWorld::CellStore*& currentCell, bool cellChange, ESM::Position& pos);
|
|
||||||
bool isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
bool isPackageCompleted(const MWWorld::Ptr& actor, AiWanderStorage& storage);
|
||||||
void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance);
|
void wanderNearStart(const MWWorld::Ptr &actor, AiWanderStorage &storage, int wanderDistance);
|
||||||
bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);
|
bool destinationIsAtWater(const MWWorld::Ptr &actor, const osg::Vec3f& destination);
|
||||||
|
@ -141,7 +137,7 @@ namespace MWMechanics
|
||||||
bool mRepeat;
|
bool mRepeat;
|
||||||
|
|
||||||
bool mStoredInitialActorPosition;
|
bool mStoredInitialActorPosition;
|
||||||
osg::Vec3f mInitialActorPosition;
|
osg::Vec3f mInitialActorPosition; // Note: an original engine does not reset coordinates even when actor changes a cell
|
||||||
|
|
||||||
bool mHasDestination;
|
bool mHasDestination;
|
||||||
osg::Vec3f mDestination;
|
osg::Vec3f mDestination;
|
||||||
|
|
|
@ -77,24 +77,20 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
ObstacleCheck::ObstacleCheck()
|
ObstacleCheck::ObstacleCheck()
|
||||||
: mWalkState(State_Norm)
|
: mWalkState(WalkState::Initial)
|
||||||
, mStuckDuration(0)
|
, mStateDuration(0)
|
||||||
, mEvadeDuration(0)
|
|
||||||
, mDistSameSpot(-1) // avoid calculating it each time
|
|
||||||
, mEvadeDirectionIndex(0)
|
, mEvadeDirectionIndex(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObstacleCheck::clear()
|
void ObstacleCheck::clear()
|
||||||
{
|
{
|
||||||
mWalkState = State_Norm;
|
mWalkState = WalkState::Initial;
|
||||||
mStuckDuration = 0;
|
|
||||||
mEvadeDuration = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObstacleCheck::isEvading() const
|
bool ObstacleCheck::isEvading() const
|
||||||
{
|
{
|
||||||
return mWalkState == State_Evade;
|
return mWalkState == WalkState::Evade;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -103,63 +99,72 @@ namespace MWMechanics
|
||||||
*
|
*
|
||||||
* Walking state transitions (player greeting check not shown):
|
* Walking state transitions (player greeting check not shown):
|
||||||
*
|
*
|
||||||
* MoveNow <------------------------------------+
|
* Initial ----> Norm <--------> CheckStuck -------> Evade ---+
|
||||||
* | d|
|
|
||||||
* | |
|
|
||||||
* +-> State_Norm <---> State_CheckStuck --> State_Evade
|
|
||||||
* ^ ^ | f ^ | t ^ | |
|
* ^ ^ | f ^ | t ^ | |
|
||||||
* | | | | | | | |
|
* | | | | | | | |
|
||||||
* | +---+ +---+ +---+ | u
|
* | +-+ +---+ +---+ | u
|
||||||
* | any < t < u |
|
* | any < t < u |
|
||||||
* +--------------------------------------------+
|
* +---------------------------------------------+
|
||||||
*
|
*
|
||||||
* f = one reaction time
|
* f = one reaction time
|
||||||
* d = proximity to a closed door
|
|
||||||
* t = how long before considered stuck
|
* t = how long before considered stuck
|
||||||
* u = how long to move sideways
|
* u = how long to move sideways
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void ObstacleCheck::update(const MWWorld::Ptr& actor, float duration)
|
void ObstacleCheck::update(const MWWorld::Ptr& actor, const osg::Vec3f& destination, float duration)
|
||||||
{
|
{
|
||||||
const osg::Vec3f pos = actor.getRefData().getPosition().asVec3();
|
const auto position = actor.getRefData().getPosition().asVec3();
|
||||||
|
|
||||||
if (mDistSameSpot == -1)
|
if (mWalkState == WalkState::Initial)
|
||||||
mDistSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor);
|
|
||||||
|
|
||||||
const float distSameSpot = mDistSameSpot * duration;
|
|
||||||
const bool samePosition = (pos - mPrev).length2() < distSameSpot * distSameSpot;
|
|
||||||
|
|
||||||
mPrev = pos;
|
|
||||||
|
|
||||||
if (mWalkState != State_Evade)
|
|
||||||
{
|
{
|
||||||
if(!samePosition)
|
mWalkState = WalkState::Norm;
|
||||||
{
|
mStateDuration = 0;
|
||||||
mWalkState = State_Norm;
|
mPrev = position;
|
||||||
mStuckDuration = 0;
|
|
||||||
mEvadeDuration = 0;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mWalkState = State_CheckStuck;
|
if (mWalkState != WalkState::Evade)
|
||||||
mStuckDuration += duration;
|
|
||||||
// consider stuck only if position unchanges for a period
|
|
||||||
if(mStuckDuration < DURATION_SAME_SPOT)
|
|
||||||
return; // still checking, note duration added to timer
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
mStuckDuration = 0;
|
const float distSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor) * duration;
|
||||||
mWalkState = State_Evade;
|
const float prevDistance = (destination - mPrev).length();
|
||||||
chooseEvasionDirection();
|
const float currentDistance = (destination - position).length();
|
||||||
}
|
const float movedDistance = prevDistance - currentDistance;
|
||||||
|
|
||||||
|
mPrev = position;
|
||||||
|
|
||||||
|
if (movedDistance >= distSameSpot)
|
||||||
|
{
|
||||||
|
mWalkState = WalkState::Norm;
|
||||||
|
mStateDuration = 0;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mEvadeDuration += duration;
|
if (mWalkState == WalkState::Norm)
|
||||||
if(mEvadeDuration >= DURATION_TO_EVADE)
|
{
|
||||||
|
mWalkState = WalkState::CheckStuck;
|
||||||
|
mStateDuration = duration;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mStateDuration += duration;
|
||||||
|
if (mStateDuration < DURATION_SAME_SPOT)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mWalkState = WalkState::Evade;
|
||||||
|
mStateDuration = 0;
|
||||||
|
chooseEvasionDirection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mStateDuration += duration;
|
||||||
|
if(mStateDuration >= DURATION_TO_EVADE)
|
||||||
{
|
{
|
||||||
// tried to evade, assume all is ok and start again
|
// tried to evade, assume all is ok and start again
|
||||||
mWalkState = State_Norm;
|
mWalkState = WalkState::Norm;
|
||||||
mEvadeDuration = 0;
|
mStateDuration = 0;
|
||||||
|
mPrev = position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,30 +32,27 @@ namespace MWMechanics
|
||||||
bool isEvading() const;
|
bool isEvading() const;
|
||||||
|
|
||||||
// Updates internal state, call each frame for moving actor
|
// Updates internal state, call each frame for moving actor
|
||||||
void update(const MWWorld::Ptr& actor, float duration);
|
void update(const MWWorld::Ptr& actor, const osg::Vec3f& destination, float duration);
|
||||||
|
|
||||||
// change direction to try to fix "stuck" actor
|
// change direction to try to fix "stuck" actor
|
||||||
void takeEvasiveAction(MWMechanics::Movement& actorMovement) const;
|
void takeEvasiveAction(MWMechanics::Movement& actorMovement) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// for checking if we're stuck
|
|
||||||
osg::Vec3f mPrev;
|
osg::Vec3f mPrev;
|
||||||
|
|
||||||
// directions to try moving in when get stuck
|
// directions to try moving in when get stuck
|
||||||
static const float evadeDirections[NUM_EVADE_DIRECTIONS][2];
|
static const float evadeDirections[NUM_EVADE_DIRECTIONS][2];
|
||||||
|
|
||||||
enum WalkState
|
enum class WalkState
|
||||||
{
|
{
|
||||||
State_Norm,
|
Initial,
|
||||||
State_CheckStuck,
|
Norm,
|
||||||
State_Evade
|
CheckStuck,
|
||||||
|
Evade
|
||||||
};
|
};
|
||||||
WalkState mWalkState;
|
WalkState mWalkState;
|
||||||
|
|
||||||
float mStuckDuration; // accumulate time here while in same spot
|
float mStateDuration;
|
||||||
float mEvadeDuration;
|
|
||||||
float mDistSameSpot; // take account of actor's speed
|
|
||||||
int mEvadeDirectionIndex;
|
int mEvadeDirectionIndex;
|
||||||
|
|
||||||
void chooseEvasionDirection();
|
void chooseEvasionDirection();
|
||||||
|
|
|
@ -314,7 +314,9 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
mPath.clear();
|
mPath.clear();
|
||||||
|
|
||||||
buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath));
|
// If it's not possible to build path over navmesh due to disabled navmesh generation fallback to straight path
|
||||||
|
if (!buildPathByNavigatorImpl(actor, startPoint, endPoint, halfExtents, flags, std::back_inserter(mPath)))
|
||||||
|
mPath.push_back(endPoint);
|
||||||
|
|
||||||
mConstructed = true;
|
mConstructed = true;
|
||||||
}
|
}
|
||||||
|
@ -335,7 +337,7 @@ namespace MWMechanics
|
||||||
mConstructed = true;
|
mConstructed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
|
bool PathFinder::buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
|
||||||
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
|
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
|
||||||
std::back_insert_iterator<std::deque<osg::Vec3f>> out)
|
std::back_insert_iterator<std::deque<osg::Vec3f>> out)
|
||||||
{
|
{
|
||||||
|
@ -344,7 +346,7 @@ namespace MWMechanics
|
||||||
const auto world = MWBase::Environment::get().getWorld();
|
const auto world = MWBase::Environment::get().getWorld();
|
||||||
const auto stepSize = getPathStepSize(actor);
|
const auto stepSize = getPathStepSize(actor);
|
||||||
const auto navigator = world->getNavigator();
|
const auto navigator = world->getNavigator();
|
||||||
navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out);
|
return navigator->findPath(halfExtents, stepSize, startPoint, endPoint, flags, out).is_initialized();
|
||||||
}
|
}
|
||||||
catch (const DetourNavigator::NavigatorException& exception)
|
catch (const DetourNavigator::NavigatorException& exception)
|
||||||
{
|
{
|
||||||
|
@ -352,6 +354,7 @@ namespace MWMechanics
|
||||||
<< "\" for \"" << actor.getClass().getName(actor) << "\" (" << actor.getBase()
|
<< "\" for \"" << actor.getClass().getName(actor) << "\" (" << actor.getBase()
|
||||||
<< ") from " << startPoint << " to " << endPoint << " with flags ("
|
<< ") from " << startPoint << " to " << endPoint << " with flags ("
|
||||||
<< DetourNavigator::WriteFlags {flags} << ")";
|
<< DetourNavigator::WriteFlags {flags} << ")";
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -201,7 +201,7 @@ namespace MWMechanics
|
||||||
void buildPathByPathgridImpl(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
void buildPathByPathgridImpl(const osg::Vec3f& startPoint, const osg::Vec3f& endPoint,
|
||||||
const PathgridGraph& pathgridGraph, std::back_insert_iterator<std::deque<osg::Vec3f>> out);
|
const PathgridGraph& pathgridGraph, std::back_insert_iterator<std::deque<osg::Vec3f>> out);
|
||||||
|
|
||||||
void buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
|
bool buildPathByNavigatorImpl(const MWWorld::ConstPtr& actor, const osg::Vec3f& startPoint,
|
||||||
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
|
const osg::Vec3f& endPoint, const osg::Vec3f& halfExtents, const DetourNavigator::Flags flags,
|
||||||
std::back_insert_iterator<std::deque<osg::Vec3f>> out);
|
std::back_insert_iterator<std::deque<osg::Vec3f>> out);
|
||||||
};
|
};
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
|
#include "../mwbase/soundmanager.hpp"
|
||||||
|
|
||||||
#include "interpretercontext.hpp"
|
#include "interpretercontext.hpp"
|
||||||
#include "ref.hpp"
|
#include "ref.hpp"
|
||||||
|
@ -501,6 +502,14 @@ namespace MWScript
|
||||||
if (!targetPtr.isEmpty() && targetPtr.getCellRef().getRefId() == testedTargetId)
|
if (!targetPtr.isEmpty() && targetPtr.getCellRef().getRefId() == testedTargetId)
|
||||||
targetsAreEqual = true;
|
targetsAreEqual = true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool turningToPlayer = creatureStats.isTurningToPlayer();
|
||||||
|
bool greeting = creatureStats.getGreetingState() == MWMechanics::Greet_InProgress;
|
||||||
|
bool sayActive = MWBase::Environment::get().getSoundManager()->sayActive(actor);
|
||||||
|
if (turningToPlayer || (greeting && sayActive))
|
||||||
|
targetsAreEqual = (testedTargetId == "player"); // Currently the player ID is hardcoded
|
||||||
|
}
|
||||||
runtime.push(int(targetsAreEqual));
|
runtime.push(int(targetsAreEqual));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,11 +2,14 @@
|
||||||
|
|
||||||
#include <components/detournavigator/navigatorimpl.hpp>
|
#include <components/detournavigator/navigatorimpl.hpp>
|
||||||
#include <components/detournavigator/exceptions.hpp>
|
#include <components/detournavigator/exceptions.hpp>
|
||||||
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
|
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
|
||||||
#include <BulletCollision/CollisionShapes/btBoxShape.h>
|
#include <BulletCollision/CollisionShapes/btBoxShape.h>
|
||||||
#include <BulletCollision/CollisionShapes/btCompoundShape.h>
|
#include <BulletCollision/CollisionShapes/btCompoundShape.h>
|
||||||
|
|
||||||
|
#include <boost/optional/optional_io.hpp>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
@ -655,4 +658,32 @@ namespace
|
||||||
osg::Vec3f(215, -215, 1.87718021869659423828125),
|
osg::Vec3f(215, -215, 1.87718021869659423828125),
|
||||||
})) << mPath;
|
})) << mPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(DetourNavigatorNavigatorTest, update_then_find_random_point_around_circle_should_return_position)
|
||||||
|
{
|
||||||
|
const std::array<btScalar, 5 * 5> heightfieldData {{
|
||||||
|
0, 0, 0, 0, 0,
|
||||||
|
0, -25, -25, -25, -25,
|
||||||
|
0, -25, -100, -100, -100,
|
||||||
|
0, -25, -100, -100, -100,
|
||||||
|
0, -25, -100, -100, -100,
|
||||||
|
}};
|
||||||
|
btHeightfieldTerrainShape shape(5, 5, heightfieldData.data(), 1, 0, 0, 2, PHY_FLOAT, false);
|
||||||
|
shape.setLocalScaling(btVector3(128, 128, 1));
|
||||||
|
|
||||||
|
mNavigator->addAgent(mAgentHalfExtents);
|
||||||
|
mNavigator->addObject(ObjectId(&shape), shape, btTransform::getIdentity());
|
||||||
|
mNavigator->update(mPlayerPosition);
|
||||||
|
mNavigator->wait();
|
||||||
|
|
||||||
|
Misc::Rng::init(42);
|
||||||
|
|
||||||
|
const auto result = mNavigator->findRandomPointAroundCircle(mAgentHalfExtents, mStart, 100.0, Flag_walk);
|
||||||
|
|
||||||
|
ASSERT_EQ(result, boost::optional<osg::Vec3f>(osg::Vec3f(-209.95985412597656, 129.89768981933594, -0.26253718137741089)));
|
||||||
|
|
||||||
|
const auto distance = (*result - mStart).length();
|
||||||
|
|
||||||
|
EXPECT_EQ(distance, 85.260780334472656) << distance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -245,6 +245,8 @@ add_component_dir(detournavigator
|
||||||
recastmeshobject
|
recastmeshobject
|
||||||
navmeshtilescache
|
navmeshtilescache
|
||||||
settings
|
settings
|
||||||
|
navigator
|
||||||
|
findrandompointaroundcircle
|
||||||
)
|
)
|
||||||
|
|
||||||
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
|
||||||
|
|
45
components/detournavigator/findrandompointaroundcircle.cpp
Normal file
45
components/detournavigator/findrandompointaroundcircle.cpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include "findrandompointaroundcircle.hpp"
|
||||||
|
#include "settings.hpp"
|
||||||
|
#include "findsmoothpath.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
|
#include <DetourCommon.h>
|
||||||
|
#include <DetourNavMesh.h>
|
||||||
|
#include <DetourNavMeshQuery.h>
|
||||||
|
|
||||||
|
namespace DetourNavigator
|
||||||
|
{
|
||||||
|
boost::optional<osg::Vec3f> findRandomPointAroundCircle(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents,
|
||||||
|
const osg::Vec3f& start, const float maxRadius, const Flags includeFlags, const Settings& settings)
|
||||||
|
{
|
||||||
|
dtNavMeshQuery navMeshQuery;
|
||||||
|
initNavMeshQuery(navMeshQuery, navMesh, settings.mMaxNavMeshQueryNodes);
|
||||||
|
|
||||||
|
dtQueryFilter queryFilter;
|
||||||
|
queryFilter.setIncludeFlags(includeFlags);
|
||||||
|
|
||||||
|
dtPolyRef startRef = 0;
|
||||||
|
osg::Vec3f startPolygonPosition;
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
const auto status = navMeshQuery.findNearestPoly(start.ptr(), (halfExtents * (1 << i)).ptr(), &queryFilter,
|
||||||
|
&startRef, startPolygonPosition.ptr());
|
||||||
|
if (!dtStatusFailed(status) && startRef != 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startRef == 0)
|
||||||
|
return boost::optional<osg::Vec3f>();
|
||||||
|
|
||||||
|
dtPolyRef resultRef = 0;
|
||||||
|
osg::Vec3f resultPosition;
|
||||||
|
navMeshQuery.findRandomPointAroundCircle(startRef, start.ptr(), maxRadius, &queryFilter,
|
||||||
|
&Misc::Rng::rollProbability, &resultRef, resultPosition.ptr());
|
||||||
|
|
||||||
|
if (resultRef == 0)
|
||||||
|
return boost::optional<osg::Vec3f>();
|
||||||
|
|
||||||
|
return boost::optional<osg::Vec3f>(resultPosition);
|
||||||
|
}
|
||||||
|
}
|
20
components/detournavigator/findrandompointaroundcircle.hpp
Normal file
20
components/detournavigator/findrandompointaroundcircle.hpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_FINDRANDOMPOINTAROUNDCIRCLE_H
|
||||||
|
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_FINDRANDOMPOINTAROUNDCIRCLE_H
|
||||||
|
|
||||||
|
#include "flags.hpp"
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
|
#include <osg/Vec3f>
|
||||||
|
|
||||||
|
class dtNavMesh;
|
||||||
|
|
||||||
|
namespace DetourNavigator
|
||||||
|
{
|
||||||
|
struct Settings;
|
||||||
|
|
||||||
|
boost::optional<osg::Vec3f> findRandomPointAroundCircle(const dtNavMesh& navMesh, const osg::Vec3f& halfExtents,
|
||||||
|
const osg::Vec3f& start, const float maxRadius, const Flags includeFlags, const Settings& settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
20
components/detournavigator/navigator.cpp
Normal file
20
components/detournavigator/navigator.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include "findrandompointaroundcircle.hpp"
|
||||||
|
#include "navigator.hpp"
|
||||||
|
|
||||||
|
namespace DetourNavigator
|
||||||
|
{
|
||||||
|
boost::optional<osg::Vec3f> Navigator::findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents,
|
||||||
|
const osg::Vec3f& start, const float maxRadius, const Flags includeFlags) const
|
||||||
|
{
|
||||||
|
const auto navMesh = getNavMesh(agentHalfExtents);
|
||||||
|
if (!navMesh)
|
||||||
|
return boost::optional<osg::Vec3f>();
|
||||||
|
const auto settings = getSettings();
|
||||||
|
const auto result = DetourNavigator::findRandomPointAroundCircle(navMesh->lockConst()->getImpl(),
|
||||||
|
toNavMeshCoordinates(settings, agentHalfExtents), toNavMeshCoordinates(settings, start),
|
||||||
|
toNavMeshCoordinates(settings, maxRadius), includeFlags, settings);
|
||||||
|
if (!result)
|
||||||
|
return boost::optional<osg::Vec3f>();
|
||||||
|
return boost::optional<osg::Vec3f>(fromNavMeshCoordinates(settings, *result));
|
||||||
|
}
|
||||||
|
}
|
|
@ -157,10 +157,9 @@ namespace DetourNavigator
|
||||||
* @param out the beginning of the destination range.
|
* @param out the beginning of the destination range.
|
||||||
* @return Output iterator to the element in the destination range, one past the last element of found path.
|
* @return Output iterator to the element in the destination range, one past the last element of found path.
|
||||||
* Equal to out if no path is found.
|
* Equal to out if no path is found.
|
||||||
* @throws InvalidArgument if there is no navmesh for given agentHalfExtents.
|
|
||||||
*/
|
*/
|
||||||
template <class OutputIterator>
|
template <class OutputIterator>
|
||||||
OutputIterator findPath(const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start,
|
boost::optional<OutputIterator> findPath(const osg::Vec3f& agentHalfExtents, const float stepSize, const osg::Vec3f& start,
|
||||||
const osg::Vec3f& end, const Flags includeFlags, OutputIterator out) const
|
const osg::Vec3f& end, const Flags includeFlags, OutputIterator out) const
|
||||||
{
|
{
|
||||||
static_assert(
|
static_assert(
|
||||||
|
@ -172,7 +171,7 @@ namespace DetourNavigator
|
||||||
);
|
);
|
||||||
const auto navMesh = getNavMesh(agentHalfExtents);
|
const auto navMesh = getNavMesh(agentHalfExtents);
|
||||||
if (!navMesh)
|
if (!navMesh)
|
||||||
return out;
|
return {};
|
||||||
const auto settings = getSettings();
|
const auto settings = getSettings();
|
||||||
return findSmoothPath(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings, agentHalfExtents),
|
return findSmoothPath(navMesh->lockConst()->getImpl(), toNavMeshCoordinates(settings, agentHalfExtents),
|
||||||
toNavMeshCoordinates(settings, stepSize), toNavMeshCoordinates(settings, start),
|
toNavMeshCoordinates(settings, stepSize), toNavMeshCoordinates(settings, start),
|
||||||
|
@ -194,6 +193,17 @@ namespace DetourNavigator
|
||||||
virtual const Settings& getSettings() const = 0;
|
virtual const Settings& getSettings() const = 0;
|
||||||
|
|
||||||
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
|
virtual void reportStats(unsigned int frameNumber, osg::Stats& stats) const = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief findRandomPointAroundCircle returns random location on navmesh within the reach of specified location.
|
||||||
|
* @param agentHalfExtents allows to find navmesh for given actor.
|
||||||
|
* @param start path from given point.
|
||||||
|
* @param maxRadius limit maximum distance from start.
|
||||||
|
* @param includeFlags setup allowed surfaces for actor to walk.
|
||||||
|
* @return not empty optional with position if point is found and empty optional if point is not found.
|
||||||
|
*/
|
||||||
|
boost::optional<osg::Vec3f> findRandomPointAroundCircle(const osg::Vec3f& agentHalfExtents,
|
||||||
|
const osg::Vec3f& start, const float maxRadius, const Flags includeFlags) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,8 +158,24 @@ namespace ESM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDataTypes & Land::DATA_WNAM) {
|
if (mDataTypes & Land::DATA_WNAM)
|
||||||
esm.writeHNT("WNAM", mWnam, 81);
|
{
|
||||||
|
// Generate WNAM record
|
||||||
|
signed char wnam[LAND_GLOBAL_MAP_LOD_SIZE];
|
||||||
|
float max = std::numeric_limits<signed char>::max();
|
||||||
|
float min = std::numeric_limits<signed char>::min();
|
||||||
|
float vertMult = static_cast<float>(ESM::Land::LAND_SIZE - 1) / LAND_GLOBAL_MAP_LOD_SIZE_SQRT;
|
||||||
|
for (int row = 0; row < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++row)
|
||||||
|
{
|
||||||
|
for (int col = 0; col < LAND_GLOBAL_MAP_LOD_SIZE_SQRT; ++col)
|
||||||
|
{
|
||||||
|
float height = mLandData->mHeights[int(row * vertMult) * ESM::Land::LAND_SIZE + int(col * vertMult)];
|
||||||
|
height /= height > 0 ? 128.f : 16.f;
|
||||||
|
height = std::min(max, std::max(min, height));
|
||||||
|
wnam[row * LAND_GLOBAL_MAP_LOD_SIZE_SQRT + col] = static_cast<signed char>(height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
esm.writeHNT("WNAM", wnam, 81);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mLandData)
|
if (mLandData)
|
||||||
|
|
|
@ -70,6 +70,8 @@ struct Land
|
||||||
|
|
||||||
static const int LAND_GLOBAL_MAP_LOD_SIZE = 81;
|
static const int LAND_GLOBAL_MAP_LOD_SIZE = 81;
|
||||||
|
|
||||||
|
static const int LAND_GLOBAL_MAP_LOD_SIZE_SQRT = 9;
|
||||||
|
|
||||||
#pragma pack(push,1)
|
#pragma pack(push,1)
|
||||||
struct VHGT
|
struct VHGT
|
||||||
{
|
{
|
||||||
|
|
|
@ -59,7 +59,7 @@ public:
|
||||||
controller.post(nif);
|
controller.post(nif);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
typedef Named NiSequenceStreamHelper;
|
using NiSequenceStreamHelper = Named;
|
||||||
|
|
||||||
} // Namespace
|
} // Namespace
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -10,17 +10,18 @@ namespace Nif
|
||||||
Named::read(nif);
|
Named::read(nif);
|
||||||
|
|
||||||
external = nif->getChar() != 0;
|
external = nif->getChar() != 0;
|
||||||
|
bool internal = false;
|
||||||
if (external)
|
if (external)
|
||||||
filename = nif->getString();
|
filename = nif->getString();
|
||||||
else
|
else
|
||||||
{
|
internal = nif->getChar();
|
||||||
nif->getChar(); // always 1
|
|
||||||
data.read(nif);
|
|
||||||
}
|
|
||||||
|
|
||||||
pixel = nif->getInt();
|
if (!external && internal)
|
||||||
mipmap = nif->getInt();
|
data.read(nif);
|
||||||
alpha = nif->getInt();
|
|
||||||
|
pixel = nif->getUInt();
|
||||||
|
mipmap = nif->getUInt();
|
||||||
|
alpha = nif->getUInt();
|
||||||
|
|
||||||
nif->getChar(); // always 1
|
nif->getChar(); // always 1
|
||||||
}
|
}
|
||||||
|
@ -113,8 +114,4 @@ namespace Nif
|
||||||
mCenter = nif->getVector3();
|
mCenter = nif->getVector3();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,13 +46,13 @@ public:
|
||||||
3 - Compressed
|
3 - Compressed
|
||||||
4 - Bumpmap
|
4 - Bumpmap
|
||||||
5 - Default */
|
5 - Default */
|
||||||
int pixel;
|
unsigned int pixel;
|
||||||
|
|
||||||
/* Mipmap format
|
/* Mipmap format
|
||||||
0 - no
|
0 - no
|
||||||
1 - yes
|
1 - yes
|
||||||
2 - default */
|
2 - default */
|
||||||
int mipmap;
|
unsigned int mipmap;
|
||||||
|
|
||||||
/* Alpha
|
/* Alpha
|
||||||
0 - none
|
0 - none
|
||||||
|
@ -60,7 +60,7 @@ public:
|
||||||
2 - smooth
|
2 - smooth
|
||||||
3 - default (use material alpha, or multiply material with texture if present)
|
3 - default (use material alpha, or multiply material with texture if present)
|
||||||
*/
|
*/
|
||||||
int alpha;
|
unsigned int alpha;
|
||||||
|
|
||||||
void read(NIFStream *nif);
|
void read(NIFStream *nif);
|
||||||
void post(NIFFile *nif);
|
void post(NIFFile *nif);
|
||||||
|
|
|
@ -92,6 +92,12 @@ namespace Nif
|
||||||
void NiMaterialColorController::read(NIFStream *nif)
|
void NiMaterialColorController::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
Controller::read(nif);
|
Controller::read(nif);
|
||||||
|
// Two bits that correspond to the controlled material color.
|
||||||
|
// 00: Ambient
|
||||||
|
// 01: Diffuse
|
||||||
|
// 10: Specular
|
||||||
|
// 11: Emissive
|
||||||
|
targetColor = (flags >> 4) & 3;
|
||||||
data.read(nif);
|
data.read(nif);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +195,8 @@ namespace Nif
|
||||||
{
|
{
|
||||||
Controller::read(nif);
|
Controller::read(nif);
|
||||||
data.read(nif);
|
data.read(nif);
|
||||||
nif->getChar(); // always 0
|
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW)
|
||||||
|
/*bool alwaysActive = */nif->getChar(); // Always 0
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiGeomMorpherController::post(NIFFile *nif)
|
void NiGeomMorpherController::post(NIFFile *nif)
|
||||||
|
|
|
@ -78,12 +78,13 @@ public:
|
||||||
void read(NIFStream *nif);
|
void read(NIFStream *nif);
|
||||||
void post(NIFFile *nif);
|
void post(NIFFile *nif);
|
||||||
};
|
};
|
||||||
typedef NiParticleSystemController NiBSPArrayController;
|
using NiBSPArrayController = NiParticleSystemController;
|
||||||
|
|
||||||
class NiMaterialColorController : public Controller
|
class NiMaterialColorController : public Controller
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NiPosDataPtr data;
|
NiPosDataPtr data;
|
||||||
|
unsigned int targetColor;
|
||||||
|
|
||||||
void read(NIFStream *nif);
|
void read(NIFStream *nif);
|
||||||
void post(NIFFile *nif);
|
void post(NIFFile *nif);
|
||||||
|
|
|
@ -225,7 +225,8 @@ void NiSkinData::read(NIFStream *nif)
|
||||||
trafo.scale = nif->getFloat();
|
trafo.scale = nif->getFloat();
|
||||||
|
|
||||||
int boneNum = nif->getInt();
|
int boneNum = nif->getInt();
|
||||||
nif->getInt(); // -1
|
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFFile::NIFVersion::VER_GAMEBRYO)
|
||||||
|
nif->skip(4); // NiSkinPartition link
|
||||||
|
|
||||||
bones.resize(boneNum);
|
bones.resize(boneNum);
|
||||||
for (BoneInfo &bi : bones)
|
for (BoneInfo &bi : bones)
|
||||||
|
|
|
@ -143,7 +143,7 @@ void NIFFile::parse(Files::IStreamPtr stream)
|
||||||
ver = nif.getUInt();
|
ver = nif.getUInt();
|
||||||
// 4.0.0.0 is an older, practically identical version of the format.
|
// 4.0.0.0 is an older, practically identical version of the format.
|
||||||
// It's not used by Morrowind assets but Morrowind supports it.
|
// It's not used by Morrowind assets but Morrowind supports it.
|
||||||
if(ver != VER_4_0_0_0 && ver != VER_MW)
|
if(ver != nif.generateVersion(4,0,0,0) && ver != VER_MW)
|
||||||
fail("Unsupported NIF version: " + printVersion(ver));
|
fail("Unsupported NIF version: " + printVersion(ver));
|
||||||
// Number of records
|
// Number of records
|
||||||
size_t recNum = nif.getInt();
|
size_t recNum = nif.getInt();
|
||||||
|
|
|
@ -75,26 +75,16 @@ class NIFFile final : public File
|
||||||
void operator = (NIFFile const &);
|
void operator = (NIFFile const &);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// For generic versions NIFStream::generateVersion() is used instead
|
||||||
enum NIFVersion
|
enum NIFVersion
|
||||||
{
|
{
|
||||||
// Feature-relevant
|
VER_MW = 0x04000002, // 4.0.0.2. Main Morrowind NIF version.
|
||||||
VER_4_1_0_0 = 0x04010000, // 1-byte booleans (previously 4-byte)
|
VER_CI = 0x04020200, // 4.2.2.0. Main Culpa Innata NIF version, also used in Civ4.
|
||||||
VER_5_0_0_1 = 0x05000001, // Optimized record type listings
|
VER_ZT2 = 0x0A000100, // 10.0.1.0. Main Zoo Tycoon 2 NIF version, also used in Oblivion and Civ4.
|
||||||
VER_5_0_0_6 = 0x05000006, // Record groups
|
VER_OB_OLD = 0x0A000102, // 10.0.1.2. Main older Oblivion NIF version.
|
||||||
VER_10_0_1_8 = 0x0A000108, // The last version without user version
|
|
||||||
VER_20_1_0_1 = 0x14010001, // String tables
|
|
||||||
VER_20_2_0_5 = 0x14020005, // Record sizes
|
|
||||||
// Game-relevant
|
|
||||||
VER_4_0_0_0 = 0x04000000, // Freedom Force NIFs, supported by Morrowind
|
|
||||||
VER_MW = 0x04000002, // 4.0.0.2. Morrowind and Freedom Force NIFs
|
|
||||||
VER_4_2_1_0 = 0x04020100, // Used in Civ4 and Dark Age of Camelot
|
|
||||||
VER_CI = 0x04020200, // 4.2.2.0. Main Culpa Innata NIF version, also used in Civ4
|
|
||||||
VER_ZT2 = 0x0A000100, // 10.0.1.0. Main Zoo Tycoon 2 NIF version, also used in Oblivion and Civ4
|
|
||||||
VER_OB_OLD = 0x0A000102, // 10.0.1.2. Main older Oblivion NIF version
|
|
||||||
VER_GAMEBRYO = 0x0A010000, // 10.1.0.0. Lots of games use it. The first version that has Gamebryo File Format header.
|
VER_GAMEBRYO = 0x0A010000, // 10.1.0.0. Lots of games use it. The first version that has Gamebryo File Format header.
|
||||||
VER_10_2_0_0 = 0x0A020000, // Lots of games use this version as well.
|
|
||||||
VER_CIV4 = 0x14000004, // 20.0.0.4. Main Civilization IV NIF version.
|
VER_CIV4 = 0x14000004, // 20.0.0.4. Main Civilization IV NIF version.
|
||||||
VER_OB = 0x14000005, // 20.0.0.5. Main Oblivion NIF version
|
VER_OB = 0x14000005, // 20.0.0.5. Main Oblivion NIF version.
|
||||||
VER_BGS = 0x14020007 // 20.2.0.7. Main Fallout 3/4/76/New Vegas and Skyrim/SkyrimSE NIF version.
|
VER_BGS = 0x14020007 // 20.2.0.7. Main Fallout 3/4/76/New Vegas and Skyrim/SkyrimSE NIF version.
|
||||||
};
|
};
|
||||||
enum BethVersion
|
enum BethVersion
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace Nif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience utility functions: get the versions of the currently read file
|
// Convenience utility functions: get the versions of the currently read file
|
||||||
unsigned int NIFStream::getVersion() { return file->getVersion(); }
|
unsigned int NIFStream::getVersion() const { return file->getVersion(); }
|
||||||
unsigned int NIFStream::getUserVersion() { return file->getBethVersion(); }
|
unsigned int NIFStream::getUserVersion() const { return file->getBethVersion(); }
|
||||||
unsigned int NIFStream::getBethVersion() { return file->getBethVersion(); }
|
unsigned int NIFStream::getBethVersion() const { return file->getBethVersion(); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,9 +159,15 @@ public:
|
||||||
|
|
||||||
std::string getString();
|
std::string getString();
|
||||||
|
|
||||||
unsigned int getVersion();
|
unsigned int getVersion() const;
|
||||||
unsigned int getUserVersion();
|
unsigned int getUserVersion() const;
|
||||||
unsigned int getBethVersion();
|
unsigned int getBethVersion() const;
|
||||||
|
|
||||||
|
// Convert human-readable version numbers into a number that can be compared.
|
||||||
|
static constexpr uint32_t generateVersion(uint8_t major, uint8_t minor, uint8_t patch, uint8_t rev)
|
||||||
|
{
|
||||||
|
return (major << 24) + (minor << 16) + (patch << 8) + rev;
|
||||||
|
}
|
||||||
|
|
||||||
///Read in a string of the given length
|
///Read in a string of the given length
|
||||||
std::string getSizedString(size_t length)
|
std::string getSizedString(size_t length)
|
||||||
|
|
|
@ -284,6 +284,7 @@ struct NiLODNode : public NiSwitchNode
|
||||||
void read(NIFStream *nif)
|
void read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
NiSwitchNode::read(nif);
|
NiSwitchNode::read(nif);
|
||||||
|
if (nif->getVersion() >= NIFFile::NIFVersion::VER_MW && nif->getVersion() <= NIFFile::NIFVersion::VER_ZT2)
|
||||||
lodCenter = nif->getVector3();
|
lodCenter = nif->getVector3();
|
||||||
unsigned int numLodLevels = nif->getUInt();
|
unsigned int numLodLevels = nif->getUInt();
|
||||||
for (unsigned int i=0; i<numLodLevels; ++i)
|
for (unsigned int i=0; i<numLodLevels; ++i)
|
||||||
|
|
|
@ -768,12 +768,7 @@ namespace NifOsg
|
||||||
const Nif::NiMaterialColorController* matctrl = static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr());
|
const Nif::NiMaterialColorController* matctrl = static_cast<const Nif::NiMaterialColorController*>(ctrl.getPtr());
|
||||||
if (matctrl->data.empty())
|
if (matctrl->data.empty())
|
||||||
continue;
|
continue;
|
||||||
// Two bits that correspond to the controlled material color.
|
auto targetColor = static_cast<MaterialColorController::TargetColor>(matctrl->targetColor);
|
||||||
// 00: Ambient
|
|
||||||
// 01: Diffuse
|
|
||||||
// 10: Specular
|
|
||||||
// 11: Emissive
|
|
||||||
MaterialColorController::TargetColor targetColor = static_cast<MaterialColorController::TargetColor>((matctrl->flags >> 4) & 3);
|
|
||||||
osg::ref_ptr<MaterialColorController> osgctrl(new MaterialColorController(matctrl->data.getPtr(), targetColor));
|
osg::ref_ptr<MaterialColorController> osgctrl(new MaterialColorController(matctrl->data.getPtr(), targetColor));
|
||||||
setupController(matctrl, osgctrl, animflags);
|
setupController(matctrl, osgctrl, animflags);
|
||||||
composite->addController(osgctrl);
|
composite->addController(osgctrl);
|
||||||
|
|
Loading…
Reference in a new issue