mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 19:19:56 +00:00
Merge branch 'master' into move
Conflicts: apps/opencs/view/render/object.cpp
This commit is contained in:
commit
6f619ea85f
138 changed files with 4601 additions and 1029 deletions
|
@ -5,4 +5,4 @@ mkdir build
|
||||||
cd build
|
cd build
|
||||||
export CODE_COVERAGE=1
|
export CODE_COVERAGE=1
|
||||||
if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi
|
if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi
|
||||||
${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DUSE_SYSTEM_TINYXML=TRUE
|
${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="None" -DUSE_SYSTEM_TINYXML=TRUE
|
||||||
|
|
|
@ -190,10 +190,6 @@ if (WIN32)
|
||||||
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
|
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ANDROID)
|
|
||||||
set(OPENGL_ES TRUE CACHE BOOL "enable opengl es support for android" FORCE)
|
|
||||||
endif (ANDROID)
|
|
||||||
|
|
||||||
option(OPENGL_ES "enable opengl es support" FALSE )
|
option(OPENGL_ES "enable opengl es support" FALSE )
|
||||||
|
|
||||||
if (OPENGL_ES)
|
if (OPENGL_ES)
|
||||||
|
@ -377,7 +373,7 @@ endif()
|
||||||
|
|
||||||
# CXX Compiler settings
|
# CXX Compiler settings
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -std=c++98 -pedantic -Wno-long-long")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -std=c++98 -pedantic -Wno-long-long")
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
|
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND NOT APPLE)
|
||||||
execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE CLANG_VERSION)
|
execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE CLANG_VERSION)
|
||||||
|
@ -566,7 +562,9 @@ endif(WIN32)
|
||||||
# Extern
|
# Extern
|
||||||
add_subdirectory (extern/osg-ffmpeg-videoplayer)
|
add_subdirectory (extern/osg-ffmpeg-videoplayer)
|
||||||
add_subdirectory (extern/oics)
|
add_subdirectory (extern/oics)
|
||||||
add_subdirectory (extern/osgQt)
|
if (USE_QT)
|
||||||
|
add_subdirectory (extern/osgQt)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Components
|
# Components
|
||||||
add_subdirectory (components)
|
add_subdirectory (components)
|
||||||
|
|
|
@ -26,7 +26,7 @@ opencs_units_noqt (model/world
|
||||||
universalid record commands columnbase columnimp scriptcontext cell refidcollection
|
universalid record commands columnbase columnimp scriptcontext cell refidcollection
|
||||||
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
|
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
|
||||||
pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
|
pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
|
||||||
idcompletionmanager metadata defaultgmsts
|
idcompletionmanager metadata defaultgmsts infoselectwrapper
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_hdrs_noqt (model/world
|
opencs_hdrs_noqt (model/world
|
||||||
|
@ -42,7 +42,7 @@ opencs_units_noqt (model/tools
|
||||||
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
|
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
|
||||||
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
|
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
|
||||||
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
|
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
|
||||||
mergestages gmstcheck
|
mergestages gmstcheck topicinfocheck journalcheck
|
||||||
)
|
)
|
||||||
|
|
||||||
opencs_hdrs_noqt (model/tools
|
opencs_hdrs_noqt (model/tools
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#ifndef CSV_PREFS_STATE_H
|
#ifndef CSM_PREFS_STATE_H
|
||||||
#define CSM_PREFS_STATE_H
|
#define CSM_PREFS_STATE_H
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
79
apps/opencs/model/tools/journalcheck.cpp
Normal file
79
apps/opencs/model/tools/journalcheck.cpp
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
#include "journalcheck.hpp"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
CSMTools::JournalCheckStage::JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue> &journals,
|
||||||
|
const CSMWorld::InfoCollection& journalInfos)
|
||||||
|
: mJournals(journals), mJournalInfos(journalInfos)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int CSMTools::JournalCheckStage::setup()
|
||||||
|
{
|
||||||
|
return mJournals.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
|
||||||
|
{
|
||||||
|
const CSMWorld::Record<ESM::Dialogue> &journalRecord = mJournals.getRecord(stage);
|
||||||
|
|
||||||
|
if (journalRecord.isDeleted())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ESM::Dialogue &journal = journalRecord.get();
|
||||||
|
int statusNamedCount = 0;
|
||||||
|
int totalInfoCount = 0;
|
||||||
|
std::set<int> questIndices;
|
||||||
|
|
||||||
|
CSMWorld::InfoCollection::Range range = mJournalInfos.getTopicRange(journal.mId);
|
||||||
|
|
||||||
|
for (CSMWorld::InfoCollection::RecordConstIterator it = range.first; it != range.second; ++it)
|
||||||
|
{
|
||||||
|
const CSMWorld::Record<CSMWorld::Info> infoRecord = (*it);
|
||||||
|
|
||||||
|
if (infoRecord.isDeleted())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const CSMWorld::Info& journalInfo = infoRecord.get();
|
||||||
|
|
||||||
|
totalInfoCount += 1;
|
||||||
|
|
||||||
|
if (journalInfo.mQuestStatus == ESM::DialInfo::QS_Name)
|
||||||
|
{
|
||||||
|
statusNamedCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (journalInfo.mResponse.empty())
|
||||||
|
{
|
||||||
|
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
|
||||||
|
|
||||||
|
messages.add(id, "Journal Info: missing description", "", CSMDoc::Message::Severity_Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::set<int>::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex);
|
||||||
|
|
||||||
|
// Duplicate index
|
||||||
|
if (result.second == false)
|
||||||
|
{
|
||||||
|
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
|
||||||
|
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << "Journal: duplicated quest index " << journalInfo.mData.mJournalIndex;
|
||||||
|
|
||||||
|
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalInfoCount == 0)
|
||||||
|
{
|
||||||
|
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
|
||||||
|
|
||||||
|
messages.add(id, "Journal: no defined Journal Infos", "", CSMDoc::Message::Severity_Warning);
|
||||||
|
}
|
||||||
|
else if (statusNamedCount > 1)
|
||||||
|
{
|
||||||
|
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId);
|
||||||
|
|
||||||
|
messages.add(id, "Journal: multiple infos with quest status \"Named\"", "", CSMDoc::Message::Severity_Error);
|
||||||
|
}
|
||||||
|
}
|
35
apps/opencs/model/tools/journalcheck.hpp
Normal file
35
apps/opencs/model/tools/journalcheck.hpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef CSM_TOOLS_JOURNALCHECK_H
|
||||||
|
#define CSM_TOOLS_JOURNALCHECK_H
|
||||||
|
|
||||||
|
#include <components/esm/loaddial.hpp>
|
||||||
|
|
||||||
|
#include "../world/idcollection.hpp"
|
||||||
|
#include "../world/infocollection.hpp"
|
||||||
|
|
||||||
|
#include "../doc/stage.hpp"
|
||||||
|
|
||||||
|
namespace CSMTools
|
||||||
|
{
|
||||||
|
/// \brief VerifyStage: make sure that journal infos are good
|
||||||
|
class JournalCheckStage : public CSMDoc::Stage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue>& journals,
|
||||||
|
const CSMWorld::InfoCollection& journalInfos);
|
||||||
|
|
||||||
|
virtual int setup();
|
||||||
|
///< \return number of steps
|
||||||
|
|
||||||
|
virtual void perform(int stage, CSMDoc::Messages& messages);
|
||||||
|
///< Messages resulting from this stage will be appended to \a messages
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
|
||||||
|
const CSMWorld::InfoCollection& mJournalInfos;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -30,6 +30,8 @@
|
||||||
#include "magiceffectcheck.hpp"
|
#include "magiceffectcheck.hpp"
|
||||||
#include "mergeoperation.hpp"
|
#include "mergeoperation.hpp"
|
||||||
#include "gmstcheck.hpp"
|
#include "gmstcheck.hpp"
|
||||||
|
#include "topicinfocheck.hpp"
|
||||||
|
#include "journalcheck.hpp"
|
||||||
|
|
||||||
CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
|
CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
|
||||||
{
|
{
|
||||||
|
@ -111,9 +113,24 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
|
||||||
mData.getReferenceables(),
|
mData.getReferenceables(),
|
||||||
mData.getResources (CSMWorld::UniversalId::Type_Icons),
|
mData.getResources (CSMWorld::UniversalId::Type_Icons),
|
||||||
mData.getResources (CSMWorld::UniversalId::Type_Textures)));
|
mData.getResources (CSMWorld::UniversalId::Type_Textures)));
|
||||||
|
|
||||||
mVerifierOperation->appendStage (new GmstCheckStage (mData.getGmsts()));
|
mVerifierOperation->appendStage (new GmstCheckStage (mData.getGmsts()));
|
||||||
|
|
||||||
|
mVerifierOperation->appendStage (new TopicInfoCheckStage (mData.getTopicInfos(),
|
||||||
|
mData.getCells(),
|
||||||
|
mData.getClasses(),
|
||||||
|
mData.getFactions(),
|
||||||
|
mData.getGmsts(),
|
||||||
|
mData.getGlobals(),
|
||||||
|
mData.getJournals(),
|
||||||
|
mData.getRaces(),
|
||||||
|
mData.getRegions(),
|
||||||
|
mData.getTopics(),
|
||||||
|
mData.getReferenceables().getDataSet(),
|
||||||
|
mData.getResources (CSMWorld::UniversalId::Type_SoundsRes)));
|
||||||
|
|
||||||
|
mVerifierOperation->appendStage (new JournalCheckStage(mData.getJournals(), mData.getJournalInfos()));
|
||||||
|
|
||||||
mVerifier.setOperation (mVerifierOperation);
|
mVerifier.setOperation (mVerifierOperation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
441
apps/opencs/model/tools/topicinfocheck.cpp
Normal file
441
apps/opencs/model/tools/topicinfocheck.cpp
Normal file
|
@ -0,0 +1,441 @@
|
||||||
|
#include "topicinfocheck.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "../world/infoselectwrapper.hpp"
|
||||||
|
|
||||||
|
CSMTools::TopicInfoCheckStage::TopicInfoCheckStage(
|
||||||
|
const CSMWorld::InfoCollection& topicInfos,
|
||||||
|
const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
|
||||||
|
const CSMWorld::IdCollection<ESM::Class>& classes,
|
||||||
|
const CSMWorld::IdCollection<ESM::Faction>& factions,
|
||||||
|
const CSMWorld::IdCollection<ESM::GameSetting>& gmsts,
|
||||||
|
const CSMWorld::IdCollection<ESM::Global>& globals,
|
||||||
|
const CSMWorld::IdCollection<ESM::Dialogue>& journals,
|
||||||
|
const CSMWorld::IdCollection<ESM::Race>& races,
|
||||||
|
const CSMWorld::IdCollection<ESM::Region>& regions,
|
||||||
|
const CSMWorld::IdCollection<ESM::Dialogue> &topics,
|
||||||
|
const CSMWorld::RefIdData& referencables,
|
||||||
|
const CSMWorld::Resources& soundFiles)
|
||||||
|
: mTopicInfos(topicInfos),
|
||||||
|
mCells(cells),
|
||||||
|
mClasses(classes),
|
||||||
|
mFactions(factions),
|
||||||
|
mGameSettings(gmsts),
|
||||||
|
mGlobals(globals),
|
||||||
|
mJournals(journals),
|
||||||
|
mRaces(races),
|
||||||
|
mRegions(regions),
|
||||||
|
mTopics(topics),
|
||||||
|
mReferencables(referencables),
|
||||||
|
mSoundFiles(soundFiles)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int CSMTools::TopicInfoCheckStage::setup()
|
||||||
|
{
|
||||||
|
// Generate list of cell names for reference checking
|
||||||
|
|
||||||
|
mCellNames.clear();
|
||||||
|
for (int i = 0; i < mCells.getSize(); ++i)
|
||||||
|
{
|
||||||
|
const CSMWorld::Record<CSMWorld::Cell>& cellRecord = mCells.getRecord(i);
|
||||||
|
|
||||||
|
if (cellRecord.isDeleted())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
mCellNames.insert(cellRecord.get().mName);
|
||||||
|
}
|
||||||
|
// Cell names can also include region names
|
||||||
|
for (int i = 0; i < mRegions.getSize(); ++i)
|
||||||
|
{
|
||||||
|
const CSMWorld::Record<ESM::Region>& regionRecord = mRegions.getRecord(i);
|
||||||
|
|
||||||
|
if (regionRecord.isDeleted())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
mCellNames.insert(regionRecord.get().mName);
|
||||||
|
}
|
||||||
|
// Default cell name
|
||||||
|
int index = mGameSettings.searchId("sDefaultCellname");
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
const CSMWorld::Record<ESM::GameSetting>& gmstRecord = mGameSettings.getRecord(index);
|
||||||
|
|
||||||
|
if (!gmstRecord.isDeleted() && gmstRecord.get().mValue.getType() == ESM::VT_String)
|
||||||
|
{
|
||||||
|
mCellNames.insert(gmstRecord.get().mValue.getString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mTopicInfos.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& messages)
|
||||||
|
{
|
||||||
|
const CSMWorld::Record<CSMWorld::Info>& infoRecord = mTopicInfos.getRecord(stage);
|
||||||
|
|
||||||
|
if (infoRecord.isDeleted())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const CSMWorld::Info& topicInfo = infoRecord.get();
|
||||||
|
|
||||||
|
// There should always be a topic that matches
|
||||||
|
int topicIndex = mTopics.searchId(topicInfo.mTopicId);
|
||||||
|
|
||||||
|
const CSMWorld::Record<ESM::Dialogue>& topicRecord = mTopics.getRecord(topicIndex);
|
||||||
|
|
||||||
|
if (topicRecord.isDeleted())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const ESM::Dialogue& topic = topicRecord.get();
|
||||||
|
|
||||||
|
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_TopicInfo, topicInfo.mId);
|
||||||
|
|
||||||
|
// Check fields
|
||||||
|
|
||||||
|
if (!topicInfo.mActor.empty())
|
||||||
|
{
|
||||||
|
verifyActor(topicInfo.mActor, id, messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!topicInfo.mClass.empty())
|
||||||
|
{
|
||||||
|
verifyId(topicInfo.mClass, mClasses, id, messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!topicInfo.mCell.empty())
|
||||||
|
{
|
||||||
|
verifyCell(topicInfo.mCell, id, messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!topicInfo.mFaction.empty())
|
||||||
|
{
|
||||||
|
if (verifyId(topicInfo.mFaction, mFactions, id, messages))
|
||||||
|
{
|
||||||
|
verifyFactionRank(topicInfo.mFaction, topicInfo.mData.mRank, id, messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!topicInfo.mPcFaction.empty())
|
||||||
|
{
|
||||||
|
if (verifyId(topicInfo.mPcFaction, mFactions, id, messages))
|
||||||
|
{
|
||||||
|
verifyFactionRank(topicInfo.mPcFaction, topicInfo.mData.mPCrank, id, messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1)
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
messages.add(id, "Gender: Value is invalid", "", CSMDoc::Message::Severity_Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!topicInfo.mRace.empty())
|
||||||
|
{
|
||||||
|
verifyId(topicInfo.mRace, mRaces, id, messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!topicInfo.mSound.empty())
|
||||||
|
{
|
||||||
|
verifySound(topicInfo.mSound, id, messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topicInfo.mResponse.empty() && topic.mType != ESM::Dialogue::Voice)
|
||||||
|
{
|
||||||
|
messages.add(id, "Response is empty", "", CSMDoc::Message::Severity_Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check info conditions
|
||||||
|
|
||||||
|
for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator it = topicInfo.mSelects.begin();
|
||||||
|
it != topicInfo.mSelects.end(); ++it)
|
||||||
|
{
|
||||||
|
verifySelectStruct((*it), id, messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verification functions
|
||||||
|
|
||||||
|
bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id,
|
||||||
|
CSMDoc::Messages& messages)
|
||||||
|
{
|
||||||
|
const std::string specifier = "Actor";
|
||||||
|
|
||||||
|
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor);
|
||||||
|
|
||||||
|
if (index.first == -1)
|
||||||
|
{
|
||||||
|
writeMissingIdError(specifier, actor, id, messages);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (mReferencables.getRecord(index).isDeleted())
|
||||||
|
{
|
||||||
|
writeDeletedRecordError(specifier, actor, id, messages);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature)
|
||||||
|
{
|
||||||
|
writeInvalidTypeError(specifier, actor, index.second, "NPC or Creature", id, messages);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id,
|
||||||
|
CSMDoc::Messages& messages)
|
||||||
|
{
|
||||||
|
const std::string specifier = "Cell";
|
||||||
|
|
||||||
|
if (mCellNames.find(cell) == mCellNames.end())
|
||||||
|
{
|
||||||
|
writeMissingIdError(specifier, cell, id, messages);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& factionName, int rank, const CSMWorld::UniversalId& id,
|
||||||
|
CSMDoc::Messages& messages)
|
||||||
|
{
|
||||||
|
if (rank < -1)
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << "Rank or PC Rank is set to " << rank << ", but should be set to -1 if no rank is required";
|
||||||
|
|
||||||
|
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = mFactions.searchId(factionName);
|
||||||
|
|
||||||
|
const ESM::Faction &faction = mFactions.getRecord(index).get();
|
||||||
|
|
||||||
|
int limit = 0;
|
||||||
|
for (; limit < 10; ++limit)
|
||||||
|
{
|
||||||
|
if (faction.mRanks[limit].empty())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rank >= limit)
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << "Rank or PC Rank is set to " << rank << " which is more than the maximum of " << limit - 1
|
||||||
|
<< " for the " << factionName << " faction";
|
||||||
|
|
||||||
|
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id,
|
||||||
|
CSMDoc::Messages& messages)
|
||||||
|
{
|
||||||
|
const std::string specifier = "Item";
|
||||||
|
|
||||||
|
CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item);
|
||||||
|
|
||||||
|
if (index.first == -1)
|
||||||
|
{
|
||||||
|
writeMissingIdError(specifier, item, id, messages);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (mReferencables.getRecord(index).isDeleted())
|
||||||
|
{
|
||||||
|
writeDeletedRecordError(specifier, item, id, messages);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (index.second)
|
||||||
|
{
|
||||||
|
case CSMWorld::UniversalId::Type_Potion:
|
||||||
|
case CSMWorld::UniversalId::Type_Apparatus:
|
||||||
|
case CSMWorld::UniversalId::Type_Armor:
|
||||||
|
case CSMWorld::UniversalId::Type_Book:
|
||||||
|
case CSMWorld::UniversalId::Type_Clothing:
|
||||||
|
case CSMWorld::UniversalId::Type_Ingredient:
|
||||||
|
case CSMWorld::UniversalId::Type_Light:
|
||||||
|
case CSMWorld::UniversalId::Type_Lockpick:
|
||||||
|
case CSMWorld::UniversalId::Type_Miscellaneous:
|
||||||
|
case CSMWorld::UniversalId::Type_Probe:
|
||||||
|
case CSMWorld::UniversalId::Type_Repair:
|
||||||
|
case CSMWorld::UniversalId::Type_Weapon:
|
||||||
|
case CSMWorld::UniversalId::Type_ItemLevelledList:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
writeInvalidTypeError(specifier, item, index.second, "Potion, Armor, Book, etc.", id, messages);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::SelectStruct& select,
|
||||||
|
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||||
|
{
|
||||||
|
CSMWorld::ConstInfoSelectWrapper infoCondition(select);
|
||||||
|
|
||||||
|
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None)
|
||||||
|
{
|
||||||
|
messages.add(id, "Invalid Info Condition: " + infoCondition.toString(), "", CSMDoc::Message::Severity_Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (!infoCondition.variantTypeIsValid())
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << "Info Condition: Value for \"" << infoCondition.toString() << "\" has a type of ";
|
||||||
|
|
||||||
|
switch (select.mValue.getType())
|
||||||
|
{
|
||||||
|
case ESM::VT_None: stream << "None"; break;
|
||||||
|
case ESM::VT_Short: stream << "Short"; break;
|
||||||
|
case ESM::VT_Int: stream << "Int"; break;
|
||||||
|
case ESM::VT_Long: stream << "Long"; break;
|
||||||
|
case ESM::VT_Float: stream << "Float"; break;
|
||||||
|
case ESM::VT_String: stream << "String"; break;
|
||||||
|
default: stream << "Unknown"; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (infoCondition.conditionIsAlwaysTrue())
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << "Info Condition: " << infoCondition.toString() << " is always true";
|
||||||
|
|
||||||
|
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (infoCondition.conditionIsNeverTrue())
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << "Info Condition: " << infoCondition.toString() << " is never true";
|
||||||
|
|
||||||
|
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Id checks
|
||||||
|
if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Global &&
|
||||||
|
!verifyId(infoCondition.getVariableName(), mGlobals, id, messages))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Journal &&
|
||||||
|
!verifyId(infoCondition.getVariableName(), mJournals, id, messages))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Item &&
|
||||||
|
!verifyItem(infoCondition.getVariableName(), id, messages))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Dead &&
|
||||||
|
!verifyActor(infoCondition.getVariableName(), id, messages))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotId &&
|
||||||
|
!verifyActor(infoCondition.getVariableName(), id, messages))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotFaction &&
|
||||||
|
!verifyId(infoCondition.getVariableName(), mFactions, id, messages))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotClass &&
|
||||||
|
!verifyId(infoCondition.getVariableName(), mClasses, id, messages))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotRace &&
|
||||||
|
!verifyId(infoCondition.getVariableName(), mRaces, id, messages))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotCell &&
|
||||||
|
!verifyCell(infoCondition.getVariableName(), id, messages))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id,
|
||||||
|
CSMDoc::Messages& messages)
|
||||||
|
{
|
||||||
|
const std::string specifier = "Sound File";
|
||||||
|
|
||||||
|
if (mSoundFiles.searchId(sound) == -1)
|
||||||
|
{
|
||||||
|
writeMissingIdError(specifier, sound, id, messages);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,
|
||||||
|
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||||
|
{
|
||||||
|
int index = collection.searchId(name);
|
||||||
|
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
writeMissingIdError(T::getRecordType(), name, id, messages);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (collection.getRecord(index).isDeleted())
|
||||||
|
{
|
||||||
|
writeDeletedRecordError(T::getRecordType(), name, id, messages);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error functions
|
||||||
|
|
||||||
|
void CSMTools::TopicInfoCheckStage::writeMissingIdError(const std::string& specifier, const std::string& missingId,
|
||||||
|
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << specifier << ": ID or name \"" << missingId << "\" could not be found";
|
||||||
|
|
||||||
|
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMTools::TopicInfoCheckStage::writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
|
||||||
|
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages)
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << specifier << ": Deleted record with ID \"" << recordId << "\" is being referenced";
|
||||||
|
|
||||||
|
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMTools::TopicInfoCheckStage::writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
|
||||||
|
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, const CSMWorld::UniversalId& id,
|
||||||
|
CSMDoc::Messages& messages)
|
||||||
|
{
|
||||||
|
CSMWorld::UniversalId tempId(invalidType, invalidId);
|
||||||
|
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << specifier << ": invalid type of " << tempId.getTypeName() << " was found for referencable \""
|
||||||
|
<< invalidId << "\" (can be of type " << expectedType << ")";
|
||||||
|
|
||||||
|
messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error);
|
||||||
|
}
|
95
apps/opencs/model/tools/topicinfocheck.hpp
Normal file
95
apps/opencs/model/tools/topicinfocheck.hpp
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#ifndef CSM_TOOLS_TOPICINFOCHECK_HPP
|
||||||
|
#define CSM_TOOLS_TOPICINFOCHECK_HPP
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <components/esm/loadclas.hpp>
|
||||||
|
#include <components/esm/loaddial.hpp>
|
||||||
|
#include <components/esm/loadfact.hpp>
|
||||||
|
#include <components/esm/loadglob.hpp>
|
||||||
|
#include <components/esm/loadgmst.hpp>
|
||||||
|
#include <components/esm/loadrace.hpp>
|
||||||
|
#include <components/esm/loadregn.hpp>
|
||||||
|
|
||||||
|
#include "../world/cell.hpp"
|
||||||
|
#include "../world/idcollection.hpp"
|
||||||
|
#include "../world/infocollection.hpp"
|
||||||
|
#include "../world/refiddata.hpp"
|
||||||
|
#include "../world/resources.hpp"
|
||||||
|
|
||||||
|
#include "../doc/stage.hpp"
|
||||||
|
|
||||||
|
namespace CSMTools
|
||||||
|
{
|
||||||
|
/// \brief VerifyStage: check topics
|
||||||
|
class TopicInfoCheckStage : public CSMDoc::Stage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
TopicInfoCheckStage(
|
||||||
|
const CSMWorld::InfoCollection& topicInfos,
|
||||||
|
const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
|
||||||
|
const CSMWorld::IdCollection<ESM::Class>& classes,
|
||||||
|
const CSMWorld::IdCollection<ESM::Faction>& factions,
|
||||||
|
const CSMWorld::IdCollection<ESM::GameSetting>& gmsts,
|
||||||
|
const CSMWorld::IdCollection<ESM::Global>& globals,
|
||||||
|
const CSMWorld::IdCollection<ESM::Dialogue>& journals,
|
||||||
|
const CSMWorld::IdCollection<ESM::Race>& races,
|
||||||
|
const CSMWorld::IdCollection<ESM::Region>& regions,
|
||||||
|
const CSMWorld::IdCollection<ESM::Dialogue>& topics,
|
||||||
|
const CSMWorld::RefIdData& referencables,
|
||||||
|
const CSMWorld::Resources& soundFiles);
|
||||||
|
|
||||||
|
virtual int setup();
|
||||||
|
///< \return number of steps
|
||||||
|
|
||||||
|
virtual void perform(int step, CSMDoc::Messages& messages);
|
||||||
|
///< Messages resulting from this stage will be appended to \a messages
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const CSMWorld::InfoCollection& mTopicInfos;
|
||||||
|
|
||||||
|
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
|
||||||
|
const CSMWorld::IdCollection<ESM::Class>& mClasses;
|
||||||
|
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
|
||||||
|
const CSMWorld::IdCollection<ESM::GameSetting>& mGameSettings;
|
||||||
|
const CSMWorld::IdCollection<ESM::Global>& mGlobals;
|
||||||
|
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
|
||||||
|
const CSMWorld::IdCollection<ESM::Race>& mRaces;
|
||||||
|
const CSMWorld::IdCollection<ESM::Region>& mRegions;
|
||||||
|
const CSMWorld::IdCollection<ESM::Dialogue>& mTopics;
|
||||||
|
|
||||||
|
const CSMWorld::RefIdData& mReferencables;
|
||||||
|
const CSMWorld::Resources& mSoundFiles;
|
||||||
|
|
||||||
|
std::set<std::string> mCellNames;
|
||||||
|
|
||||||
|
// These return false when not successful and write an error
|
||||||
|
bool verifyActor(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||||
|
bool verifyCell(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||||
|
bool verifyFactionRank(const std::string& name, int rank, const CSMWorld::UniversalId& id,
|
||||||
|
CSMDoc::Messages& messages);
|
||||||
|
bool verifyItem(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||||
|
bool verifySelectStruct(const ESM::DialInfo::SelectStruct& select, const CSMWorld::UniversalId& id,
|
||||||
|
CSMDoc::Messages& messages);
|
||||||
|
bool verifySound(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,
|
||||||
|
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||||
|
|
||||||
|
// Common error messages
|
||||||
|
void writeMissingIdError(const std::string& specifier, const std::string& missingId,
|
||||||
|
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||||
|
|
||||||
|
void writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
|
||||||
|
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||||
|
|
||||||
|
void writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
|
||||||
|
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType,
|
||||||
|
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -3,6 +3,7 @@
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
#include "universalid.hpp"
|
#include "universalid.hpp"
|
||||||
|
#include "infoselectwrapper.hpp"
|
||||||
|
|
||||||
namespace CSMWorld
|
namespace CSMWorld
|
||||||
{
|
{
|
||||||
|
@ -273,8 +274,8 @@ namespace CSMWorld
|
||||||
{ ColumnId_InfoList, "Info List" },
|
{ ColumnId_InfoList, "Info List" },
|
||||||
{ ColumnId_InfoCondition, "Info Conditions" },
|
{ ColumnId_InfoCondition, "Info Conditions" },
|
||||||
{ ColumnId_InfoCondFunc, "Function" },
|
{ ColumnId_InfoCondFunc, "Function" },
|
||||||
{ ColumnId_InfoCondVar, "Func/Variable" },
|
{ ColumnId_InfoCondVar, "Variable/Object" },
|
||||||
{ ColumnId_InfoCondComp, "Comp" },
|
{ ColumnId_InfoCondComp, "Relation" },
|
||||||
{ ColumnId_InfoCondValue, "Values" },
|
{ ColumnId_InfoCondValue, "Values" },
|
||||||
{ ColumnId_OriginalCell, "Original Cell" },
|
{ ColumnId_OriginalCell, "Original Cell" },
|
||||||
|
|
||||||
|
@ -546,18 +547,6 @@ namespace
|
||||||
"AI Wander", "AI Travel", "AI Follow", "AI Escort", "AI Activate", 0
|
"AI Wander", "AI Travel", "AI Follow", "AI Escort", "AI Activate", 0
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *sInfoCondFunc[] =
|
|
||||||
{
|
|
||||||
" ", "Function", "Global", "Local", "Journal",
|
|
||||||
"Item", "Dead", "Not ID", "Not Faction", "Not Class",
|
|
||||||
"Not Race", "Not Cell", "Not Local", 0
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *sInfoCondComp[] =
|
|
||||||
{
|
|
||||||
"!=", "<", "<=", "=", ">", ">=", 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const char **getEnumNames (CSMWorld::Columns::ColumnId column)
|
const char **getEnumNames (CSMWorld::Columns::ColumnId column)
|
||||||
{
|
{
|
||||||
switch (column)
|
switch (column)
|
||||||
|
@ -585,10 +574,8 @@ namespace
|
||||||
case CSMWorld::Columns::ColumnId_EffectId: return sEffectId;
|
case CSMWorld::Columns::ColumnId_EffectId: return sEffectId;
|
||||||
case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType;
|
case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType;
|
||||||
case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType;
|
case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType;
|
||||||
case CSMWorld::Columns::ColumnId_InfoCondFunc: return sInfoCondFunc;
|
case CSMWorld::Columns::ColumnId_InfoCondFunc: return CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings;
|
||||||
// FIXME: don't have dynamic value enum delegate, use Display_String for now
|
case CSMWorld::Columns::ColumnId_InfoCondComp: return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings;
|
||||||
//case CSMWorld::Columns::ColumnId_InfoCond: return sInfoCond;
|
|
||||||
case CSMWorld::Columns::ColumnId_InfoCondComp: return sInfoCondComp;
|
|
||||||
|
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,9 +68,6 @@ void CSMWorld::ModifyCommand::undo()
|
||||||
|
|
||||||
void CSMWorld::CreateCommand::applyModifications()
|
void CSMWorld::CreateCommand::applyModifications()
|
||||||
{
|
{
|
||||||
for (std::map<int, QVariant>::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter)
|
|
||||||
mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second);
|
|
||||||
|
|
||||||
if (!mNestedValues.empty())
|
if (!mNestedValues.empty())
|
||||||
{
|
{
|
||||||
CSMWorld::IdTree *tree = dynamic_cast<CSMWorld::IdTree *>(&mModel);
|
CSMWorld::IdTree *tree = dynamic_cast<CSMWorld::IdTree *>(&mModel);
|
||||||
|
@ -114,7 +111,7 @@ void CSMWorld::CreateCommand::setType (UniversalId::Type type)
|
||||||
|
|
||||||
void CSMWorld::CreateCommand::redo()
|
void CSMWorld::CreateCommand::redo()
|
||||||
{
|
{
|
||||||
mModel.addRecord (mId, mType);
|
mModel.addRecordWithData (mId, mValues, mType);
|
||||||
applyModifications();
|
applyModifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -271,7 +271,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
|
||||||
new NestedChildColumn (Columns::ColumnId_InfoCondFunc, ColumnBase::Display_InfoCondFunc));
|
new NestedChildColumn (Columns::ColumnId_InfoCondFunc, ColumnBase::Display_InfoCondFunc));
|
||||||
// FIXME: don't have dynamic value enum delegate, use Display_String for now
|
// FIXME: don't have dynamic value enum delegate, use Display_String for now
|
||||||
mTopicInfos.getNestableColumn(index)->addColumn(
|
mTopicInfos.getNestableColumn(index)->addColumn(
|
||||||
new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_String));
|
new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_InfoCondVar));
|
||||||
mTopicInfos.getNestableColumn(index)->addColumn(
|
mTopicInfos.getNestableColumn(index)->addColumn(
|
||||||
new NestedChildColumn (Columns::ColumnId_InfoCondComp, ColumnBase::Display_InfoCondComp));
|
new NestedChildColumn (Columns::ColumnId_InfoCondComp, ColumnBase::Display_InfoCondComp));
|
||||||
mTopicInfos.getNestableColumn(index)->addColumn(
|
mTopicInfos.getNestableColumn(index)->addColumn(
|
||||||
|
|
|
@ -60,6 +60,10 @@ std::vector<CSMWorld::ColumnBase::Display> CSMWorld::IdCompletionManager::getDis
|
||||||
{
|
{
|
||||||
types.push_back(current->first);
|
types.push_back(current->first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hack for Display_InfoCondVar
|
||||||
|
types.push_back(CSMWorld::ColumnBase::Display_InfoCondVar);
|
||||||
|
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +108,7 @@ void CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data &data)
|
||||||
QAbstractItemView *popup = new CSVWidget::CompleterPopup();
|
QAbstractItemView *popup = new CSVWidget::CompleterPopup();
|
||||||
completer->setPopup(popup); // The completer takes ownership of the popup
|
completer->setPopup(popup); // The completer takes ownership of the popup
|
||||||
completer->setMaxVisibleItems(10);
|
completer->setMaxVisibleItems(10);
|
||||||
|
|
||||||
mCompleters[current->first] = completer;
|
mCompleters[current->first] = completer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,6 +149,21 @@ void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSMWorld::IdTable::addRecordWithData (const std::string& id,
|
||||||
|
const std::map<int, QVariant>& data, UniversalId::Type type)
|
||||||
|
{
|
||||||
|
int index = mIdCollection->getAppendIndex (id, type);
|
||||||
|
|
||||||
|
beginInsertRows (QModelIndex(), index, index);
|
||||||
|
|
||||||
|
mIdCollection->appendBlankRecord (id, type);
|
||||||
|
|
||||||
|
for (std::map<int, QVariant>::const_iterator iter (data.begin()); iter!=data.end(); ++iter)
|
||||||
|
mIdCollection->setData (index, iter->first, iter->second);
|
||||||
|
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
void CSMWorld::IdTable::cloneRecord(const std::string& origin,
|
void CSMWorld::IdTable::cloneRecord(const std::string& origin,
|
||||||
const std::string& destination,
|
const std::string& destination,
|
||||||
CSMWorld::UniversalId::Type type)
|
CSMWorld::UniversalId::Type type)
|
||||||
|
|
|
@ -53,6 +53,10 @@ namespace CSMWorld
|
||||||
void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None);
|
void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None);
|
||||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||||
|
|
||||||
|
void addRecordWithData (const std::string& id, const std::map<int, QVariant>& data,
|
||||||
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
|
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||||
|
|
||||||
void cloneRecord(const std::string& origin,
|
void cloneRecord(const std::string& origin,
|
||||||
const std::string& destination,
|
const std::string& destination,
|
||||||
UniversalId::Type type = UniversalId::Type_None);
|
UniversalId::Type type = UniversalId::Type_None);
|
||||||
|
|
893
apps/opencs/model/world/infoselectwrapper.cpp
Normal file
893
apps/opencs/model/world/infoselectwrapper.cpp
Normal file
|
@ -0,0 +1,893 @@
|
||||||
|
#include "infoselectwrapper.hpp"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
const size_t CSMWorld::ConstInfoSelectWrapper::RuleMinSize = 5;
|
||||||
|
|
||||||
|
const size_t CSMWorld::ConstInfoSelectWrapper::FunctionPrefixOffset = 1;
|
||||||
|
const size_t CSMWorld::ConstInfoSelectWrapper::FunctionIndexOffset = 2;
|
||||||
|
const size_t CSMWorld::ConstInfoSelectWrapper::RelationIndexOffset = 4;
|
||||||
|
const size_t CSMWorld::ConstInfoSelectWrapper::VarNameOffset = 5;
|
||||||
|
|
||||||
|
const char* CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings[] =
|
||||||
|
{
|
||||||
|
"Rank Low",
|
||||||
|
"Rank High",
|
||||||
|
"Rank Requirement",
|
||||||
|
"Reputation",
|
||||||
|
"Health Percent",
|
||||||
|
"PC Reputation",
|
||||||
|
"PC Level",
|
||||||
|
"PC Health Percent",
|
||||||
|
"PC Magicka",
|
||||||
|
"PC Fatigue",
|
||||||
|
"PC Strength",
|
||||||
|
"PC Block",
|
||||||
|
"PC Armorer",
|
||||||
|
"PC Medium Armor",
|
||||||
|
"PC Heavy Armor",
|
||||||
|
"PC Blunt Weapon",
|
||||||
|
"PC Long Blade",
|
||||||
|
"PC Axe",
|
||||||
|
"PC Spear",
|
||||||
|
"PC Athletics",
|
||||||
|
"PC Enchant",
|
||||||
|
"PC Detruction",
|
||||||
|
"PC Alteration",
|
||||||
|
"PC Illusion",
|
||||||
|
"PC Conjuration",
|
||||||
|
"PC Mysticism",
|
||||||
|
"PC Restoration",
|
||||||
|
"PC Alchemy",
|
||||||
|
"PC Unarmored",
|
||||||
|
"PC Security",
|
||||||
|
"PC Sneak",
|
||||||
|
"PC Acrobatics",
|
||||||
|
"PC Light Armor",
|
||||||
|
"PC Short Blade",
|
||||||
|
"PC Marksman",
|
||||||
|
"PC Merchantile",
|
||||||
|
"PC Speechcraft",
|
||||||
|
"PC Hand to Hand",
|
||||||
|
"PC Sex",
|
||||||
|
"PC Expelled",
|
||||||
|
"PC Common Disease",
|
||||||
|
"PC Blight Disease",
|
||||||
|
"PC Clothing Modifier",
|
||||||
|
"PC Crime Level",
|
||||||
|
"Same Sex",
|
||||||
|
"Same Race",
|
||||||
|
"Same Faction",
|
||||||
|
"Faction Rank Difference",
|
||||||
|
"Detected",
|
||||||
|
"Alarmed",
|
||||||
|
"Choice",
|
||||||
|
"PC Intelligence",
|
||||||
|
"PC Willpower",
|
||||||
|
"PC Agility",
|
||||||
|
"PC Speed",
|
||||||
|
"PC Endurance",
|
||||||
|
"PC Personality",
|
||||||
|
"PC Luck",
|
||||||
|
"PC Corpus",
|
||||||
|
"Weather",
|
||||||
|
"PC Vampire",
|
||||||
|
"Level",
|
||||||
|
"Attacked",
|
||||||
|
"Talked to PC",
|
||||||
|
"PC Health",
|
||||||
|
"Creature Target",
|
||||||
|
"Friend Hit",
|
||||||
|
"Fight",
|
||||||
|
"Hello",
|
||||||
|
"Alarm",
|
||||||
|
"Flee",
|
||||||
|
"Should Attack",
|
||||||
|
"Werewolf",
|
||||||
|
"PC Werewolf Kills",
|
||||||
|
"Global",
|
||||||
|
"Local",
|
||||||
|
"Journal",
|
||||||
|
"Item",
|
||||||
|
"Dead",
|
||||||
|
"Not Id",
|
||||||
|
"Not Faction",
|
||||||
|
"Not Class",
|
||||||
|
"Not Race",
|
||||||
|
"Not Cell",
|
||||||
|
"Not Local",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings[] =
|
||||||
|
{
|
||||||
|
"=",
|
||||||
|
"!=",
|
||||||
|
">",
|
||||||
|
">=",
|
||||||
|
"<",
|
||||||
|
"<=",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* CSMWorld::ConstInfoSelectWrapper::ComparisonEnumStrings[] =
|
||||||
|
{
|
||||||
|
"Boolean",
|
||||||
|
"Integer",
|
||||||
|
"Numeric",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
// static functions
|
||||||
|
|
||||||
|
std::string CSMWorld::ConstInfoSelectWrapper::convertToString(FunctionName name)
|
||||||
|
{
|
||||||
|
if (name < Function_None)
|
||||||
|
return FunctionEnumStrings[name];
|
||||||
|
else
|
||||||
|
return "(Invalid Data: Function)";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CSMWorld::ConstInfoSelectWrapper::convertToString(RelationType type)
|
||||||
|
{
|
||||||
|
if (type < Relation_None)
|
||||||
|
return RelationEnumStrings[type];
|
||||||
|
else
|
||||||
|
return "(Invalid Data: Relation)";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CSMWorld::ConstInfoSelectWrapper::convertToString(ComparisonType type)
|
||||||
|
{
|
||||||
|
if (type < Comparison_None)
|
||||||
|
return ComparisonEnumStrings[type];
|
||||||
|
else
|
||||||
|
return "(Invalid Data: Comparison)";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConstInfoSelectWrapper
|
||||||
|
|
||||||
|
CSMWorld::ConstInfoSelectWrapper::ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select)
|
||||||
|
: mConstSelect(select)
|
||||||
|
{
|
||||||
|
readRule();
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::ConstInfoSelectWrapper::FunctionName CSMWorld::ConstInfoSelectWrapper::getFunctionName() const
|
||||||
|
{
|
||||||
|
return mFunctionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::ConstInfoSelectWrapper::RelationType CSMWorld::ConstInfoSelectWrapper::getRelationType() const
|
||||||
|
{
|
||||||
|
return mRelationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::ConstInfoSelectWrapper::ComparisonType CSMWorld::ConstInfoSelectWrapper::getComparisonType() const
|
||||||
|
{
|
||||||
|
return mComparisonType;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::ConstInfoSelectWrapper::hasVariable() const
|
||||||
|
{
|
||||||
|
return mHasVariable;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& CSMWorld::ConstInfoSelectWrapper::getVariableName() const
|
||||||
|
{
|
||||||
|
return mVariableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue() const
|
||||||
|
{
|
||||||
|
if (!variantTypeIsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer)
|
||||||
|
{
|
||||||
|
if (mConstSelect.mValue.getType() == ESM::VT_Float)
|
||||||
|
return conditionIsAlwaysTrue(getConditionFloatRange(), getValidIntRange());
|
||||||
|
else
|
||||||
|
return conditionIsAlwaysTrue(getConditionIntRange(), getValidIntRange());
|
||||||
|
}
|
||||||
|
else if (mComparisonType == Comparison_Numeric)
|
||||||
|
{
|
||||||
|
if (mConstSelect.mValue.getType() == ESM::VT_Float)
|
||||||
|
return conditionIsAlwaysTrue(getConditionFloatRange(), getValidFloatRange());
|
||||||
|
else
|
||||||
|
return conditionIsAlwaysTrue(getConditionIntRange(), getValidFloatRange());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue() const
|
||||||
|
{
|
||||||
|
if (!variantTypeIsValid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer)
|
||||||
|
{
|
||||||
|
if (mConstSelect.mValue.getType() == ESM::VT_Float)
|
||||||
|
return conditionIsNeverTrue(getConditionFloatRange(), getValidIntRange());
|
||||||
|
else
|
||||||
|
return conditionIsNeverTrue(getConditionIntRange(), getValidIntRange());
|
||||||
|
}
|
||||||
|
else if (mComparisonType == Comparison_Numeric)
|
||||||
|
{
|
||||||
|
if (mConstSelect.mValue.getType() == ESM::VT_Float)
|
||||||
|
return conditionIsNeverTrue(getConditionFloatRange(), getValidFloatRange());
|
||||||
|
else
|
||||||
|
return conditionIsNeverTrue(getConditionIntRange(), getValidFloatRange());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::ConstInfoSelectWrapper::variantTypeIsValid() const
|
||||||
|
{
|
||||||
|
return (mConstSelect.mValue.getType() == ESM::VT_Int || mConstSelect.mValue.getType() == ESM::VT_Float);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESM::Variant& CSMWorld::ConstInfoSelectWrapper::getVariant() const
|
||||||
|
{
|
||||||
|
return mConstSelect.mValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CSMWorld::ConstInfoSelectWrapper::toString() const
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << convertToString(mFunctionName) << " ";
|
||||||
|
|
||||||
|
if (mHasVariable)
|
||||||
|
stream << mVariableName << " ";
|
||||||
|
|
||||||
|
stream << convertToString(mRelationType) << " ";
|
||||||
|
|
||||||
|
switch (mConstSelect.mValue.getType())
|
||||||
|
{
|
||||||
|
case ESM::VT_Int:
|
||||||
|
stream << mConstSelect.mValue.getInteger();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESM::VT_Float:
|
||||||
|
stream << mConstSelect.mValue.getFloat();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
stream << "(Invalid value type)";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::ConstInfoSelectWrapper::readRule()
|
||||||
|
{
|
||||||
|
if (mConstSelect.mSelectRule.size() < RuleMinSize)
|
||||||
|
throw std::runtime_error("InfoSelectWrapper: rule is to small");
|
||||||
|
|
||||||
|
readFunctionName();
|
||||||
|
readRelationType();
|
||||||
|
readVariableName();
|
||||||
|
updateHasVariable();
|
||||||
|
updateComparisonType();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::ConstInfoSelectWrapper::readFunctionName()
|
||||||
|
{
|
||||||
|
char functionPrefix = mConstSelect.mSelectRule[FunctionPrefixOffset];
|
||||||
|
std::string functionIndex = mConstSelect.mSelectRule.substr(FunctionIndexOffset, 2);
|
||||||
|
int convertedIndex = -1;
|
||||||
|
|
||||||
|
// Read in function index, form ## from 00 .. 73, skip leading zero
|
||||||
|
if (functionIndex[0] == '0')
|
||||||
|
functionIndex = functionIndex[1];
|
||||||
|
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << functionIndex;
|
||||||
|
stream >> convertedIndex;
|
||||||
|
|
||||||
|
switch (functionPrefix)
|
||||||
|
{
|
||||||
|
case '1':
|
||||||
|
if (convertedIndex >= 0 && convertedIndex <= 73)
|
||||||
|
mFunctionName = static_cast<FunctionName>(convertedIndex);
|
||||||
|
else
|
||||||
|
mFunctionName = Function_None;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '2': mFunctionName = Function_Global; break;
|
||||||
|
case '3': mFunctionName = Function_Local; break;
|
||||||
|
case '4': mFunctionName = Function_Journal; break;
|
||||||
|
case '5': mFunctionName = Function_Item; break;
|
||||||
|
case '6': mFunctionName = Function_Dead; break;
|
||||||
|
case '7': mFunctionName = Function_NotId; break;
|
||||||
|
case '8': mFunctionName = Function_NotFaction; break;
|
||||||
|
case '9': mFunctionName = Function_NotClass; break;
|
||||||
|
case 'A': mFunctionName = Function_NotRace; break;
|
||||||
|
case 'B': mFunctionName = Function_NotCell; break;
|
||||||
|
case 'C': mFunctionName = Function_NotLocal; break;
|
||||||
|
default: mFunctionName = Function_None; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::ConstInfoSelectWrapper::readRelationType()
|
||||||
|
{
|
||||||
|
char relationIndex = mConstSelect.mSelectRule[RelationIndexOffset];
|
||||||
|
|
||||||
|
switch (relationIndex)
|
||||||
|
{
|
||||||
|
case '0': mRelationType = Relation_Equal; break;
|
||||||
|
case '1': mRelationType = Relation_NotEqual; break;
|
||||||
|
case '2': mRelationType = Relation_Greater; break;
|
||||||
|
case '3': mRelationType = Relation_GreaterOrEqual; break;
|
||||||
|
case '4': mRelationType = Relation_Less; break;
|
||||||
|
case '5': mRelationType = Relation_LessOrEqual; break;
|
||||||
|
default: mRelationType = Relation_None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::ConstInfoSelectWrapper::readVariableName()
|
||||||
|
{
|
||||||
|
if (mConstSelect.mSelectRule.size() >= VarNameOffset)
|
||||||
|
mVariableName = mConstSelect.mSelectRule.substr(VarNameOffset);
|
||||||
|
else
|
||||||
|
mVariableName.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::ConstInfoSelectWrapper::updateHasVariable()
|
||||||
|
{
|
||||||
|
switch (mFunctionName)
|
||||||
|
{
|
||||||
|
case Function_Global:
|
||||||
|
case Function_Local:
|
||||||
|
case Function_Journal:
|
||||||
|
case Function_Item:
|
||||||
|
case Function_Dead:
|
||||||
|
case Function_NotId:
|
||||||
|
case Function_NotFaction:
|
||||||
|
case Function_NotClass:
|
||||||
|
case Function_NotRace:
|
||||||
|
case Function_NotCell:
|
||||||
|
case Function_NotLocal:
|
||||||
|
mHasVariable = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
mHasVariable = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::ConstInfoSelectWrapper::updateComparisonType()
|
||||||
|
{
|
||||||
|
switch (mFunctionName)
|
||||||
|
{
|
||||||
|
// Boolean
|
||||||
|
case Function_NotId:
|
||||||
|
case Function_NotFaction:
|
||||||
|
case Function_NotClass:
|
||||||
|
case Function_NotRace:
|
||||||
|
case Function_NotCell:
|
||||||
|
case Function_NotLocal:
|
||||||
|
case Function_PcExpelled:
|
||||||
|
case Function_PcCommonDisease:
|
||||||
|
case Function_PcBlightDisease:
|
||||||
|
case Function_SameSex:
|
||||||
|
case Function_SameRace:
|
||||||
|
case Function_SameFaction:
|
||||||
|
case Function_Detected:
|
||||||
|
case Function_Alarmed:
|
||||||
|
case Function_PcCorpus:
|
||||||
|
case Function_PcVampire:
|
||||||
|
case Function_Attacked:
|
||||||
|
case Function_TalkedToPc:
|
||||||
|
case Function_ShouldAttack:
|
||||||
|
case Function_Werewolf:
|
||||||
|
mComparisonType = Comparison_Boolean;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Integer
|
||||||
|
case Function_Journal:
|
||||||
|
case Function_Item:
|
||||||
|
case Function_Dead:
|
||||||
|
case Function_RankLow:
|
||||||
|
case Function_RankHigh:
|
||||||
|
case Function_RankRequirement:
|
||||||
|
case Function_Reputation:
|
||||||
|
case Function_PcReputation:
|
||||||
|
case Function_PcLevel:
|
||||||
|
case Function_PcStrength:
|
||||||
|
case Function_PcBlock:
|
||||||
|
case Function_PcArmorer:
|
||||||
|
case Function_PcMediumArmor:
|
||||||
|
case Function_PcHeavyArmor:
|
||||||
|
case Function_PcBluntWeapon:
|
||||||
|
case Function_PcLongBlade:
|
||||||
|
case Function_PcAxe:
|
||||||
|
case Function_PcSpear:
|
||||||
|
case Function_PcAthletics:
|
||||||
|
case Function_PcEnchant:
|
||||||
|
case Function_PcDestruction:
|
||||||
|
case Function_PcAlteration:
|
||||||
|
case Function_PcIllusion:
|
||||||
|
case Function_PcConjuration:
|
||||||
|
case Function_PcMysticism:
|
||||||
|
case Function_PcRestoration:
|
||||||
|
case Function_PcAlchemy:
|
||||||
|
case Function_PcUnarmored:
|
||||||
|
case Function_PcSecurity:
|
||||||
|
case Function_PcSneak:
|
||||||
|
case Function_PcAcrobatics:
|
||||||
|
case Function_PcLightArmor:
|
||||||
|
case Function_PcShortBlade:
|
||||||
|
case Function_PcMarksman:
|
||||||
|
case Function_PcMerchantile:
|
||||||
|
case Function_PcSpeechcraft:
|
||||||
|
case Function_PcHandToHand:
|
||||||
|
case Function_PcGender:
|
||||||
|
case Function_PcClothingModifier:
|
||||||
|
case Function_PcCrimeLevel:
|
||||||
|
case Function_FactionRankDifference:
|
||||||
|
case Function_Choice:
|
||||||
|
case Function_PcIntelligence:
|
||||||
|
case Function_PcWillpower:
|
||||||
|
case Function_PcAgility:
|
||||||
|
case Function_PcSpeed:
|
||||||
|
case Function_PcEndurance:
|
||||||
|
case Function_PcPersonality:
|
||||||
|
case Function_PcLuck:
|
||||||
|
case Function_Weather:
|
||||||
|
case Function_Level:
|
||||||
|
case Function_CreatureTarget:
|
||||||
|
case Function_FriendHit:
|
||||||
|
case Function_Fight:
|
||||||
|
case Function_Hello:
|
||||||
|
case Function_Alarm:
|
||||||
|
case Function_Flee:
|
||||||
|
case Function_PcWerewolfKills:
|
||||||
|
mComparisonType = Comparison_Integer;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Numeric
|
||||||
|
case Function_Global:
|
||||||
|
case Function_Local:
|
||||||
|
|
||||||
|
case Function_Health_Percent:
|
||||||
|
case Function_PcHealthPercent:
|
||||||
|
case Function_PcMagicka:
|
||||||
|
case Function_PcFatigue:
|
||||||
|
case Function_PcHealth:
|
||||||
|
mComparisonType = Comparison_Numeric;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
mComparisonType = Comparison_None;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int, int> CSMWorld::ConstInfoSelectWrapper::getConditionIntRange() const
|
||||||
|
{
|
||||||
|
const int IntMax = std::numeric_limits<int>::max();
|
||||||
|
const int IntMin = std::numeric_limits<int>::min();
|
||||||
|
const std::pair<int, int> InvalidRange(IntMax, IntMin);
|
||||||
|
|
||||||
|
int value = mConstSelect.mValue.getInteger();
|
||||||
|
|
||||||
|
switch (mRelationType)
|
||||||
|
{
|
||||||
|
case Relation_Equal:
|
||||||
|
case Relation_NotEqual:
|
||||||
|
return std::pair<int, int>(value, value);
|
||||||
|
|
||||||
|
case Relation_Greater:
|
||||||
|
if (value == IntMax)
|
||||||
|
{
|
||||||
|
return InvalidRange;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::pair<int, int>(value + 1, IntMax);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Relation_GreaterOrEqual:
|
||||||
|
return std::pair<int, int>(value, IntMax);
|
||||||
|
|
||||||
|
case Relation_Less:
|
||||||
|
if (value == IntMin)
|
||||||
|
{
|
||||||
|
return InvalidRange;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::pair<int, int>(IntMin, value - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Relation_LessOrEqual:
|
||||||
|
return std::pair<int, int>(IntMin, value);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::logic_error("InfoSelectWrapper: relation does not have a range");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<float, float> CSMWorld::ConstInfoSelectWrapper::getConditionFloatRange() const
|
||||||
|
{
|
||||||
|
const float FloatMax = std::numeric_limits<float>::infinity();
|
||||||
|
const float FloatMin = -std::numeric_limits<float>::infinity();
|
||||||
|
const float Epsilon = std::numeric_limits<float>::epsilon();
|
||||||
|
const std::pair<float, float> InvalidRange(FloatMax, FloatMin);
|
||||||
|
|
||||||
|
float value = mConstSelect.mValue.getFloat();
|
||||||
|
|
||||||
|
switch (mRelationType)
|
||||||
|
{
|
||||||
|
case Relation_Equal:
|
||||||
|
case Relation_NotEqual:
|
||||||
|
return std::pair<float, float>(value, value);
|
||||||
|
|
||||||
|
case Relation_Greater:
|
||||||
|
return std::pair<float, float>(value + Epsilon, FloatMax);
|
||||||
|
|
||||||
|
case Relation_GreaterOrEqual:
|
||||||
|
return std::pair<float, float>(value, FloatMax);
|
||||||
|
|
||||||
|
case Relation_Less:
|
||||||
|
return std::pair<float, float>(FloatMin, value - Epsilon);
|
||||||
|
|
||||||
|
case Relation_LessOrEqual:
|
||||||
|
return std::pair<float, float>(FloatMin, value);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::logic_error("InfoSelectWrapper: given relation does not have a range");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int, int> CSMWorld::ConstInfoSelectWrapper::getValidIntRange() const
|
||||||
|
{
|
||||||
|
const int IntMax = std::numeric_limits<int>::max();
|
||||||
|
const int IntMin = std::numeric_limits<int>::min();
|
||||||
|
|
||||||
|
switch (mFunctionName)
|
||||||
|
{
|
||||||
|
// Boolean
|
||||||
|
case Function_NotId:
|
||||||
|
case Function_NotFaction:
|
||||||
|
case Function_NotClass:
|
||||||
|
case Function_NotRace:
|
||||||
|
case Function_NotCell:
|
||||||
|
case Function_NotLocal:
|
||||||
|
case Function_PcExpelled:
|
||||||
|
case Function_PcCommonDisease:
|
||||||
|
case Function_PcBlightDisease:
|
||||||
|
case Function_SameSex:
|
||||||
|
case Function_SameRace:
|
||||||
|
case Function_SameFaction:
|
||||||
|
case Function_Detected:
|
||||||
|
case Function_Alarmed:
|
||||||
|
case Function_PcCorpus:
|
||||||
|
case Function_PcVampire:
|
||||||
|
case Function_Attacked:
|
||||||
|
case Function_TalkedToPc:
|
||||||
|
case Function_ShouldAttack:
|
||||||
|
case Function_Werewolf:
|
||||||
|
return std::pair<int, int>(0, 1);
|
||||||
|
|
||||||
|
// Integer
|
||||||
|
case Function_RankLow:
|
||||||
|
case Function_RankHigh:
|
||||||
|
case Function_Reputation:
|
||||||
|
case Function_PcReputation:
|
||||||
|
case Function_Journal:
|
||||||
|
return std::pair<int, int>(IntMin, IntMax);
|
||||||
|
|
||||||
|
case Function_Item:
|
||||||
|
case Function_Dead:
|
||||||
|
case Function_PcLevel:
|
||||||
|
case Function_PcStrength:
|
||||||
|
case Function_PcBlock:
|
||||||
|
case Function_PcArmorer:
|
||||||
|
case Function_PcMediumArmor:
|
||||||
|
case Function_PcHeavyArmor:
|
||||||
|
case Function_PcBluntWeapon:
|
||||||
|
case Function_PcLongBlade:
|
||||||
|
case Function_PcAxe:
|
||||||
|
case Function_PcSpear:
|
||||||
|
case Function_PcAthletics:
|
||||||
|
case Function_PcEnchant:
|
||||||
|
case Function_PcDestruction:
|
||||||
|
case Function_PcAlteration:
|
||||||
|
case Function_PcIllusion:
|
||||||
|
case Function_PcConjuration:
|
||||||
|
case Function_PcMysticism:
|
||||||
|
case Function_PcRestoration:
|
||||||
|
case Function_PcAlchemy:
|
||||||
|
case Function_PcUnarmored:
|
||||||
|
case Function_PcSecurity:
|
||||||
|
case Function_PcSneak:
|
||||||
|
case Function_PcAcrobatics:
|
||||||
|
case Function_PcLightArmor:
|
||||||
|
case Function_PcShortBlade:
|
||||||
|
case Function_PcMarksman:
|
||||||
|
case Function_PcMerchantile:
|
||||||
|
case Function_PcSpeechcraft:
|
||||||
|
case Function_PcHandToHand:
|
||||||
|
case Function_PcClothingModifier:
|
||||||
|
case Function_PcCrimeLevel:
|
||||||
|
case Function_Choice:
|
||||||
|
case Function_PcIntelligence:
|
||||||
|
case Function_PcWillpower:
|
||||||
|
case Function_PcAgility:
|
||||||
|
case Function_PcSpeed:
|
||||||
|
case Function_PcEndurance:
|
||||||
|
case Function_PcPersonality:
|
||||||
|
case Function_PcLuck:
|
||||||
|
case Function_Level:
|
||||||
|
case Function_PcWerewolfKills:
|
||||||
|
return std::pair<int, int>(0, IntMax);
|
||||||
|
|
||||||
|
case Function_Fight:
|
||||||
|
case Function_Hello:
|
||||||
|
case Function_Alarm:
|
||||||
|
case Function_Flee:
|
||||||
|
return std::pair<int, int>(0, 100);
|
||||||
|
|
||||||
|
case Function_Weather:
|
||||||
|
return std::pair<int, int>(0, 9);
|
||||||
|
|
||||||
|
case Function_FriendHit:
|
||||||
|
return std::pair<int, int>(0, 4);
|
||||||
|
|
||||||
|
case Function_RankRequirement:
|
||||||
|
return std::pair<int, int>(0, 3);
|
||||||
|
|
||||||
|
case Function_CreatureTarget:
|
||||||
|
return std::pair<int, int>(0, 2);
|
||||||
|
|
||||||
|
case Function_PcGender:
|
||||||
|
return std::pair<int, int>(0, 1);
|
||||||
|
|
||||||
|
case Function_FactionRankDifference:
|
||||||
|
return std::pair<int, int>(-9, 9);
|
||||||
|
|
||||||
|
// Numeric
|
||||||
|
case Function_Global:
|
||||||
|
case Function_Local:
|
||||||
|
return std::pair<int, int>(IntMin, IntMax);
|
||||||
|
|
||||||
|
case Function_PcMagicka:
|
||||||
|
case Function_PcFatigue:
|
||||||
|
case Function_PcHealth:
|
||||||
|
return std::pair<int, int>(0, IntMax);
|
||||||
|
|
||||||
|
case Function_Health_Percent:
|
||||||
|
case Function_PcHealthPercent:
|
||||||
|
return std::pair<int, int>(0, 100);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("InfoSelectWrapper: function does not exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<float, float> CSMWorld::ConstInfoSelectWrapper::getValidFloatRange() const
|
||||||
|
{
|
||||||
|
const float FloatMax = std::numeric_limits<float>::infinity();
|
||||||
|
const float FloatMin = -std::numeric_limits<float>::infinity();
|
||||||
|
|
||||||
|
switch (mFunctionName)
|
||||||
|
{
|
||||||
|
// Numeric
|
||||||
|
case Function_Global:
|
||||||
|
case Function_Local:
|
||||||
|
case Function_NotLocal:
|
||||||
|
return std::pair<float, float>(FloatMin, FloatMax);
|
||||||
|
|
||||||
|
case Function_PcMagicka:
|
||||||
|
case Function_PcFatigue:
|
||||||
|
case Function_PcHealth:
|
||||||
|
return std::pair<float, float>(0, FloatMax);
|
||||||
|
|
||||||
|
case Function_Health_Percent:
|
||||||
|
case Function_PcHealthPercent:
|
||||||
|
return std::pair<float, float>(0, 100);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("InfoSelectWrapper: function does not exist or is not numeric");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
bool CSMWorld::ConstInfoSelectWrapper::rangeContains(T1 value, std::pair<T2,T2> range) const
|
||||||
|
{
|
||||||
|
return (value >= range.first && value <= range.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
bool CSMWorld::ConstInfoSelectWrapper::rangeFullyContains(std::pair<T1,T1> containingRange,
|
||||||
|
std::pair<T2,T2> testRange) const
|
||||||
|
{
|
||||||
|
return (containingRange.first <= testRange.first) && (testRange.second <= containingRange.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
bool CSMWorld::ConstInfoSelectWrapper::rangesOverlap(std::pair<T1,T1> range1, std::pair<T2,T2> range2) const
|
||||||
|
{
|
||||||
|
// One of the bounds of either range should fall within the other range
|
||||||
|
return
|
||||||
|
(range1.first <= range2.first && range2.first <= range1.second) ||
|
||||||
|
(range1.first <= range2.second && range2.second <= range1.second) ||
|
||||||
|
(range2.first <= range1.first && range1.first <= range2.second) ||
|
||||||
|
(range2.first <= range1.second && range1.second <= range2.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
bool CSMWorld::ConstInfoSelectWrapper::rangesMatch(std::pair<T1,T1> range1, std::pair<T2,T2> range2) const
|
||||||
|
{
|
||||||
|
return (range1.first == range2.first && range1.second == range2.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue(std::pair<T1,T1> conditionRange,
|
||||||
|
std::pair<T2,T2> validRange) const
|
||||||
|
{
|
||||||
|
switch (mRelationType)
|
||||||
|
{
|
||||||
|
case Relation_Equal:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case Relation_NotEqual:
|
||||||
|
// If value is not within range, it will always be true
|
||||||
|
return !rangeContains(conditionRange.first, validRange);
|
||||||
|
|
||||||
|
case Relation_Greater:
|
||||||
|
case Relation_GreaterOrEqual:
|
||||||
|
case Relation_Less:
|
||||||
|
case Relation_LessOrEqual:
|
||||||
|
// If the valid range is completely within the condition range, it will always be true
|
||||||
|
return rangeFullyContains(conditionRange, validRange);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::logic_error("InfoCondition: operator can not be used to compare");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue(std::pair<T1,T1> conditionRange,
|
||||||
|
std::pair<T2,T2> validRange) const
|
||||||
|
{
|
||||||
|
switch (mRelationType)
|
||||||
|
{
|
||||||
|
case Relation_Equal:
|
||||||
|
return !rangeContains(conditionRange.first, validRange);
|
||||||
|
|
||||||
|
case Relation_NotEqual:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case Relation_Greater:
|
||||||
|
case Relation_GreaterOrEqual:
|
||||||
|
case Relation_Less:
|
||||||
|
case Relation_LessOrEqual:
|
||||||
|
// If ranges do not overlap, it will never be true
|
||||||
|
return !rangesOverlap(conditionRange, validRange);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw std::logic_error("InfoCondition: operator can not be used to compare");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoSelectWrapper
|
||||||
|
|
||||||
|
CSMWorld::InfoSelectWrapper::InfoSelectWrapper(ESM::DialInfo::SelectStruct& select)
|
||||||
|
: CSMWorld::ConstInfoSelectWrapper(select), mSelect(select)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::InfoSelectWrapper::setFunctionName(FunctionName name)
|
||||||
|
{
|
||||||
|
mFunctionName = name;
|
||||||
|
updateHasVariable();
|
||||||
|
updateComparisonType();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::InfoSelectWrapper::setRelationType(RelationType type)
|
||||||
|
{
|
||||||
|
mRelationType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::InfoSelectWrapper::setVariableName(const std::string& name)
|
||||||
|
{
|
||||||
|
mVariableName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::InfoSelectWrapper::setDefaults()
|
||||||
|
{
|
||||||
|
if (!variantTypeIsValid())
|
||||||
|
mSelect.mValue.setType(ESM::VT_Int);
|
||||||
|
|
||||||
|
switch (mComparisonType)
|
||||||
|
{
|
||||||
|
case Comparison_Boolean:
|
||||||
|
setRelationType(Relation_Equal);
|
||||||
|
mSelect.mValue.setInteger(1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Comparison_Integer:
|
||||||
|
case Comparison_Numeric:
|
||||||
|
setRelationType(Relation_Greater);
|
||||||
|
mSelect.mValue.setInteger(0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::InfoSelectWrapper::update()
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
|
||||||
|
// Leading 0
|
||||||
|
stream << '0';
|
||||||
|
|
||||||
|
// Write Function
|
||||||
|
|
||||||
|
bool writeIndex = false;
|
||||||
|
size_t functionIndex = static_cast<size_t>(mFunctionName);
|
||||||
|
|
||||||
|
switch (mFunctionName)
|
||||||
|
{
|
||||||
|
case Function_None: stream << '0'; break;
|
||||||
|
case Function_Global: stream << '2'; break;
|
||||||
|
case Function_Local: stream << '3'; break;
|
||||||
|
case Function_Journal: stream << '4'; break;
|
||||||
|
case Function_Item: stream << '5'; break;
|
||||||
|
case Function_Dead: stream << '6'; break;
|
||||||
|
case Function_NotId: stream << '7'; break;
|
||||||
|
case Function_NotFaction: stream << '8'; break;
|
||||||
|
case Function_NotClass: stream << '9'; break;
|
||||||
|
case Function_NotRace: stream << 'A'; break;
|
||||||
|
case Function_NotCell: stream << 'B'; break;
|
||||||
|
case Function_NotLocal: stream << 'C'; break;
|
||||||
|
default: stream << '1'; writeIndex = true; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (writeIndex && functionIndex < 10) // leading 0
|
||||||
|
stream << '0' << functionIndex;
|
||||||
|
else if (writeIndex)
|
||||||
|
stream << functionIndex;
|
||||||
|
else
|
||||||
|
stream << "00";
|
||||||
|
|
||||||
|
// Write Relation
|
||||||
|
switch (mRelationType)
|
||||||
|
{
|
||||||
|
case Relation_Equal: stream << '0'; break;
|
||||||
|
case Relation_NotEqual: stream << '1'; break;
|
||||||
|
case Relation_Greater: stream << '2'; break;
|
||||||
|
case Relation_GreaterOrEqual: stream << '3'; break;
|
||||||
|
case Relation_Less: stream << '4'; break;
|
||||||
|
case Relation_LessOrEqual: stream << '5'; break;
|
||||||
|
default: stream << '0'; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mHasVariable)
|
||||||
|
stream << mVariableName;
|
||||||
|
|
||||||
|
mSelect.mSelectRule = stream.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
ESM::Variant& CSMWorld::InfoSelectWrapper::getVariant()
|
||||||
|
{
|
||||||
|
return mSelect.mValue;
|
||||||
|
}
|
243
apps/opencs/model/world/infoselectwrapper.hpp
Normal file
243
apps/opencs/model/world/infoselectwrapper.hpp
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
#ifndef CSM_WORLD_INFOSELECTWRAPPER_H
|
||||||
|
#define CSM_WORLD_INFOSELECTWRAPPER_H
|
||||||
|
|
||||||
|
#include <components/esm/loadinfo.hpp>
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
// ESM::DialInfo::SelectStruct.mSelectRule
|
||||||
|
// 012345...
|
||||||
|
// ^^^ ^^
|
||||||
|
// ||| ||
|
||||||
|
// ||| |+------------- condition variable string
|
||||||
|
// ||| +-------------- comparison type, ['0'..'5']; e.g. !=, <, >=, etc
|
||||||
|
// ||+---------------- function index (encoded, where function == '1')
|
||||||
|
// |+----------------- function, ['1'..'C']; e.g. Global, Local, Not ID, etc
|
||||||
|
// +------------------ unknown
|
||||||
|
//
|
||||||
|
|
||||||
|
// Wrapper for DialInfo::SelectStruct
|
||||||
|
class ConstInfoSelectWrapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Order matters
|
||||||
|
enum FunctionName
|
||||||
|
{
|
||||||
|
Function_RankLow=0,
|
||||||
|
Function_RankHigh,
|
||||||
|
Function_RankRequirement,
|
||||||
|
Function_Reputation,
|
||||||
|
Function_Health_Percent,
|
||||||
|
Function_PcReputation,
|
||||||
|
Function_PcLevel,
|
||||||
|
Function_PcHealthPercent,
|
||||||
|
Function_PcMagicka,
|
||||||
|
Function_PcFatigue,
|
||||||
|
Function_PcStrength,
|
||||||
|
Function_PcBlock,
|
||||||
|
Function_PcArmorer,
|
||||||
|
Function_PcMediumArmor,
|
||||||
|
Function_PcHeavyArmor,
|
||||||
|
Function_PcBluntWeapon,
|
||||||
|
Function_PcLongBlade,
|
||||||
|
Function_PcAxe,
|
||||||
|
Function_PcSpear,
|
||||||
|
Function_PcAthletics,
|
||||||
|
Function_PcEnchant,
|
||||||
|
Function_PcDestruction,
|
||||||
|
Function_PcAlteration,
|
||||||
|
Function_PcIllusion,
|
||||||
|
Function_PcConjuration,
|
||||||
|
Function_PcMysticism,
|
||||||
|
Function_PcRestoration,
|
||||||
|
Function_PcAlchemy,
|
||||||
|
Function_PcUnarmored,
|
||||||
|
Function_PcSecurity,
|
||||||
|
Function_PcSneak,
|
||||||
|
Function_PcAcrobatics,
|
||||||
|
Function_PcLightArmor,
|
||||||
|
Function_PcShortBlade,
|
||||||
|
Function_PcMarksman,
|
||||||
|
Function_PcMerchantile,
|
||||||
|
Function_PcSpeechcraft,
|
||||||
|
Function_PcHandToHand,
|
||||||
|
Function_PcGender,
|
||||||
|
Function_PcExpelled,
|
||||||
|
Function_PcCommonDisease,
|
||||||
|
Function_PcBlightDisease,
|
||||||
|
Function_PcClothingModifier,
|
||||||
|
Function_PcCrimeLevel,
|
||||||
|
Function_SameSex,
|
||||||
|
Function_SameRace,
|
||||||
|
Function_SameFaction,
|
||||||
|
Function_FactionRankDifference,
|
||||||
|
Function_Detected,
|
||||||
|
Function_Alarmed,
|
||||||
|
Function_Choice,
|
||||||
|
Function_PcIntelligence,
|
||||||
|
Function_PcWillpower,
|
||||||
|
Function_PcAgility,
|
||||||
|
Function_PcSpeed,
|
||||||
|
Function_PcEndurance,
|
||||||
|
Function_PcPersonality,
|
||||||
|
Function_PcLuck,
|
||||||
|
Function_PcCorpus,
|
||||||
|
Function_Weather,
|
||||||
|
Function_PcVampire,
|
||||||
|
Function_Level,
|
||||||
|
Function_Attacked,
|
||||||
|
Function_TalkedToPc,
|
||||||
|
Function_PcHealth,
|
||||||
|
Function_CreatureTarget,
|
||||||
|
Function_FriendHit,
|
||||||
|
Function_Fight,
|
||||||
|
Function_Hello,
|
||||||
|
Function_Alarm,
|
||||||
|
Function_Flee,
|
||||||
|
Function_ShouldAttack,
|
||||||
|
Function_Werewolf,
|
||||||
|
Function_PcWerewolfKills=73,
|
||||||
|
|
||||||
|
Function_Global,
|
||||||
|
Function_Local,
|
||||||
|
Function_Journal,
|
||||||
|
Function_Item,
|
||||||
|
Function_Dead,
|
||||||
|
Function_NotId,
|
||||||
|
Function_NotFaction,
|
||||||
|
Function_NotClass,
|
||||||
|
Function_NotRace,
|
||||||
|
Function_NotCell,
|
||||||
|
Function_NotLocal,
|
||||||
|
|
||||||
|
Function_None
|
||||||
|
};
|
||||||
|
|
||||||
|
enum RelationType
|
||||||
|
{
|
||||||
|
Relation_Equal,
|
||||||
|
Relation_NotEqual,
|
||||||
|
Relation_Greater,
|
||||||
|
Relation_GreaterOrEqual,
|
||||||
|
Relation_Less,
|
||||||
|
Relation_LessOrEqual,
|
||||||
|
|
||||||
|
Relation_None
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ComparisonType
|
||||||
|
{
|
||||||
|
Comparison_Boolean,
|
||||||
|
Comparison_Integer,
|
||||||
|
Comparison_Numeric,
|
||||||
|
|
||||||
|
Comparison_None
|
||||||
|
};
|
||||||
|
|
||||||
|
static const size_t RuleMinSize;
|
||||||
|
|
||||||
|
static const size_t FunctionPrefixOffset;
|
||||||
|
static const size_t FunctionIndexOffset;
|
||||||
|
static const size_t RelationIndexOffset;
|
||||||
|
static const size_t VarNameOffset;
|
||||||
|
|
||||||
|
static const char* FunctionEnumStrings[];
|
||||||
|
static const char* RelationEnumStrings[];
|
||||||
|
static const char* ComparisonEnumStrings[];
|
||||||
|
|
||||||
|
static std::string convertToString(FunctionName name);
|
||||||
|
static std::string convertToString(RelationType type);
|
||||||
|
static std::string convertToString(ComparisonType type);
|
||||||
|
|
||||||
|
ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select);
|
||||||
|
|
||||||
|
FunctionName getFunctionName() const;
|
||||||
|
RelationType getRelationType() const;
|
||||||
|
ComparisonType getComparisonType() const;
|
||||||
|
|
||||||
|
bool hasVariable() const;
|
||||||
|
const std::string& getVariableName() const;
|
||||||
|
|
||||||
|
bool conditionIsAlwaysTrue() const;
|
||||||
|
bool conditionIsNeverTrue() const;
|
||||||
|
bool variantTypeIsValid() const;
|
||||||
|
|
||||||
|
const ESM::Variant& getVariant() const;
|
||||||
|
|
||||||
|
std::string toString() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void readRule();
|
||||||
|
void readFunctionName();
|
||||||
|
void readRelationType();
|
||||||
|
void readVariableName();
|
||||||
|
void updateHasVariable();
|
||||||
|
void updateComparisonType();
|
||||||
|
|
||||||
|
std::pair<int, int> getConditionIntRange() const;
|
||||||
|
std::pair<float, float> getConditionFloatRange() const;
|
||||||
|
|
||||||
|
std::pair<int, int> getValidIntRange() const;
|
||||||
|
std::pair<float, float> getValidFloatRange() const;
|
||||||
|
|
||||||
|
template <typename Type1, typename Type2>
|
||||||
|
bool rangeContains(Type1 value, std::pair<Type2,Type2> range) const;
|
||||||
|
|
||||||
|
template <typename Type1, typename Type2>
|
||||||
|
bool rangesOverlap(std::pair<Type1,Type1> range1, std::pair<Type2,Type2> range2) const;
|
||||||
|
|
||||||
|
template <typename Type1, typename Type2>
|
||||||
|
bool rangeFullyContains(std::pair<Type1,Type1> containing, std::pair<Type2,Type2> test) const;
|
||||||
|
|
||||||
|
template <typename Type1, typename Type2>
|
||||||
|
bool rangesMatch(std::pair<Type1,Type1> range1, std::pair<Type2,Type2> range2) const;
|
||||||
|
|
||||||
|
template <typename Type1, typename Type2>
|
||||||
|
bool conditionIsAlwaysTrue(std::pair<Type1,Type1> conditionRange, std::pair<Type2,Type2> validRange) const;
|
||||||
|
|
||||||
|
template <typename Type1, typename Type2>
|
||||||
|
bool conditionIsNeverTrue(std::pair<Type1,Type1> conditionRange, std::pair<Type2,Type2> validRange) const;
|
||||||
|
|
||||||
|
FunctionName mFunctionName;
|
||||||
|
RelationType mRelationType;
|
||||||
|
ComparisonType mComparisonType;
|
||||||
|
|
||||||
|
bool mHasVariable;
|
||||||
|
std::string mVariableName;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
const ESM::DialInfo::SelectStruct& mConstSelect;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for DialInfo::SelectStruct that can modify the wrapped select struct
|
||||||
|
class InfoSelectWrapper : public ConstInfoSelectWrapper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
InfoSelectWrapper(ESM::DialInfo::SelectStruct& select);
|
||||||
|
|
||||||
|
// Wrapped SelectStruct will not be modified until update() is called
|
||||||
|
void setFunctionName(FunctionName name);
|
||||||
|
void setRelationType(RelationType type);
|
||||||
|
void setVariableName(const std::string& name);
|
||||||
|
|
||||||
|
// Modified wrapped SelectStruct
|
||||||
|
void update();
|
||||||
|
|
||||||
|
// This sets properties based on the function name to its defaults and updates the wrapped object
|
||||||
|
void setDefaults();
|
||||||
|
|
||||||
|
ESM::Variant& getVariant();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
ESM::DialInfo::SelectStruct& mSelect;
|
||||||
|
|
||||||
|
void writeRule();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -6,6 +6,7 @@
|
||||||
#include "idcollection.hpp"
|
#include "idcollection.hpp"
|
||||||
#include "pathgrid.hpp"
|
#include "pathgrid.hpp"
|
||||||
#include "info.hpp"
|
#include "info.hpp"
|
||||||
|
#include "infoselectwrapper.hpp"
|
||||||
|
|
||||||
namespace CSMWorld
|
namespace CSMWorld
|
||||||
{
|
{
|
||||||
|
@ -529,16 +530,6 @@ namespace CSMWorld
|
||||||
return 1; // fixed at size 1
|
return 1; // fixed at size 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// ESM::DialInfo::SelectStruct.mSelectRule
|
|
||||||
// 012345...
|
|
||||||
// ^^^ ^^
|
|
||||||
// ||| ||
|
|
||||||
// ||| |+------------- condition variable string
|
|
||||||
// ||| +-------------- comparison type, ['0'..'5']; e.g. !=, <, >=, etc
|
|
||||||
// ||+---------------- function index (encoded, where function == '1')
|
|
||||||
// |+----------------- function, ['1'..'C']; e.g. Global, Local, Not ID, etc
|
|
||||||
// +------------------ unknown
|
|
||||||
//
|
|
||||||
InfoConditionAdapter::InfoConditionAdapter () {}
|
InfoConditionAdapter::InfoConditionAdapter () {}
|
||||||
|
|
||||||
void InfoConditionAdapter::addRow(Record<Info>& record, int position) const
|
void InfoConditionAdapter::addRow(Record<Info>& record, int position) const
|
||||||
|
@ -547,11 +538,11 @@ namespace CSMWorld
|
||||||
|
|
||||||
std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;
|
std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;
|
||||||
|
|
||||||
// blank row
|
// default row
|
||||||
ESM::DialInfo::SelectStruct condStruct;
|
ESM::DialInfo::SelectStruct condStruct;
|
||||||
condStruct.mSelectRule = "00000";
|
condStruct.mSelectRule = "01000";
|
||||||
condStruct.mValue = ESM::Variant();
|
condStruct.mValue = ESM::Variant();
|
||||||
condStruct.mValue.setType(ESM::VT_Int); // default to ints
|
condStruct.mValue.setType(ESM::VT_Int);
|
||||||
|
|
||||||
conditions.insert(conditions.begin()+position, condStruct);
|
conditions.insert(conditions.begin()+position, condStruct);
|
||||||
|
|
||||||
|
@ -589,89 +580,6 @@ namespace CSMWorld
|
||||||
return new NestedTableWrapper<std::vector<ESM::DialInfo::SelectStruct> >(record.get().mSelects);
|
return new NestedTableWrapper<std::vector<ESM::DialInfo::SelectStruct> >(record.get().mSelects);
|
||||||
}
|
}
|
||||||
|
|
||||||
// See the mappings in MWDialogue::SelectWrapper::getArgument
|
|
||||||
// from ESM::Attribute, ESM::Skill and MWMechanics::CreatureStats (for AI)
|
|
||||||
static std::map<const std::string, std::string> populateEncToInfoFunc()
|
|
||||||
{
|
|
||||||
std::map<const std::string, std::string> funcMap;
|
|
||||||
funcMap["00"] = "Rank Low";
|
|
||||||
funcMap["01"] = "Rank High";
|
|
||||||
funcMap["02"] = "Rank Requirement";
|
|
||||||
funcMap["03"] = "Reputation";
|
|
||||||
funcMap["04"] = "Health Percent";
|
|
||||||
funcMap["05"] = "PC Reputation";
|
|
||||||
funcMap["06"] = "PC Level";
|
|
||||||
funcMap["07"] = "PC Health Percent";
|
|
||||||
funcMap["08"] = "PC Magicka";
|
|
||||||
funcMap["09"] = "PC Fatigue";
|
|
||||||
funcMap["10"] = "PC Strength";
|
|
||||||
funcMap["11"] = "PC Block";
|
|
||||||
funcMap["12"] = "PC Armorer";
|
|
||||||
funcMap["13"] = "PC Medium Armor";
|
|
||||||
funcMap["14"] = "PC Heavy Armor";
|
|
||||||
funcMap["15"] = "PC Blunt Weapon";
|
|
||||||
funcMap["16"] = "PC Long Blade";
|
|
||||||
funcMap["17"] = "PC Axe";
|
|
||||||
funcMap["18"] = "PC Spear";
|
|
||||||
funcMap["19"] = "PC Athletics";
|
|
||||||
funcMap["20"] = "PC Enchant";
|
|
||||||
funcMap["21"] = "PC Destruction";
|
|
||||||
funcMap["22"] = "PC Alteration";
|
|
||||||
funcMap["23"] = "PC Illusion";
|
|
||||||
funcMap["24"] = "PC Conjuration";
|
|
||||||
funcMap["25"] = "PC Mysticism";
|
|
||||||
funcMap["26"] = "PC Restoration";
|
|
||||||
funcMap["27"] = "PC Alchemy";
|
|
||||||
funcMap["28"] = "PC Unarmored";
|
|
||||||
funcMap["29"] = "PC Security";
|
|
||||||
funcMap["30"] = "PC Sneak";
|
|
||||||
funcMap["31"] = "PC Acrobatics";
|
|
||||||
funcMap["32"] = "PC Light Armor";
|
|
||||||
funcMap["33"] = "PC Short Blade";
|
|
||||||
funcMap["34"] = "PC Marksman";
|
|
||||||
funcMap["35"] = "PC Merchantile";
|
|
||||||
funcMap["36"] = "PC Speechcraft";
|
|
||||||
funcMap["37"] = "PC Hand To Hand";
|
|
||||||
funcMap["38"] = "PC Sex";
|
|
||||||
funcMap["39"] = "PC Expelled";
|
|
||||||
funcMap["40"] = "PC Common Disease";
|
|
||||||
funcMap["41"] = "PC Blight Disease";
|
|
||||||
funcMap["42"] = "PC Clothing Modifier";
|
|
||||||
funcMap["43"] = "PC Crime Level";
|
|
||||||
funcMap["44"] = "Same Sex";
|
|
||||||
funcMap["45"] = "Same Race";
|
|
||||||
funcMap["46"] = "Same Faction";
|
|
||||||
funcMap["47"] = "Faction Rank Difference";
|
|
||||||
funcMap["48"] = "Detected";
|
|
||||||
funcMap["49"] = "Alarmed";
|
|
||||||
funcMap["50"] = "Choice";
|
|
||||||
funcMap["51"] = "PC Intelligence";
|
|
||||||
funcMap["52"] = "PC Willpower";
|
|
||||||
funcMap["53"] = "PC Agility";
|
|
||||||
funcMap["54"] = "PC Speed";
|
|
||||||
funcMap["55"] = "PC Endurance";
|
|
||||||
funcMap["56"] = "PC Personality";
|
|
||||||
funcMap["57"] = "PC Luck";
|
|
||||||
funcMap["58"] = "PC Corpus";
|
|
||||||
funcMap["59"] = "Weather";
|
|
||||||
funcMap["60"] = "PC Vampire";
|
|
||||||
funcMap["61"] = "Level";
|
|
||||||
funcMap["62"] = "Attacked";
|
|
||||||
funcMap["63"] = "Talked To PC";
|
|
||||||
funcMap["64"] = "PC Health";
|
|
||||||
funcMap["65"] = "Creature Target";
|
|
||||||
funcMap["66"] = "Friend Hit";
|
|
||||||
funcMap["67"] = "Fight";
|
|
||||||
funcMap["68"] = "Hello";
|
|
||||||
funcMap["69"] = "Alarm";
|
|
||||||
funcMap["70"] = "Flee";
|
|
||||||
funcMap["71"] = "Should Attack";
|
|
||||||
funcMap["72"] = "Werewolf";
|
|
||||||
funcMap["73"] = "PC Werewolf Kills";
|
|
||||||
return funcMap;
|
|
||||||
}
|
|
||||||
static const std::map<const std::string, std::string> sEncToInfoFunc = populateEncToInfoFunc();
|
|
||||||
|
|
||||||
QVariant InfoConditionAdapter::getData(const Record<Info>& record,
|
QVariant InfoConditionAdapter::getData(const Record<Info>& record,
|
||||||
int subRowIndex, int subColIndex) const
|
int subRowIndex, int subColIndex) const
|
||||||
{
|
{
|
||||||
|
@ -682,70 +590,36 @@ namespace CSMWorld
|
||||||
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))
|
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))
|
||||||
throw std::runtime_error ("index out of range");
|
throw std::runtime_error ("index out of range");
|
||||||
|
|
||||||
|
ConstInfoSelectWrapper infoSelectWrapper(conditions[subRowIndex]);
|
||||||
|
|
||||||
switch (subColIndex)
|
switch (subColIndex)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
char condType = conditions[subRowIndex].mSelectRule[1];
|
return infoSelectWrapper.getFunctionName();
|
||||||
switch (condType)
|
|
||||||
{
|
|
||||||
case '0': return 0; // blank space
|
|
||||||
case '1': return 1; // Function
|
|
||||||
case '2': return 2; // Global
|
|
||||||
case '3': return 3; // Local
|
|
||||||
case '4': return 4; // Journal
|
|
||||||
case '5': return 5; // Item
|
|
||||||
case '6': return 6; // Dead
|
|
||||||
case '7': return 7; // Not ID
|
|
||||||
case '8': return 8; // Not Factio
|
|
||||||
case '9': return 9; // Not Class
|
|
||||||
case 'A': return 10; // Not Race
|
|
||||||
case 'B': return 11; // Not Cell
|
|
||||||
case 'C': return 12; // Not Local
|
|
||||||
default: return QVariant(); // TODO: log an error?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
if (conditions[subRowIndex].mSelectRule[1] == '1')
|
if (infoSelectWrapper.hasVariable())
|
||||||
{
|
return QString(infoSelectWrapper.getVariableName().c_str());
|
||||||
// throws an exception if the encoding is not found
|
|
||||||
return sEncToInfoFunc.at(conditions[subRowIndex].mSelectRule.substr(2, 2)).c_str();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return QString(conditions[subRowIndex].mSelectRule.substr(5).c_str());
|
return "";
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
char compType = conditions[subRowIndex].mSelectRule[4];
|
return infoSelectWrapper.getRelationType();
|
||||||
switch (compType)
|
|
||||||
{
|
|
||||||
case '0': return 3; // =
|
|
||||||
case '1': return 0; // !=
|
|
||||||
case '2': return 4; // >
|
|
||||||
case '3': return 5; // >=
|
|
||||||
case '4': return 1; // <
|
|
||||||
case '5': return 2; // <=
|
|
||||||
default: return QVariant(); // TODO: log an error?
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
switch (conditions[subRowIndex].mValue.getType())
|
switch (infoSelectWrapper.getVariant().getType())
|
||||||
{
|
{
|
||||||
case ESM::VT_String:
|
|
||||||
{
|
|
||||||
return QString::fromUtf8 (conditions[subRowIndex].mValue.getString().c_str());
|
|
||||||
}
|
|
||||||
case ESM::VT_Int:
|
case ESM::VT_Int:
|
||||||
case ESM::VT_Short:
|
|
||||||
case ESM::VT_Long:
|
|
||||||
{
|
{
|
||||||
return conditions[subRowIndex].mValue.getInteger();
|
return infoSelectWrapper.getVariant().getInteger();
|
||||||
}
|
}
|
||||||
case ESM::VT_Float:
|
case ESM::VT_Float:
|
||||||
{
|
{
|
||||||
return conditions[subRowIndex].mValue.getFloat();
|
return infoSelectWrapper.getVariant().getFloat();
|
||||||
}
|
}
|
||||||
default: return QVariant();
|
default: return QVariant();
|
||||||
}
|
}
|
||||||
|
@ -764,101 +638,63 @@ namespace CSMWorld
|
||||||
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))
|
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))
|
||||||
throw std::runtime_error ("index out of range");
|
throw std::runtime_error ("index out of range");
|
||||||
|
|
||||||
|
InfoSelectWrapper infoSelectWrapper(conditions[subRowIndex]);
|
||||||
|
bool conversionResult = false;
|
||||||
|
|
||||||
switch (subColIndex)
|
switch (subColIndex)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0: // Function
|
||||||
{
|
{
|
||||||
// See sInfoCondFunc in columns.cpp for the enum values
|
infoSelectWrapper.setFunctionName(static_cast<ConstInfoSelectWrapper::FunctionName>(value.toInt()));
|
||||||
switch (value.toInt())
|
|
||||||
{
|
|
||||||
// FIXME: when these change the values of the other columns need to change
|
|
||||||
// correspondingly (and automatically)
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
conditions[subRowIndex].mSelectRule[1] = '1'; // Function
|
|
||||||
// default to "Rank Low"
|
|
||||||
conditions[subRowIndex].mSelectRule[2] = '0';
|
|
||||||
conditions[subRowIndex].mSelectRule[3] = '0';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2: conditions[subRowIndex].mSelectRule[1] = '2'; break; // Global
|
|
||||||
case 3: conditions[subRowIndex].mSelectRule[1] = '3'; break; // Local
|
|
||||||
case 4: conditions[subRowIndex].mSelectRule[1] = '4'; break; // Journal
|
|
||||||
case 5: conditions[subRowIndex].mSelectRule[1] = '5'; break; // Item
|
|
||||||
case 6: conditions[subRowIndex].mSelectRule[1] = '6'; break; // Dead
|
|
||||||
case 7: conditions[subRowIndex].mSelectRule[1] = '7'; break; // Not ID
|
|
||||||
case 8: conditions[subRowIndex].mSelectRule[1] = '8'; break; // Not Faction
|
|
||||||
case 9: conditions[subRowIndex].mSelectRule[1] = '9'; break; // Not Class
|
|
||||||
case 10: conditions[subRowIndex].mSelectRule[1] = 'A'; break; // Not Race
|
|
||||||
case 11: conditions[subRowIndex].mSelectRule[1] = 'B'; break; // Not Cell
|
|
||||||
case 12: conditions[subRowIndex].mSelectRule[1] = 'C'; break; // Not Local
|
|
||||||
default: return; // return without saving
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
if (conditions[subRowIndex].mSelectRule[1] == '1')
|
|
||||||
{
|
|
||||||
std::map<const std::string, std::string>::const_iterator it = sEncToInfoFunc.begin();
|
|
||||||
for (;it != sEncToInfoFunc.end(); ++it)
|
|
||||||
{
|
|
||||||
if (it->second == value.toString().toUtf8().constData())
|
|
||||||
{
|
|
||||||
std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 2);
|
|
||||||
rule.append(it->first);
|
|
||||||
// leave old values for undo (NOTE: may not be vanilla's behaviour)
|
|
||||||
rule.append(conditions[subRowIndex].mSelectRule.substr(4));
|
|
||||||
conditions[subRowIndex].mSelectRule = rule;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (it == sEncToInfoFunc.end())
|
if (infoSelectWrapper.getComparisonType() != ConstInfoSelectWrapper::Comparison_Numeric &&
|
||||||
return; // return without saving; TODO: maybe log an error here
|
infoSelectWrapper.getVariant().getType() != ESM::VT_Int)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// FIXME: validate the string values before saving, based on the current function
|
infoSelectWrapper.getVariant().setType(ESM::VT_Int);
|
||||||
std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 5);
|
|
||||||
conditions[subRowIndex].mSelectRule = rule.append(value.toString().toUtf8().constData());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
infoSelectWrapper.update();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2:
|
case 1: // Variable
|
||||||
{
|
{
|
||||||
// See sInfoCondComp in columns.cpp for the enum values
|
infoSelectWrapper.setVariableName(value.toString().toUtf8().constData());
|
||||||
switch (value.toInt())
|
infoSelectWrapper.update();
|
||||||
{
|
|
||||||
case 0: conditions[subRowIndex].mSelectRule[4] = '1'; break; // !=
|
|
||||||
case 1: conditions[subRowIndex].mSelectRule[4] = '4'; break; // <
|
|
||||||
case 2: conditions[subRowIndex].mSelectRule[4] = '5'; break; // <=
|
|
||||||
case 3: conditions[subRowIndex].mSelectRule[4] = '0'; break; // =
|
|
||||||
case 4: conditions[subRowIndex].mSelectRule[4] = '2'; break; // >
|
|
||||||
case 5: conditions[subRowIndex].mSelectRule[4] = '3'; break; // >=
|
|
||||||
default: return; // return without saving
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 3:
|
case 2: // Relation
|
||||||
{
|
{
|
||||||
switch (conditions[subRowIndex].mValue.getType())
|
infoSelectWrapper.setRelationType(static_cast<ConstInfoSelectWrapper::RelationType>(value.toInt()));
|
||||||
|
infoSelectWrapper.update();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: // Value
|
||||||
|
{
|
||||||
|
switch (infoSelectWrapper.getComparisonType())
|
||||||
{
|
{
|
||||||
case ESM::VT_String:
|
case ConstInfoSelectWrapper::Comparison_Numeric:
|
||||||
{
|
{
|
||||||
conditions[subRowIndex].mValue.setString (value.toString().toUtf8().constData());
|
// QVariant seems to have issues converting 0
|
||||||
|
if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare("0") == 0)
|
||||||
|
{
|
||||||
|
infoSelectWrapper.getVariant().setType(ESM::VT_Int);
|
||||||
|
infoSelectWrapper.getVariant().setInteger(value.toInt());
|
||||||
|
}
|
||||||
|
else if (value.toFloat(&conversionResult) && conversionResult)
|
||||||
|
{
|
||||||
|
infoSelectWrapper.getVariant().setType(ESM::VT_Float);
|
||||||
|
infoSelectWrapper.getVariant().setFloat(value.toFloat());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESM::VT_Int:
|
case ConstInfoSelectWrapper::Comparison_Boolean:
|
||||||
case ESM::VT_Short:
|
case ConstInfoSelectWrapper::Comparison_Integer:
|
||||||
case ESM::VT_Long:
|
|
||||||
{
|
{
|
||||||
conditions[subRowIndex].mValue.setInteger (value.toInt());
|
if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare("0") == 0)
|
||||||
break;
|
{
|
||||||
}
|
infoSelectWrapper.getVariant().setType(ESM::VT_Int);
|
||||||
case ESM::VT_Float:
|
infoSelectWrapper.getVariant().setInteger(value.toInt());
|
||||||
{
|
}
|
||||||
conditions[subRowIndex].mValue.setFloat (value.toFloat());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: break;
|
default: break;
|
||||||
|
|
|
@ -108,7 +108,7 @@ void CSMWorld::IngredEffectRefIdAdapter::setNestedTable (const RefIdColumn* colu
|
||||||
ESM::Ingredient ingredient = record.get();
|
ESM::Ingredient ingredient = record.get();
|
||||||
|
|
||||||
ingredient.mData =
|
ingredient.mData =
|
||||||
static_cast<const NestedTableWrapper<std::vector<typename ESM::Ingredient::IRDTstruct> >&>(nestedTable).mNestedTable.at(0);
|
static_cast<const NestedTableWrapper<std::vector<ESM::Ingredient::IRDTstruct> >&>(nestedTable).mNestedTable.at(0);
|
||||||
|
|
||||||
record.setModified (ingredient);
|
record.setModified (ingredient);
|
||||||
}
|
}
|
||||||
|
@ -120,11 +120,11 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::IngredEffectRefIdAdapter::nestedTabl
|
||||||
static_cast<const Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
|
static_cast<const Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
|
||||||
|
|
||||||
// return the whole struct
|
// return the whole struct
|
||||||
std::vector<typename ESM::Ingredient::IRDTstruct> wrap;
|
std::vector<ESM::Ingredient::IRDTstruct> wrap;
|
||||||
wrap.push_back(record.get().mData);
|
wrap.push_back(record.get().mData);
|
||||||
|
|
||||||
// deleted by dtor of NestedTableStoring
|
// deleted by dtor of NestedTableStoring
|
||||||
return new NestedTableWrapper<std::vector<typename ESM::Ingredient::IRDTstruct> >(wrap);
|
return new NestedTableWrapper<std::vector<ESM::Ingredient::IRDTstruct> >(wrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant CSMWorld::IngredEffectRefIdAdapter::getNestedData (const RefIdColumn *column,
|
QVariant CSMWorld::IngredEffectRefIdAdapter::getNestedData (const RefIdColumn *column,
|
||||||
|
@ -1129,7 +1129,7 @@ void CSMWorld::CreatureAttributesRefIdAdapter::setNestedTable (const RefIdColumn
|
||||||
|
|
||||||
// store the whole struct
|
// store the whole struct
|
||||||
creature.mData =
|
creature.mData =
|
||||||
static_cast<const NestedTableWrapper<std::vector<typename ESM::Creature::NPDTstruct> > &>(nestedTable).mNestedTable.at(0);
|
static_cast<const NestedTableWrapper<std::vector<ESM::Creature::NPDTstruct> > &>(nestedTable).mNestedTable.at(0);
|
||||||
|
|
||||||
record.setModified (creature);
|
record.setModified (creature);
|
||||||
}
|
}
|
||||||
|
@ -1141,10 +1141,10 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::CreatureAttributesRefIdAdapter::nest
|
||||||
static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
|
static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
|
||||||
|
|
||||||
// return the whole struct
|
// return the whole struct
|
||||||
std::vector<typename ESM::Creature::NPDTstruct> wrap;
|
std::vector<ESM::Creature::NPDTstruct> wrap;
|
||||||
wrap.push_back(record.get().mData);
|
wrap.push_back(record.get().mData);
|
||||||
// deleted by dtor of NestedTableStoring
|
// deleted by dtor of NestedTableStoring
|
||||||
return new NestedTableWrapper<std::vector<typename ESM::Creature::NPDTstruct> >(wrap);
|
return new NestedTableWrapper<std::vector<ESM::Creature::NPDTstruct> >(wrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant CSMWorld::CreatureAttributesRefIdAdapter::getNestedData (const RefIdColumn *column,
|
QVariant CSMWorld::CreatureAttributesRefIdAdapter::getNestedData (const RefIdColumn *column,
|
||||||
|
@ -1235,7 +1235,7 @@ void CSMWorld::CreatureAttackRefIdAdapter::setNestedTable (const RefIdColumn* co
|
||||||
|
|
||||||
// store the whole struct
|
// store the whole struct
|
||||||
creature.mData =
|
creature.mData =
|
||||||
static_cast<const NestedTableWrapper<std::vector<typename ESM::Creature::NPDTstruct> > &>(nestedTable).mNestedTable.at(0);
|
static_cast<const NestedTableWrapper<std::vector<ESM::Creature::NPDTstruct> > &>(nestedTable).mNestedTable.at(0);
|
||||||
|
|
||||||
record.setModified (creature);
|
record.setModified (creature);
|
||||||
}
|
}
|
||||||
|
@ -1247,10 +1247,10 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::CreatureAttackRefIdAdapter::nestedTa
|
||||||
static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
|
static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
|
||||||
|
|
||||||
// return the whole struct
|
// return the whole struct
|
||||||
std::vector<typename ESM::Creature::NPDTstruct> wrap;
|
std::vector<ESM::Creature::NPDTstruct> wrap;
|
||||||
wrap.push_back(record.get().mData);
|
wrap.push_back(record.get().mData);
|
||||||
// deleted by dtor of NestedTableStoring
|
// deleted by dtor of NestedTableStoring
|
||||||
return new NestedTableWrapper<std::vector<typename ESM::Creature::NPDTstruct> >(wrap);
|
return new NestedTableWrapper<std::vector<ESM::Creature::NPDTstruct> >(wrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant CSMWorld::CreatureAttackRefIdAdapter::getNestedData (const RefIdColumn *column,
|
QVariant CSMWorld::CreatureAttackRefIdAdapter::getNestedData (const RefIdColumn *column,
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <osg/Group>
|
#include <osg/Group>
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
#include <components/esm/loadcell.hpp>
|
||||||
#include <components/esm/loadland.hpp>
|
#include <components/esm/loadland.hpp>
|
||||||
|
|
||||||
#include "../../model/world/idtable.hpp"
|
#include "../../model/world/idtable.hpp"
|
||||||
|
@ -308,12 +309,19 @@ void CSVRender::Cell::setCellArrows (int mask)
|
||||||
void CSVRender::Cell::setCellMarker()
|
void CSVRender::Cell::setCellMarker()
|
||||||
{
|
{
|
||||||
bool cellExists = false;
|
bool cellExists = false;
|
||||||
|
bool isInteriorCell = false;
|
||||||
|
|
||||||
int cellIndex = mData.getCells().searchId(mId);
|
int cellIndex = mData.getCells().searchId(mId);
|
||||||
if (cellIndex > -1)
|
if (cellIndex > -1)
|
||||||
{
|
{
|
||||||
cellExists = !mData.getCells().getRecord(cellIndex).isDeleted();
|
const CSMWorld::Record<CSMWorld::Cell>& cellRecord = mData.getCells().getRecord(cellIndex);
|
||||||
|
cellExists = !cellRecord.isDeleted();
|
||||||
|
isInteriorCell = cellRecord.get().mData.mFlags & ESM::Cell::Interior;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isInteriorCell) {
|
||||||
|
mCellMarker.reset(new CellMarker(mCellNode, mCoordinates, cellExists));
|
||||||
}
|
}
|
||||||
mCellMarker.reset(new CellMarker(mCellNode, mCoordinates, cellExists));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::CellCoordinates CSVRender::Cell::getCoordinates() const
|
CSMWorld::CellCoordinates CSVRender::Cell::getCoordinates() const
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "idcompletiondelegate.hpp"
|
#include "idcompletiondelegate.hpp"
|
||||||
|
|
||||||
#include "../../model/world/idcompletionmanager.hpp"
|
#include "../../model/world/idcompletionmanager.hpp"
|
||||||
|
#include "../../model/world/infoselectwrapper.hpp"
|
||||||
|
|
||||||
#include "../widget/droplineedit.hpp"
|
#include "../widget/droplineedit.hpp"
|
||||||
|
|
||||||
|
@ -27,6 +28,56 @@ QWidget *CSVWorld::IdCompletionDelegate::createEditor(QWidget *parent,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The completer for InfoCondVar needs to return a completer based on the first column
|
||||||
|
if (display == CSMWorld::ColumnBase::Display_InfoCondVar)
|
||||||
|
{
|
||||||
|
QModelIndex sibling = index.sibling(index.row(), 0);
|
||||||
|
int conditionFunction = sibling.model()->data(sibling, Qt::EditRole).toInt();
|
||||||
|
|
||||||
|
switch (conditionFunction)
|
||||||
|
{
|
||||||
|
case CSMWorld::ConstInfoSelectWrapper::Function_Global:
|
||||||
|
{
|
||||||
|
return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_GlobalVariable);
|
||||||
|
}
|
||||||
|
case CSMWorld::ConstInfoSelectWrapper::Function_Journal:
|
||||||
|
{
|
||||||
|
return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Journal);
|
||||||
|
}
|
||||||
|
case CSMWorld::ConstInfoSelectWrapper::Function_Item:
|
||||||
|
{
|
||||||
|
return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Referenceable);
|
||||||
|
}
|
||||||
|
case CSMWorld::ConstInfoSelectWrapper::Function_Dead:
|
||||||
|
case CSMWorld::ConstInfoSelectWrapper::Function_NotId:
|
||||||
|
{
|
||||||
|
return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Referenceable);
|
||||||
|
}
|
||||||
|
case CSMWorld::ConstInfoSelectWrapper::Function_NotFaction:
|
||||||
|
{
|
||||||
|
return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Faction);
|
||||||
|
}
|
||||||
|
case CSMWorld::ConstInfoSelectWrapper::Function_NotClass:
|
||||||
|
{
|
||||||
|
return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Class);
|
||||||
|
}
|
||||||
|
case CSMWorld::ConstInfoSelectWrapper::Function_NotRace:
|
||||||
|
{
|
||||||
|
return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Race);
|
||||||
|
}
|
||||||
|
case CSMWorld::ConstInfoSelectWrapper::Function_NotCell:
|
||||||
|
{
|
||||||
|
return createEditor (parent, option, index, CSMWorld::ColumnBase::Display_Cell);
|
||||||
|
}
|
||||||
|
case CSMWorld::ConstInfoSelectWrapper::Function_Local:
|
||||||
|
case CSMWorld::ConstInfoSelectWrapper::Function_NotLocal:
|
||||||
|
{
|
||||||
|
return new CSVWidget::DropLineEdit(display, parent);
|
||||||
|
}
|
||||||
|
default: return 0; // The rest of them can't be edited anyway
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CSMWorld::IdCompletionManager &completionManager = getDocument().getIdCompletionManager();
|
CSMWorld::IdCompletionManager &completionManager = getDocument().getIdCompletionManager();
|
||||||
CSVWidget::DropLineEdit *editor = new CSVWidget::DropLineEdit(display, parent);
|
CSVWidget::DropLineEdit *editor = new CSVWidget::DropLineEdit(display, parent);
|
||||||
editor->setCompleter(completionManager.getCompleter(display).get());
|
editor->setCompleter(completionManager.getCompleter(display).get());
|
||||||
|
|
|
@ -76,8 +76,6 @@ void OMW::Engine::executeLocalScripts()
|
||||||
&script.second.getRefData().getLocals(), script.second);
|
&script.second.getRefData().getLocals(), script.second);
|
||||||
mEnvironment.getScriptManager()->run (script.first, interpreterContext);
|
mEnvironment.getScriptManager()->run (script.first, interpreterContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
localScripts.setIgnore (MWWorld::Ptr());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OMW::Engine::frame(float frametime)
|
void OMW::Engine::frame(float frametime)
|
||||||
|
@ -453,8 +451,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
Settings::Manager::getString("texture mag filter", "General"),
|
Settings::Manager::getString("texture mag filter", "General"),
|
||||||
Settings::Manager::getString("texture min filter", "General"),
|
Settings::Manager::getString("texture min filter", "General"),
|
||||||
Settings::Manager::getString("texture mipmap", "General"),
|
Settings::Manager::getString("texture mipmap", "General"),
|
||||||
Settings::Manager::getInt("anisotropy", "General"),
|
Settings::Manager::getInt("anisotropy", "General")
|
||||||
NULL
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create input and UI first to set up a bootstrapping environment for
|
// Create input and UI first to set up a bootstrapping environment for
|
||||||
|
|
|
@ -231,15 +231,16 @@ namespace MWBase
|
||||||
|
|
||||||
virtual float getTimeScaleFactor() const = 0;
|
virtual float getTimeScaleFactor() const = 0;
|
||||||
|
|
||||||
virtual void changeToInteriorCell (const std::string& cellName,
|
virtual void changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool changeEvent=true) = 0;
|
||||||
const ESM::Position& position) = 0;
|
|
||||||
///< Move to interior cell.
|
///< Move to interior cell.
|
||||||
|
///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
|
||||||
|
|
||||||
virtual void changeToExteriorCell (const ESM::Position& position) = 0;
|
virtual void changeToExteriorCell (const ESM::Position& position, bool changeEvent=true) = 0;
|
||||||
///< Move to exterior cell.
|
///< Move to exterior cell.
|
||||||
|
///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
|
||||||
|
|
||||||
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange=true) = 0;
|
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool changeEvent=true) = 0;
|
||||||
///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes
|
///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
|
||||||
|
|
||||||
virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0;
|
virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0;
|
||||||
///< Return a cell matching the given name or a 0-pointer, if there is no such cell.
|
///< Return a cell matching the given name or a 0-pointer, if there is no such cell.
|
||||||
|
@ -277,8 +278,12 @@ namespace MWBase
|
||||||
|
|
||||||
virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0;
|
virtual void rotateObject(const MWWorld::Ptr& ptr,float x,float y,float z, bool adjust = false) = 0;
|
||||||
|
|
||||||
virtual MWWorld::Ptr safePlaceObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0;
|
virtual MWWorld::Ptr placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0;
|
||||||
///< place an object in a "safe" location (ie not in the void, etc).
|
///< Place an object. Makes a copy of the Ptr.
|
||||||
|
|
||||||
|
virtual MWWorld::Ptr safePlaceObject (const MWWorld::ConstPtr& ptr, const MWWorld::ConstPtr& referenceObject, MWWorld::CellStore* referenceCell, int direction, float distance) = 0;
|
||||||
|
///< Place an object in a safe place next to \a referenceObject. \a direction and \a distance specify the wanted placement
|
||||||
|
/// relative to \a referenceObject (but the object may be placed somewhere else if the wanted location is obstructed).
|
||||||
|
|
||||||
virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false)
|
virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false)
|
||||||
const = 0;
|
const = 0;
|
||||||
|
|
|
@ -306,18 +306,7 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply "On hit" enchanted weapons
|
// Apply "On hit" enchanted weapons
|
||||||
std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : "";
|
MWMechanics::applyOnStrikeEnchantment(ptr, victim, weapon, hitPosition);
|
||||||
if (!enchantmentName.empty())
|
|
||||||
{
|
|
||||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
|
|
||||||
enchantmentName);
|
|
||||||
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
|
||||||
{
|
|
||||||
MWMechanics::CastSpell cast(ptr, victim);
|
|
||||||
cast.mHitPosition = hitPosition;
|
|
||||||
cast.cast(weapon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (isBipedal(ptr))
|
else if (isBipedal(ptr))
|
||||||
{
|
{
|
||||||
|
@ -770,7 +759,18 @@ namespace MWClass
|
||||||
|
|
||||||
void Creature::respawn(const MWWorld::Ptr &ptr) const
|
void Creature::respawn(const MWWorld::Ptr &ptr) const
|
||||||
{
|
{
|
||||||
if (isFlagBitSet(ptr, ESM::Creature::Respawn))
|
const MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);
|
||||||
|
if (ptr.getRefData().getCount() > 0 && !creatureStats.isDead())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
|
static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat();
|
||||||
|
static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat();
|
||||||
|
|
||||||
|
float delay = ptr.getRefData().getCount() == 0 ? fCorpseClearDelay : std::min(fCorpseRespawnDelay, fCorpseClearDelay);
|
||||||
|
|
||||||
|
if (isFlagBitSet(ptr, ESM::Creature::Respawn)
|
||||||
|
&& creatureStats.getTimeOfDeath() + delay <= MWBase::Environment::get().getWorld()->getTimeStamp())
|
||||||
{
|
{
|
||||||
if (ptr.getCellRef().hasContentFile())
|
if (ptr.getCellRef().hasContentFile())
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,7 +44,28 @@ namespace MWClass
|
||||||
ensureCustomData(ptr);
|
ensureCustomData(ptr);
|
||||||
|
|
||||||
CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData();
|
CreatureLevListCustomData& customData = ptr.getRefData().getCustomData()->asCreatureLevListCustomData();
|
||||||
customData.mSpawn = true;
|
if (customData.mSpawn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
MWWorld::Ptr creature = (customData.mSpawnActorId == -1) ? MWWorld::Ptr() : MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId);
|
||||||
|
if (!creature.isEmpty())
|
||||||
|
{
|
||||||
|
const MWMechanics::CreatureStats& creatureStats = creature.getClass().getCreatureStats(creature);
|
||||||
|
if (creature.getRefData().getCount() == 0)
|
||||||
|
customData.mSpawn = true;
|
||||||
|
else if (creatureStats.isDead())
|
||||||
|
{
|
||||||
|
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
|
static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat();
|
||||||
|
static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat();
|
||||||
|
|
||||||
|
float delay = std::min(fCorpseRespawnDelay, fCorpseClearDelay);
|
||||||
|
if (creatureStats.getTimeOfDeath() + delay <= MWBase::Environment::get().getWorld()->getTimeStamp())
|
||||||
|
customData.mSpawn = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
customData.mSpawn = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreatureLevList::registerSelf()
|
void CreatureLevList::registerSelf()
|
||||||
|
@ -56,8 +77,9 @@ namespace MWClass
|
||||||
|
|
||||||
void CreatureLevList::getModelsToPreload(const MWWorld::Ptr &ptr, std::vector<std::string> &models) const
|
void CreatureLevList::getModelsToPreload(const MWWorld::Ptr &ptr, std::vector<std::string> &models) const
|
||||||
{
|
{
|
||||||
|
// disable for now, too many false positives
|
||||||
|
/*
|
||||||
const MWWorld::LiveCellRef<ESM::CreatureLevList> *ref = ptr.get<ESM::CreatureLevList>();
|
const MWWorld::LiveCellRef<ESM::CreatureLevList> *ref = ptr.get<ESM::CreatureLevList>();
|
||||||
|
|
||||||
for (std::vector<ESM::LevelledListBase::LevelItem>::const_iterator it = ref->mBase->mList.begin(); it != ref->mBase->mList.end(); ++it)
|
for (std::vector<ESM::LevelledListBase::LevelItem>::const_iterator it = ref->mBase->mList.begin(); it != ref->mBase->mList.end(); ++it)
|
||||||
{
|
{
|
||||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||||
|
@ -68,6 +90,7 @@ namespace MWClass
|
||||||
MWWorld::ManualRef ref(store, it->mId);
|
MWWorld::ManualRef ref(store, it->mId);
|
||||||
ref.getPtr().getClass().getModelsToPreload(ref.getPtr(), models);
|
ref.getPtr().getClass().getModelsToPreload(ref.getPtr(), models);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, const std::string& model, MWRender::RenderingInterface &renderingInterface) const
|
void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, const std::string& model, MWRender::RenderingInterface &renderingInterface) const
|
||||||
|
@ -97,7 +120,7 @@ namespace MWClass
|
||||||
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
||||||
MWWorld::ManualRef ref(store, id);
|
MWWorld::ManualRef ref(store, id);
|
||||||
ref.getPtr().getCellRef().setPosition(ptr.getCellRef().getPosition());
|
ref.getPtr().getCellRef().setPosition(ptr.getCellRef().getPosition());
|
||||||
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().getPosition());
|
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().getPosition());
|
||||||
customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId();
|
customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId();
|
||||||
customData.mSpawn = false;
|
customData.mSpawn = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -637,18 +637,7 @@ namespace MWClass
|
||||||
damage *= store.find("fCombatKODamageMult")->getFloat();
|
damage *= store.find("fCombatKODamageMult")->getFloat();
|
||||||
|
|
||||||
// Apply "On hit" enchanted weapons
|
// Apply "On hit" enchanted weapons
|
||||||
std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : "";
|
MWMechanics::applyOnStrikeEnchantment(ptr, victim, weapon, hitPosition);
|
||||||
if (!enchantmentName.empty())
|
|
||||||
{
|
|
||||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
|
|
||||||
enchantmentName);
|
|
||||||
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
|
||||||
{
|
|
||||||
MWMechanics::CastSpell cast(ptr, victim);
|
|
||||||
cast.mHitPosition = hitPosition;
|
|
||||||
cast.cast(weapon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MWMechanics::applyElementalShields(ptr, victim);
|
MWMechanics::applyElementalShields(ptr, victim);
|
||||||
|
|
||||||
|
@ -1303,7 +1292,18 @@ namespace MWClass
|
||||||
|
|
||||||
void Npc::respawn(const MWWorld::Ptr &ptr) const
|
void Npc::respawn(const MWWorld::Ptr &ptr) const
|
||||||
{
|
{
|
||||||
if (ptr.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Respawn)
|
const MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);
|
||||||
|
if (ptr.getRefData().getCount() > 0 && !creatureStats.isDead())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||||
|
static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat();
|
||||||
|
static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat();
|
||||||
|
|
||||||
|
float delay = ptr.getRefData().getCount() == 0 ? fCorpseClearDelay : std::min(fCorpseRespawnDelay, fCorpseClearDelay);
|
||||||
|
|
||||||
|
if (ptr.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Respawn
|
||||||
|
&& creatureStats.getTimeOfDeath() + delay <= MWBase::Environment::get().getWorld()->getTimeStamp())
|
||||||
{
|
{
|
||||||
if (ptr.getCellRef().hasContentFile())
|
if (ptr.getCellRef().hasContentFile())
|
||||||
{
|
{
|
||||||
|
|
|
@ -158,7 +158,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve overlapping keywords
|
// resolve overlapping keywords
|
||||||
while (matches.size())
|
while (!matches.empty())
|
||||||
{
|
{
|
||||||
int longestKeywordSize = 0;
|
int longestKeywordSize = 0;
|
||||||
typename std::vector<Match>::iterator longestKeyword = matches.begin();
|
typename std::vector<Match>::iterator longestKeyword = matches.begin();
|
||||||
|
|
|
@ -67,46 +67,29 @@ namespace MWGui
|
||||||
{
|
{
|
||||||
MWMechanics::Alchemy::Result result = mAlchemy->create (mNameEdit->getCaption ());
|
MWMechanics::Alchemy::Result result = mAlchemy->create (mNameEdit->getCaption ());
|
||||||
|
|
||||||
if (result == MWMechanics::Alchemy::Result_NoName)
|
switch (result)
|
||||||
{
|
{
|
||||||
|
case MWMechanics::Alchemy::Result_NoName:
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage37}");
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage37}");
|
||||||
return;
|
break;
|
||||||
}
|
case MWMechanics::Alchemy::Result_NoMortarAndPestle:
|
||||||
|
|
||||||
// check if mortar & pestle is available (always needed)
|
|
||||||
if (result == MWMechanics::Alchemy::Result_NoMortarAndPestle)
|
|
||||||
{
|
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage45}");
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage45}");
|
||||||
return;
|
break;
|
||||||
}
|
case MWMechanics::Alchemy::Result_LessThanTwoIngredients:
|
||||||
|
|
||||||
// make sure 2 or more ingredients were selected
|
|
||||||
if (result == MWMechanics::Alchemy::Result_LessThanTwoIngredients)
|
|
||||||
{
|
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage6a}");
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage6a}");
|
||||||
return;
|
break;
|
||||||
}
|
case MWMechanics::Alchemy::Result_Success:
|
||||||
|
|
||||||
if (result == MWMechanics::Alchemy::Result_NoEffects)
|
|
||||||
{
|
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage8}");
|
|
||||||
MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == MWMechanics::Alchemy::Result_Success)
|
|
||||||
{
|
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sPotionSuccess}");
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sPotionSuccess}");
|
||||||
MWBase::Environment::get().getSoundManager()->playSound("potion success", 1.f, 1.f);
|
MWBase::Environment::get().getSoundManager()->playSound("potion success", 1.f, 1.f);
|
||||||
}
|
break;
|
||||||
else if (result == MWMechanics::Alchemy::Result_RandomFailure)
|
case MWMechanics::Alchemy::Result_NoEffects:
|
||||||
{
|
case MWMechanics::Alchemy::Result_RandomFailure:
|
||||||
// potion failed
|
|
||||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage8}");
|
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage8}");
|
||||||
MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f);
|
MWBase::Environment::get().getSoundManager()->playSound("potion fail", 1.f, 1.f);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reduce count of the ingredients
|
// remove ingredient slots that have been fully used up
|
||||||
for (int i=0; i<4; ++i)
|
for (int i=0; i<4; ++i)
|
||||||
if (mIngredients[i]->isUserString("ToolTipType"))
|
if (mIngredients[i]->isUserString("ToolTipType"))
|
||||||
{
|
{
|
||||||
|
|
|
@ -240,7 +240,7 @@ namespace MWGui
|
||||||
mCommandLine->setCaption(newCaption);
|
mCommandLine->setCaption(newCaption);
|
||||||
|
|
||||||
// List candidates if repeatedly pressing tab
|
// List candidates if repeatedly pressing tab
|
||||||
if (oldCaption == newCaption && matches.size())
|
if (oldCaption == newCaption && !matches.empty())
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
printOK("");
|
printOK("");
|
||||||
|
|
|
@ -37,7 +37,7 @@ ContainerItemModel::ContainerItemModel(const std::vector<MWWorld::Ptr>& itemSour
|
||||||
: mItemSources(itemSources)
|
: mItemSources(itemSources)
|
||||||
, mWorldItems(worldItems)
|
, mWorldItems(worldItems)
|
||||||
{
|
{
|
||||||
assert (mItemSources.size());
|
assert (!mItemSources.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source)
|
ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source)
|
||||||
|
|
|
@ -27,13 +27,6 @@
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::string fpsLevelToStr(int level)
|
|
||||||
{
|
|
||||||
if (level == 0)
|
|
||||||
return "#{sOff}";
|
|
||||||
else //if (level == 1)
|
|
||||||
return "#{sOn}";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string textureMipmappingToStr(const std::string& val)
|
std::string textureMipmappingToStr(const std::string& val)
|
||||||
{
|
{
|
||||||
|
@ -182,13 +175,9 @@ namespace MWGui
|
||||||
getWidget(mOkButton, "OkButton");
|
getWidget(mOkButton, "OkButton");
|
||||||
getWidget(mResolutionList, "ResolutionList");
|
getWidget(mResolutionList, "ResolutionList");
|
||||||
getWidget(mFullscreenButton, "FullscreenButton");
|
getWidget(mFullscreenButton, "FullscreenButton");
|
||||||
getWidget(mVSyncButton, "VSyncButton");
|
|
||||||
getWidget(mWindowBorderButton, "WindowBorderButton");
|
getWidget(mWindowBorderButton, "WindowBorderButton");
|
||||||
getWidget(mTextureFilteringButton, "TextureFilteringButton");
|
getWidget(mTextureFilteringButton, "TextureFilteringButton");
|
||||||
getWidget(mAnisotropyBox, "AnisotropyBox");
|
getWidget(mAnisotropyBox, "AnisotropyBox");
|
||||||
getWidget(mShadersButton, "ShadersButton");
|
|
||||||
getWidget(mShadowsEnabledButton, "ShadowsEnabledButton");
|
|
||||||
getWidget(mShadowsTextureSize, "ShadowsTextureSize");
|
|
||||||
getWidget(mControlsBox, "ControlsBox");
|
getWidget(mControlsBox, "ControlsBox");
|
||||||
getWidget(mResetControlsButton, "ResetControlsButton");
|
getWidget(mResetControlsButton, "ResetControlsButton");
|
||||||
getWidget(mKeyboardSwitch, "KeyboardButton");
|
getWidget(mKeyboardSwitch, "KeyboardButton");
|
||||||
|
@ -218,8 +207,6 @@ namespace MWGui
|
||||||
|
|
||||||
mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged);
|
mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged);
|
||||||
|
|
||||||
mShadowsTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onShadowTextureSizeChanged);
|
|
||||||
|
|
||||||
mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked);
|
mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked);
|
||||||
mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked);
|
mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked);
|
||||||
|
|
||||||
|
@ -260,13 +247,6 @@ namespace MWGui
|
||||||
if (waterTextureSize >= 2048)
|
if (waterTextureSize >= 2048)
|
||||||
mWaterTextureSize->setIndexSelected(2);
|
mWaterTextureSize->setIndexSelected(2);
|
||||||
|
|
||||||
mShadowsTextureSize->setCaption (Settings::Manager::getString ("texture size", "Shadows"));
|
|
||||||
|
|
||||||
if (!Settings::Manager::getBool("shaders", "Objects"))
|
|
||||||
{
|
|
||||||
mShadowsEnabledButton->setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video"));
|
mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video"));
|
||||||
|
|
||||||
mKeyboardSwitch->setStateSelected(true);
|
mKeyboardSwitch->setStateSelected(true);
|
||||||
|
@ -346,12 +326,6 @@ namespace MWGui
|
||||||
apply();
|
apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsWindow::onShadowTextureSizeChanged(MyGUI::ComboBox *_sender, size_t pos)
|
|
||||||
{
|
|
||||||
Settings::Manager::setString("texture size", "Shadows", _sender->getItemNameAt(pos));
|
|
||||||
apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SettingsWindow::onButtonToggled(MyGUI::Widget* _sender)
|
void SettingsWindow::onButtonToggled(MyGUI::Widget* _sender)
|
||||||
{
|
{
|
||||||
std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOn", "On");
|
std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOn", "On");
|
||||||
|
@ -368,21 +342,6 @@ namespace MWGui
|
||||||
newState = true;
|
newState = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_sender == mShadersButton)
|
|
||||||
{
|
|
||||||
if (newState == false)
|
|
||||||
{
|
|
||||||
// shadows not supported
|
|
||||||
mShadowsEnabledButton->setEnabled(false);
|
|
||||||
mShadowsEnabledButton->setCaptionWithReplacing("#{sOff}");
|
|
||||||
Settings::Manager::setBool("enabled", "Shadows", false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mShadowsEnabledButton->setEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sender == mFullscreenButton)
|
if (_sender == mFullscreenButton)
|
||||||
{
|
{
|
||||||
// check if this resolution is supported in fullscreen
|
// check if this resolution is supported in fullscreen
|
||||||
|
|
|
@ -28,17 +28,12 @@ namespace MWGui
|
||||||
// graphics
|
// graphics
|
||||||
MyGUI::ListBox* mResolutionList;
|
MyGUI::ListBox* mResolutionList;
|
||||||
MyGUI::Button* mFullscreenButton;
|
MyGUI::Button* mFullscreenButton;
|
||||||
MyGUI::Button* mVSyncButton;
|
|
||||||
MyGUI::Button* mWindowBorderButton;
|
MyGUI::Button* mWindowBorderButton;
|
||||||
MyGUI::ComboBox* mTextureFilteringButton;
|
MyGUI::ComboBox* mTextureFilteringButton;
|
||||||
MyGUI::Widget* mAnisotropyBox;
|
MyGUI::Widget* mAnisotropyBox;
|
||||||
MyGUI::Button* mShadersButton;
|
|
||||||
|
|
||||||
MyGUI::ComboBox* mWaterTextureSize;
|
MyGUI::ComboBox* mWaterTextureSize;
|
||||||
|
|
||||||
MyGUI::Button* mShadowsEnabledButton;
|
|
||||||
MyGUI::ComboBox* mShadowsTextureSize;
|
|
||||||
|
|
||||||
// controls
|
// controls
|
||||||
MyGUI::ScrollView* mControlsBox;
|
MyGUI::ScrollView* mControlsBox;
|
||||||
MyGUI::Button* mResetControlsButton;
|
MyGUI::Button* mResetControlsButton;
|
||||||
|
@ -58,8 +53,6 @@ namespace MWGui
|
||||||
|
|
||||||
void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
|
void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
|
||||||
|
|
||||||
void onShadowTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
|
|
||||||
|
|
||||||
void onRebindAction(MyGUI::Widget* _sender);
|
void onRebindAction(MyGUI::Widget* _sender);
|
||||||
void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel);
|
void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel);
|
||||||
void onResetDefaultBindings(MyGUI::Widget* _sender);
|
void onResetDefaultBindings(MyGUI::Widget* _sender);
|
||||||
|
|
|
@ -1377,7 +1377,7 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow)
|
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow)
|
||||||
{
|
{
|
||||||
MWWorld::Ptr followTarget = static_cast<MWMechanics::AiFollow*>(*it)->getTarget();
|
MWWorld::Ptr followTarget = (*it)->getTarget();
|
||||||
if (followTarget.isEmpty())
|
if (followTarget.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
if (followTarget == actor)
|
if (followTarget == actor)
|
||||||
|
|
|
@ -119,7 +119,7 @@ namespace MWMechanics
|
||||||
return TypeIdEscort;
|
return TypeIdEscort;
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr AiEscort::getTarget()
|
MWWorld::Ptr AiEscort::getTarget() const
|
||||||
{
|
{
|
||||||
return MWBase::Environment::get().getWorld()->getPtr(mActorId, false);
|
return MWBase::Environment::get().getWorld()->getPtr(mActorId, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
virtual int getTypeId() const;
|
virtual int getTypeId() const;
|
||||||
|
|
||||||
MWWorld::Ptr getTarget();
|
MWWorld::Ptr getTarget() const;
|
||||||
virtual bool sideWithTarget() const { return true; }
|
virtual bool sideWithTarget() const { return true; }
|
||||||
|
|
||||||
void writeState(ESM::AiSequence::AiSequence &sequence) const;
|
void writeState(ESM::AiSequence::AiSequence &sequence) const;
|
||||||
|
|
|
@ -200,7 +200,7 @@ void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
|
||||||
sequence.mPackages.push_back(package);
|
sequence.mPackages.push_back(package);
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr AiFollow::getTarget()
|
MWWorld::Ptr AiFollow::getTarget() const
|
||||||
{
|
{
|
||||||
if (mActorId == -2)
|
if (mActorId == -2)
|
||||||
return MWWorld::Ptr();
|
return MWWorld::Ptr();
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
AiFollow(const ESM::AiSequence::AiFollow* follow);
|
AiFollow(const ESM::AiSequence::AiFollow* follow);
|
||||||
|
|
||||||
MWWorld::Ptr getTarget();
|
MWWorld::Ptr getTarget() const;
|
||||||
virtual bool sideWithTarget() const { return true; }
|
virtual bool sideWithTarget() const { return true; }
|
||||||
virtual bool followTargetThroughDoors() const { return true; }
|
virtual bool followTargetThroughDoors() const { return true; }
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ namespace MWMechanics
|
||||||
float mY;
|
float mY;
|
||||||
float mZ;
|
float mZ;
|
||||||
std::string mActorRefId;
|
std::string mActorRefId;
|
||||||
int mActorId;
|
mutable int mActorId;
|
||||||
std::string mCellId;
|
std::string mCellId;
|
||||||
bool mActive; // have we spotted the target?
|
bool mActive; // have we spotted the target?
|
||||||
int mFollowIndex;
|
int mFollowIndex;
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
MWMechanics::AiPackage::~AiPackage() {}
|
MWMechanics::AiPackage::~AiPackage() {}
|
||||||
|
|
||||||
MWWorld::Ptr MWMechanics::AiPackage::getTarget()
|
MWWorld::Ptr MWMechanics::AiPackage::getTarget() const
|
||||||
{
|
{
|
||||||
return MWWorld::Ptr();
|
return MWWorld::Ptr();
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ namespace MWMechanics
|
||||||
virtual void fastForward(const MWWorld::Ptr& actor, AiState& state) {}
|
virtual void fastForward(const MWWorld::Ptr& actor, AiState& state) {}
|
||||||
|
|
||||||
/// Get the target actor the AI is targeted at (not applicable to all AI packages, default return empty Ptr)
|
/// Get the target actor the AI is targeted at (not applicable to all AI packages, default return empty Ptr)
|
||||||
virtual MWWorld::Ptr getTarget();
|
virtual MWWorld::Ptr getTarget() const;
|
||||||
|
|
||||||
/// Return true if having this AiPackage makes the actor side with the target in fights (default false)
|
/// Return true if having this AiPackage makes the actor side with the target in fights (default false)
|
||||||
virtual bool sideWithTarget() const;
|
virtual bool sideWithTarget() const;
|
||||||
|
|
|
@ -68,9 +68,8 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const
|
||||||
{
|
{
|
||||||
if (getTypeId() != AiPackage::TypeIdCombat)
|
if (getTypeId() != AiPackage::TypeIdCombat)
|
||||||
return false;
|
return false;
|
||||||
const AiCombat *combat = static_cast<const AiCombat *>(mPackages.front());
|
|
||||||
|
|
||||||
targetActor = combat->getTarget();
|
targetActor = mPackages.front()->getTarget();
|
||||||
|
|
||||||
return !targetActor.isEmpty();
|
return !targetActor.isEmpty();
|
||||||
}
|
}
|
||||||
|
@ -114,8 +113,7 @@ bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const
|
||||||
{
|
{
|
||||||
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
|
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
|
||||||
{
|
{
|
||||||
const AiCombat *combat = static_cast<const AiCombat *>(*it);
|
if ((*it)->getTarget() == actor)
|
||||||
if (combat->getTarget() == actor)
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,7 +253,7 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor)
|
||||||
for (std::list<AiPackage *>::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter)
|
for (std::list<AiPackage *>::const_iterator iter (mPackages.begin()); iter!=mPackages.end(); ++iter)
|
||||||
{
|
{
|
||||||
if((*iter)->getTypeId() == AiPackage::TypeIdCombat && package.getTypeId() == AiPackage::TypeIdCombat
|
if((*iter)->getTypeId() == AiPackage::TypeIdCombat && package.getTypeId() == AiPackage::TypeIdCombat
|
||||||
&& static_cast<const AiCombat*>(*iter)->getTarget() == static_cast<const AiCombat*>(&package)->getTarget())
|
&& (*iter)->getTarget() == (&package)->getTarget())
|
||||||
{
|
{
|
||||||
return; // already in combat with this actor
|
return; // already in combat with this actor
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,7 +255,7 @@ namespace MWMechanics
|
||||||
// Construct a new path if there isn't one
|
// Construct a new path if there isn't one
|
||||||
if(!storage.mPathFinder.isPathConstructed())
|
if(!storage.mPathFinder.isPathConstructed())
|
||||||
{
|
{
|
||||||
if (mAllowedNodes.size())
|
if (!mAllowedNodes.empty())
|
||||||
{
|
{
|
||||||
setPathToAnAllowedNode(actor, storage, pos);
|
setPathToAnAllowedNode(actor, storage, pos);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2140,10 +2140,10 @@ void CharacterController::updateHeadTracking(float duration)
|
||||||
|
|
||||||
if (!mHeadTrackTarget.isEmpty())
|
if (!mHeadTrackTarget.isEmpty())
|
||||||
{
|
{
|
||||||
osg::MatrixList mats = head->getWorldMatrices();
|
osg::NodePathList nodepaths = head->getParentalNodePaths();
|
||||||
if (mats.empty())
|
if (nodepaths.empty())
|
||||||
return;
|
return;
|
||||||
osg::Matrixf mat = mats[0];
|
osg::Matrixf mat = osg::computeLocalToWorld(nodepaths[0]);
|
||||||
osg::Vec3f headPos = mat.getTrans();
|
osg::Vec3f headPos = mat.getTrans();
|
||||||
|
|
||||||
osg::Vec3f direction;
|
osg::Vec3f direction;
|
||||||
|
@ -2154,9 +2154,9 @@ void CharacterController::updateHeadTracking(float duration)
|
||||||
node = anim->getNode("Bip01 Head");
|
node = anim->getNode("Bip01 Head");
|
||||||
if (node != NULL)
|
if (node != NULL)
|
||||||
{
|
{
|
||||||
osg::MatrixList mats = node->getWorldMatrices();
|
osg::NodePathList nodepaths = node->getParentalNodePaths();
|
||||||
if (mats.size())
|
if (!nodepaths.empty())
|
||||||
direction = mats[0].getTrans() - headPos;
|
direction = osg::computeLocalToWorld(nodepaths[0]).getTrans() - headPos;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
// no head node to look at, fall back to look at center of collision box
|
// no head node to look at, fall back to look at center of collision box
|
||||||
|
|
|
@ -29,29 +29,29 @@ float signedAngleRadians (const osg::Vec3f& v1, const osg::Vec3f& v2, const osg:
|
||||||
return std::atan2((normal * (v1 ^ v2)), (v1 * v2));
|
return std::atan2((normal * (v1 ^ v2)), (v1 * v2));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool applyEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const osg::Vec3f& hitPosition)
|
|
||||||
{
|
|
||||||
std::string enchantmentName = !object.isEmpty() ? object.getClass().getEnchantment(object) : "";
|
|
||||||
if (!enchantmentName.empty())
|
|
||||||
{
|
|
||||||
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
|
|
||||||
enchantmentName);
|
|
||||||
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
|
||||||
{
|
|
||||||
MWMechanics::CastSpell cast(attacker, victim);
|
|
||||||
cast.mHitPosition = hitPosition;
|
|
||||||
cast.cast(object);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
|
||||||
|
bool applyOnStrikeEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const osg::Vec3f& hitPosition)
|
||||||
|
{
|
||||||
|
std::string enchantmentName = !object.isEmpty() ? object.getClass().getEnchantment(object) : "";
|
||||||
|
if (!enchantmentName.empty())
|
||||||
|
{
|
||||||
|
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
|
||||||
|
enchantmentName);
|
||||||
|
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
||||||
|
{
|
||||||
|
MWMechanics::CastSpell cast(attacker, victim);
|
||||||
|
cast.mHitPosition = hitPosition;
|
||||||
|
cast.cast(object, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool blockMeleeAttack(const MWWorld::Ptr &attacker, const MWWorld::Ptr &blocker, const MWWorld::Ptr &weapon, float damage, float attackStrength)
|
bool blockMeleeAttack(const MWWorld::Ptr &attacker, const MWWorld::Ptr &blocker, const MWWorld::Ptr &weapon, float damage, float attackStrength)
|
||||||
{
|
{
|
||||||
if (!blocker.getClass().hasInventoryStore(blocker))
|
if (!blocker.getClass().hasInventoryStore(blocker))
|
||||||
|
@ -215,9 +215,9 @@ namespace MWMechanics
|
||||||
damage *= gmst.find("fCombatKODamageMult")->getFloat();
|
damage *= gmst.find("fCombatKODamageMult")->getFloat();
|
||||||
|
|
||||||
// Apply "On hit" effect of the weapon
|
// Apply "On hit" effect of the weapon
|
||||||
bool appliedEnchantment = applyEnchantment(attacker, victim, weapon, hitPosition);
|
bool appliedEnchantment = applyOnStrikeEnchantment(attacker, victim, weapon, hitPosition);
|
||||||
if (weapon != projectile)
|
if (weapon != projectile)
|
||||||
appliedEnchantment = applyEnchantment(attacker, victim, projectile, hitPosition);
|
appliedEnchantment = applyOnStrikeEnchantment(attacker, victim, projectile, hitPosition);
|
||||||
|
|
||||||
if (damage > 0)
|
if (damage > 0)
|
||||||
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
|
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
|
||||||
|
bool applyOnStrikeEnchantment(const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const osg::Vec3f& hitPosition);
|
||||||
|
|
||||||
/// @return can we block the attack?
|
/// @return can we block the attack?
|
||||||
bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker, const MWWorld::Ptr& weapon, float damage, float attackStrength);
|
bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker, const MWWorld::Ptr& weapon, float damage, float attackStrength);
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace MWMechanics
|
||||||
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
|
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
|
||||||
mHitRecovery(false), mBlock(false), mMovementFlags(0),
|
mHitRecovery(false), mBlock(false), mMovementFlags(0),
|
||||||
mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1),
|
mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1),
|
||||||
mDeathAnimation(0), mLevel (0)
|
mDeathAnimation(0), mTimeOfDeath(), mLevel (0)
|
||||||
{
|
{
|
||||||
for (int i=0; i<4; ++i)
|
for (int i=0; i<4; ++i)
|
||||||
mAiSettings[i] = 0;
|
mAiSettings[i] = 0;
|
||||||
|
@ -187,6 +187,9 @@ namespace MWMechanics
|
||||||
|
|
||||||
if (index==0 && mDynamic[index].getCurrent()<1)
|
if (index==0 && mDynamic[index].getCurrent()<1)
|
||||||
{
|
{
|
||||||
|
if (!mDead)
|
||||||
|
mTimeOfDeath = MWBase::Environment::get().getWorld()->getTimeStamp();
|
||||||
|
|
||||||
mDead = true;
|
mDead = true;
|
||||||
|
|
||||||
mDynamic[index].setModifier(0);
|
mDynamic[index].setModifier(0);
|
||||||
|
@ -503,6 +506,7 @@ namespace MWMechanics
|
||||||
state.mLevel = mLevel;
|
state.mLevel = mLevel;
|
||||||
state.mActorId = mActorId;
|
state.mActorId = mActorId;
|
||||||
state.mDeathAnimation = mDeathAnimation;
|
state.mDeathAnimation = mDeathAnimation;
|
||||||
|
state.mTimeOfDeath = mTimeOfDeath.toEsm();
|
||||||
|
|
||||||
mSpells.writeState(state.mSpells);
|
mSpells.writeState(state.mSpells);
|
||||||
mActiveSpells.writeState(state.mActiveSpells);
|
mActiveSpells.writeState(state.mActiveSpells);
|
||||||
|
@ -549,6 +553,7 @@ namespace MWMechanics
|
||||||
mLevel = state.mLevel;
|
mLevel = state.mLevel;
|
||||||
mActorId = state.mActorId;
|
mActorId = state.mActorId;
|
||||||
mDeathAnimation = state.mDeathAnimation;
|
mDeathAnimation = state.mDeathAnimation;
|
||||||
|
mTimeOfDeath = MWWorld::TimeStamp(state.mTimeOfDeath);
|
||||||
|
|
||||||
mSpells.readState(state.mSpells);
|
mSpells.readState(state.mSpells);
|
||||||
mActiveSpells.readState(state.mActiveSpells);
|
mActiveSpells.readState(state.mActiveSpells);
|
||||||
|
@ -622,6 +627,11 @@ namespace MWMechanics
|
||||||
mDeathAnimation = index;
|
mDeathAnimation = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MWWorld::TimeStamp CreatureStats::getTimeOfDeath() const
|
||||||
|
{
|
||||||
|
return mTimeOfDeath;
|
||||||
|
}
|
||||||
|
|
||||||
std::map<CreatureStats::SummonKey, int>& CreatureStats::getSummonedCreatureMap()
|
std::map<CreatureStats::SummonKey, int>& CreatureStats::getSummonedCreatureMap()
|
||||||
{
|
{
|
||||||
return mSummonedCreatures;
|
return mSummonedCreatures;
|
||||||
|
|
|
@ -65,6 +65,8 @@ namespace MWMechanics
|
||||||
// The index of the death animation that was played
|
// The index of the death animation that was played
|
||||||
unsigned char mDeathAnimation;
|
unsigned char mDeathAnimation;
|
||||||
|
|
||||||
|
MWWorld::TimeStamp mTimeOfDeath;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef std::pair<int, std::string> SummonKey; // <ESM::MagicEffect index, spell ID>
|
typedef std::pair<int, std::string> SummonKey; // <ESM::MagicEffect index, spell ID>
|
||||||
private:
|
private:
|
||||||
|
@ -259,6 +261,8 @@ namespace MWMechanics
|
||||||
unsigned char getDeathAnimation() const;
|
unsigned char getDeathAnimation() const;
|
||||||
void setDeathAnimation(unsigned char index);
|
void setDeathAnimation(unsigned char index);
|
||||||
|
|
||||||
|
MWWorld::TimeStamp getTimeOfDeath() const;
|
||||||
|
|
||||||
int getActorId();
|
int getActorId();
|
||||||
///< Will generate an actor ID, if the actor does not have one yet.
|
///< Will generate an actor ID, if the actor does not have one yet.
|
||||||
|
|
||||||
|
|
|
@ -1330,7 +1330,7 @@ namespace MWMechanics
|
||||||
{
|
{
|
||||||
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
|
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
|
||||||
{
|
{
|
||||||
MWWorld::Ptr target = static_cast<AiCombat*>(*it)->getTarget();
|
MWWorld::Ptr target = (*it)->getTarget();
|
||||||
if (!target.isEmpty() && target.getClass().isNpc())
|
if (!target.isEmpty() && target.getClass().isNpc())
|
||||||
isFightingNpc = true;
|
isFightingNpc = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,14 +120,10 @@ namespace MWMechanics
|
||||||
const MWWorld::Class& cls = actor.getClass();
|
const MWWorld::Class& cls = actor.getClass();
|
||||||
ESM::Position pos = actor.getRefData().getPosition();
|
ESM::Position pos = actor.getRefData().getPosition();
|
||||||
|
|
||||||
// actors can move at most 60 fps (the physics framerate).
|
|
||||||
// the max() can be removed if we implement physics interpolation.
|
|
||||||
float movementDuration = std::max(1/60.f, duration);
|
|
||||||
|
|
||||||
if(mDistSameSpot == -1)
|
if(mDistSameSpot == -1)
|
||||||
mDistSameSpot = DIST_SAME_SPOT * cls.getSpeed(actor);
|
mDistSameSpot = DIST_SAME_SPOT * cls.getSpeed(actor);
|
||||||
|
|
||||||
float distSameSpot = mDistSameSpot * movementDuration;
|
float distSameSpot = mDistSameSpot * duration;
|
||||||
|
|
||||||
bool samePosition = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2() < distSameSpot * distSameSpot;
|
bool samePosition = (osg::Vec2f(pos.pos[0], pos.pos[1]) - osg::Vec2f(mPrevX, mPrevY)).length2() < distSameSpot * distSameSpot;
|
||||||
|
|
||||||
|
|
|
@ -62,13 +62,6 @@ namespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyDynamicStatsEffect(int attribute, const MWWorld::Ptr& target, float magnitude)
|
|
||||||
{
|
|
||||||
MWMechanics::DynamicStat<float> value = target.getClass().getCreatureStats(target).getDynamic(attribute);
|
|
||||||
value.setCurrent(value.getCurrent()+magnitude, attribute == 2);
|
|
||||||
target.getClass().getCreatureStats(target).setDynamic(attribute, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
|
@ -694,7 +687,7 @@ namespace MWMechanics
|
||||||
throw std::runtime_error("ID type cannot be casted");
|
throw std::runtime_error("ID type cannot be casted");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CastSpell::cast(const MWWorld::Ptr &item)
|
bool CastSpell::cast(const MWWorld::Ptr &item, bool launchProjectile)
|
||||||
{
|
{
|
||||||
std::string enchantmentName = item.getClass().getEnchantment(item);
|
std::string enchantmentName = item.getClass().getEnchantment(item);
|
||||||
if (enchantmentName.empty())
|
if (enchantmentName.empty())
|
||||||
|
@ -761,15 +754,20 @@ namespace MWMechanics
|
||||||
inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch);
|
inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string projectileModel;
|
if (launchProjectile)
|
||||||
std::string sound;
|
{
|
||||||
float speed = 0;
|
std::string projectileModel;
|
||||||
getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed);
|
std::string sound;
|
||||||
if (!projectileModel.empty())
|
float speed = 0;
|
||||||
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
|
getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed);
|
||||||
false, enchantment->mEffects, mCaster, mSourceName,
|
if (!projectileModel.empty())
|
||||||
// Not needed, enchantments can only be cast by actors
|
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
|
||||||
osg::Vec3f(1,0,0));
|
false, enchantment->mEffects, mCaster, mSourceName,
|
||||||
|
// Not needed, enchantments can only be cast by actors
|
||||||
|
osg::Vec3f(1,0,0));
|
||||||
|
}
|
||||||
|
else if (!mTarget.isEmpty())
|
||||||
|
inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Target);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,8 @@ namespace MWMechanics
|
||||||
bool cast (const ESM::Spell* spell);
|
bool cast (const ESM::Spell* spell);
|
||||||
|
|
||||||
/// @note mCaster must be an actor
|
/// @note mCaster must be an actor
|
||||||
bool cast (const MWWorld::Ptr& item);
|
/// @param launchProjectile If set to false, "on target" effects are directly applied instead of being launched as projectile originating from the caster.
|
||||||
|
bool cast (const MWWorld::Ptr& item, bool launchProjectile=true);
|
||||||
|
|
||||||
/// @note mCaster must be an NPC
|
/// @note mCaster must be an NPC
|
||||||
bool cast (const ESM::Ingredient* ingredient);
|
bool cast (const ESM::Ingredient* ingredient);
|
||||||
|
|
|
@ -114,31 +114,16 @@ namespace MWMechanics
|
||||||
bool found = creatureMap.find(std::make_pair(it->first, it->second)) != creatureMap.end();
|
bool found = creatureMap.find(std::make_pair(it->first, it->second)) != creatureMap.end();
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
ESM::Position ipos = mActor.getRefData().getPosition();
|
|
||||||
osg::Vec3f pos(ipos.asVec3());
|
|
||||||
|
|
||||||
osg::Quat rot (-ipos.rot[2], osg::Vec3f(0,0,1));
|
|
||||||
const float distance = 50;
|
|
||||||
pos = pos + (rot * osg::Vec3f(0,1,0)) * distance;
|
|
||||||
ipos.pos[0] = pos.x();
|
|
||||||
ipos.pos[1] = pos.y();
|
|
||||||
ipos.pos[2] = pos.z();
|
|
||||||
ipos.rot[0] = 0;
|
|
||||||
ipos.rot[1] = 0;
|
|
||||||
ipos.rot[2] = 0;
|
|
||||||
|
|
||||||
const std::string& creatureGmst = summonMap[it->first];
|
const std::string& creatureGmst = summonMap[it->first];
|
||||||
std::string creatureID =
|
std::string creatureID =
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(creatureGmst)->getString();
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(creatureGmst)->getString();
|
||||||
|
|
||||||
if (!creatureID.empty())
|
if (!creatureID.empty())
|
||||||
{
|
{
|
||||||
MWWorld::CellStore* store = mActor.getCell();
|
|
||||||
int creatureActorId = -1;
|
int creatureActorId = -1;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1);
|
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1);
|
||||||
ref.getPtr().getCellRef().setPosition(ipos);
|
|
||||||
|
|
||||||
MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr());
|
MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr());
|
||||||
|
|
||||||
|
@ -147,7 +132,7 @@ namespace MWMechanics
|
||||||
summonedCreatureStats.getAiSequence().stack(package, ref.getPtr());
|
summonedCreatureStats.getAiSequence().stack(package, ref.getPtr());
|
||||||
creatureActorId = summonedCreatureStats.getActorId();
|
creatureActorId = summonedCreatureStats.getActorId();
|
||||||
|
|
||||||
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos);
|
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), mActor, mActor.getCell(), 0, 120.f);
|
||||||
|
|
||||||
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed);
|
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed);
|
||||||
if (anim)
|
if (anim)
|
||||||
|
|
|
@ -52,8 +52,8 @@ namespace
|
||||||
class GlowUpdater : public SceneUtil::StateSetUpdater
|
class GlowUpdater : public SceneUtil::StateSetUpdater
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GlowUpdater(osg::Vec4f color, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures)
|
GlowUpdater(int texUnit, osg::Vec4f color, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures)
|
||||||
: mTexUnit(1) // FIXME: might not always be 1
|
: mTexUnit(texUnit)
|
||||||
, mColor(color)
|
, mColor(color)
|
||||||
, mTextures(textures)
|
, mTextures(textures)
|
||||||
{
|
{
|
||||||
|
@ -1041,6 +1041,25 @@ namespace MWRender
|
||||||
return mObjectRoot.get();
|
return mObjectRoot.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FindLowestUnusedTexUnitVisitor : public osg::NodeVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FindLowestUnusedTexUnitVisitor()
|
||||||
|
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
|
||||||
|
, mLowestUnusedTexUnit(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void apply(osg::Node& node)
|
||||||
|
{
|
||||||
|
if (osg::StateSet* stateset = node.getStateSet())
|
||||||
|
mLowestUnusedTexUnit = std::max(mLowestUnusedTexUnit, int(stateset->getTextureAttributeList().size()));
|
||||||
|
|
||||||
|
traverse(node);
|
||||||
|
}
|
||||||
|
int mLowestUnusedTexUnit;
|
||||||
|
};
|
||||||
|
|
||||||
void Animation::addGlow(osg::ref_ptr<osg::Node> node, osg::Vec4f glowColor)
|
void Animation::addGlow(osg::ref_ptr<osg::Node> node, osg::Vec4f glowColor)
|
||||||
{
|
{
|
||||||
std::vector<osg::ref_ptr<osg::Texture2D> > textures;
|
std::vector<osg::ref_ptr<osg::Texture2D> > textures;
|
||||||
|
@ -1055,14 +1074,29 @@ namespace MWRender
|
||||||
|
|
||||||
osg::ref_ptr<osg::Image> image = mResourceSystem->getImageManager()->getImage(stream.str());
|
osg::ref_ptr<osg::Image> image = mResourceSystem->getImageManager()->getImage(stream.str());
|
||||||
osg::ref_ptr<osg::Texture2D> tex (new osg::Texture2D(image));
|
osg::ref_ptr<osg::Texture2D> tex (new osg::Texture2D(image));
|
||||||
|
tex->setName("envMap");
|
||||||
tex->setWrap(osg::Texture::WRAP_S, osg::Texture2D::REPEAT);
|
tex->setWrap(osg::Texture::WRAP_S, osg::Texture2D::REPEAT);
|
||||||
tex->setWrap(osg::Texture::WRAP_T, osg::Texture2D::REPEAT);
|
tex->setWrap(osg::Texture::WRAP_T, osg::Texture2D::REPEAT);
|
||||||
mResourceSystem->getSceneManager()->applyFilterSettings(tex);
|
mResourceSystem->getSceneManager()->applyFilterSettings(tex);
|
||||||
textures.push_back(tex);
|
textures.push_back(tex);
|
||||||
}
|
}
|
||||||
|
|
||||||
osg::ref_ptr<GlowUpdater> glowupdater (new GlowUpdater(glowColor, textures));
|
FindLowestUnusedTexUnitVisitor findLowestUnusedTexUnitVisitor;
|
||||||
|
node->accept(findLowestUnusedTexUnitVisitor);
|
||||||
|
int texUnit = findLowestUnusedTexUnitVisitor.mLowestUnusedTexUnit;
|
||||||
|
osg::ref_ptr<GlowUpdater> glowupdater (new GlowUpdater(texUnit, glowColor, textures));
|
||||||
node->addUpdateCallback(glowupdater);
|
node->addUpdateCallback(glowupdater);
|
||||||
|
|
||||||
|
// set a texture now so that the ShaderVisitor can find it
|
||||||
|
osg::ref_ptr<osg::StateSet> writableStateSet = NULL;
|
||||||
|
if (!node->getStateSet())
|
||||||
|
writableStateSet = node->getOrCreateStateSet();
|
||||||
|
else
|
||||||
|
writableStateSet = osg::clone(node->getStateSet(), osg::CopyOp::SHALLOW_COPY);
|
||||||
|
writableStateSet->setTextureAttributeAndModes(texUnit, textures.front(), osg::StateAttribute::ON);
|
||||||
|
writableStateSet->addUniform(new osg::Uniform("envMapColor", glowColor));
|
||||||
|
|
||||||
|
mResourceSystem->getSceneManager()->recreateShaders(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Should not be here
|
// TODO: Should not be here
|
||||||
|
@ -1244,10 +1278,14 @@ namespace MWRender
|
||||||
stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||||
|
|
||||||
mObjectRoot->setStateSet(stateset);
|
mObjectRoot->setStateSet(stateset);
|
||||||
|
|
||||||
|
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mObjectRoot->setStateSet(NULL);
|
mObjectRoot->setStateSet(NULL);
|
||||||
|
|
||||||
|
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
setRenderBin();
|
setRenderBin();
|
||||||
|
@ -1315,9 +1353,24 @@ namespace MWRender
|
||||||
if (found != getNodeMap().end())
|
if (found != getNodeMap().end())
|
||||||
{
|
{
|
||||||
osg::MatrixTransform* node = found->second;
|
osg::MatrixTransform* node = found->second;
|
||||||
mHeadController = new RotateController(mObjectRoot.get());
|
|
||||||
node->addUpdateCallback(mHeadController);
|
bool foundKeyframeCtrl = false;
|
||||||
mActiveControllers.insert(std::make_pair(node, mHeadController));
|
osg::Callback* cb = node->getUpdateCallback();
|
||||||
|
while (cb)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<NifOsg::KeyframeController*>(cb))
|
||||||
|
{
|
||||||
|
foundKeyframeCtrl = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundKeyframeCtrl)
|
||||||
|
{
|
||||||
|
mHeadController = new RotateController(mObjectRoot.get());
|
||||||
|
node->addUpdateCallback(mHeadController);
|
||||||
|
mActiveControllers.insert(std::make_pair(node, mHeadController));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,10 +88,10 @@ namespace MWRender
|
||||||
const osg::Node* trackNode = mTrackingNode;
|
const osg::Node* trackNode = mTrackingNode;
|
||||||
if (!trackNode)
|
if (!trackNode)
|
||||||
return osg::Vec3d();
|
return osg::Vec3d();
|
||||||
osg::MatrixList mats = trackNode->getWorldMatrices();
|
osg::NodePathList nodepaths = trackNode->getParentalNodePaths();
|
||||||
if (!mats.size())
|
if (nodepaths.empty())
|
||||||
return osg::Vec3d();
|
return osg::Vec3d();
|
||||||
const osg::Matrix& worldMat = mats[0];
|
osg::Matrix worldMat = osg::computeLocalToWorld(nodepaths[0]);
|
||||||
|
|
||||||
osg::Vec3d position = worldMat.getTrans();
|
osg::Vec3d position = worldMat.getTrans();
|
||||||
if (!isFirstPerson())
|
if (!isFirstPerson())
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <osg/Fog>
|
||||||
#include <osg/Texture2D>
|
#include <osg/Texture2D>
|
||||||
#include <osg/Camera>
|
#include <osg/Camera>
|
||||||
#include <osg/PositionAttitudeTransform>
|
#include <osg/PositionAttitudeTransform>
|
||||||
|
@ -98,10 +99,16 @@ namespace MWRender
|
||||||
|
|
||||||
osg::ref_ptr<SceneUtil::LightManager> lightManager = new SceneUtil::LightManager;
|
osg::ref_ptr<SceneUtil::LightManager> lightManager = new SceneUtil::LightManager;
|
||||||
lightManager->setStartLight(1);
|
lightManager->setStartLight(1);
|
||||||
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
|
osg::ref_ptr<osg::StateSet> stateset = lightManager->getOrCreateStateSet();
|
||||||
stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);
|
stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);
|
||||||
stateset->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
|
stateset->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
|
||||||
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
|
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
|
||||||
|
// assign large value to effectively turn off fog
|
||||||
|
// shaders don't respect glDisable(GL_FOG)
|
||||||
|
osg::ref_ptr<osg::Fog> fog (new osg::Fog);
|
||||||
|
fog->setStart(10000000);
|
||||||
|
fog->setEnd(10000000);
|
||||||
|
stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
|
||||||
|
|
||||||
osg::ref_ptr<osg::LightModel> lightmodel = new osg::LightModel;
|
osg::ref_ptr<osg::LightModel> lightmodel = new osg::LightModel;
|
||||||
lightmodel->setAmbientIntensity(osg::Vec4(0.25, 0.25, 0.25, 1.0));
|
lightmodel->setAmbientIntensity(osg::Vec4(0.25, 0.25, 0.25, 1.0));
|
||||||
|
@ -123,7 +130,6 @@ namespace MWRender
|
||||||
|
|
||||||
lightSource->setStateSetModes(*stateset, osg::StateAttribute::ON);
|
lightSource->setStateSetModes(*stateset, osg::StateAttribute::ON);
|
||||||
|
|
||||||
lightManager->setStateSet(stateset);
|
|
||||||
lightManager->addChild(lightSource);
|
lightManager->addChild(lightSource);
|
||||||
|
|
||||||
mCamera->addChild(lightManager);
|
mCamera->addChild(lightManager);
|
||||||
|
@ -359,10 +365,10 @@ namespace MWRender
|
||||||
traverse(node, nv);
|
traverse(node, nv);
|
||||||
|
|
||||||
// Now update camera utilizing the updated head position
|
// Now update camera utilizing the updated head position
|
||||||
osg::MatrixList mats = mNodeToFollow->getWorldMatrices();
|
osg::NodePathList nodepaths = mNodeToFollow->getParentalNodePaths();
|
||||||
if (!mats.size())
|
if (nodepaths.empty())
|
||||||
return;
|
return;
|
||||||
osg::Matrix worldMat = mats[0];
|
osg::Matrix worldMat = osg::computeLocalToWorld(nodepaths[0]);
|
||||||
osg::Vec3 headOffset = worldMat.getTrans();
|
osg::Vec3 headOffset = worldMat.getTrans();
|
||||||
|
|
||||||
cam->setViewMatrixAsLookAt(headOffset + mPosOffset, headOffset + mLookAtOffset, osg::Vec3(0,0,1));
|
cam->setViewMatrixAsLookAt(headOffset + mPosOffset, headOffset + mLookAtOffset, osg::Vec3(0,0,1));
|
||||||
|
|
|
@ -402,7 +402,7 @@ namespace MWRender
|
||||||
|| bounds.mMinY > bounds.mMaxY)
|
|| bounds.mMinY > bounds.mMaxY)
|
||||||
throw std::runtime_error("invalid map bounds");
|
throw std::runtime_error("invalid map bounds");
|
||||||
|
|
||||||
if (!map.mImageData.size())
|
if (map.mImageData.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Files::IMemStream istream(&map.mImageData[0], map.mImageData.size());
|
Files::IMemStream istream(&map.mImageData[0], map.mImageData.size());
|
||||||
|
|
|
@ -180,7 +180,12 @@ osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, f
|
||||||
stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);
|
stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);
|
||||||
stateset->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
|
stateset->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
|
||||||
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
|
stateset->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
|
||||||
stateset->setMode(GL_FOG, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
|
// assign large value to effectively turn off fog
|
||||||
|
// shaders don't respect glDisable(GL_FOG)
|
||||||
|
osg::ref_ptr<osg::Fog> fog (new osg::Fog);
|
||||||
|
fog->setStart(10000000);
|
||||||
|
fog->setEnd(10000000);
|
||||||
|
stateset->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
|
||||||
|
|
||||||
osg::ref_ptr<osg::LightModel> lightmodel = new osg::LightModel;
|
osg::ref_ptr<osg::LightModel> lightmodel = new osg::LightModel;
|
||||||
lightmodel->setAmbientIntensity(osg::Vec4(0.3f, 0.3f, 0.3f, 1.f));
|
lightmodel->setAmbientIntensity(osg::Vec4(0.3f, 0.3f, 0.3f, 1.f));
|
||||||
|
@ -627,7 +632,7 @@ void LocalMap::MapSegment::initFogOfWar()
|
||||||
void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm)
|
void LocalMap::MapSegment::loadFogOfWar(const ESM::FogTexture &esm)
|
||||||
{
|
{
|
||||||
const std::vector<char>& data = esm.mImageData;
|
const std::vector<char>& data = esm.mImageData;
|
||||||
if (!data.size())
|
if (data.empty())
|
||||||
{
|
{
|
||||||
initFogOfWar();
|
initFogOfWar();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -168,6 +168,14 @@ namespace MWRender
|
||||||
, mFieldOfViewOverridden(false)
|
, mFieldOfViewOverridden(false)
|
||||||
{
|
{
|
||||||
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
|
resourceSystem->getSceneManager()->setParticleSystemMask(MWRender::Mask_ParticleSystem);
|
||||||
|
resourceSystem->getSceneManager()->setShaderPath(resourcePath + "/shaders");
|
||||||
|
resourceSystem->getSceneManager()->setForceShaders(Settings::Manager::getBool("force shaders", "Shaders"));
|
||||||
|
resourceSystem->getSceneManager()->setClampLighting(Settings::Manager::getBool("clamp lighting", "Shaders"));
|
||||||
|
resourceSystem->getSceneManager()->setForcePerPixelLighting(Settings::Manager::getBool("force per pixel lighting", "Shaders"));
|
||||||
|
resourceSystem->getSceneManager()->setAutoUseNormalMaps(Settings::Manager::getBool("auto use object normal maps", "Shaders"));
|
||||||
|
resourceSystem->getSceneManager()->setNormalMapPattern(Settings::Manager::getString("normal map pattern", "Shaders"));
|
||||||
|
resourceSystem->getSceneManager()->setAutoUseSpecularMaps(Settings::Manager::getBool("auto use object specular maps", "Shaders"));
|
||||||
|
resourceSystem->getSceneManager()->setSpecularMapPattern(Settings::Manager::getString("specular map pattern", "Shaders"));
|
||||||
|
|
||||||
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager;
|
osg::ref_ptr<SceneUtil::LightManager> sceneRoot = new SceneUtil::LightManager;
|
||||||
sceneRoot->setLightingMask(Mask_Lighting);
|
sceneRoot->setLightingMask(Mask_Lighting);
|
||||||
|
@ -189,7 +197,9 @@ namespace MWRender
|
||||||
mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath));
|
mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath));
|
||||||
|
|
||||||
mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(),
|
mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(),
|
||||||
new TerrainStorage(mResourceSystem->getVFS(), false), Mask_Terrain, mUnrefQueue.get()));
|
new TerrainStorage(mResourceSystem->getVFS(), Settings::Manager::getString("normal map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain normal maps", "Shaders"),
|
||||||
|
Settings::Manager::getString("terrain specular map pattern", "Shaders"), Settings::Manager::getBool("auto use terrain specular maps", "Shaders")),
|
||||||
|
Mask_Terrain, &mResourceSystem->getSceneManager()->getShaderManager(), mUnrefQueue.get()));
|
||||||
|
|
||||||
mCamera.reset(new Camera(mViewer->getCamera()));
|
mCamera.reset(new Camera(mViewer->getCamera()));
|
||||||
|
|
||||||
|
@ -333,7 +343,9 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
setAmbientColour(SceneUtil::colourFromRGB(cell->mAmbi.mAmbient));
|
setAmbientColour(SceneUtil::colourFromRGB(cell->mAmbi.mAmbient));
|
||||||
|
|
||||||
mSunLight->setDiffuse(SceneUtil::colourFromRGB(cell->mAmbi.mSunlight));
|
osg::Vec4f diffuse = SceneUtil::colourFromRGB(cell->mAmbi.mSunlight);
|
||||||
|
mSunLight->setDiffuse(diffuse);
|
||||||
|
mSunLight->setSpecular(diffuse);
|
||||||
mSunLight->setDirection(osg::Vec3f(1.f,-1.f,-1.f));
|
mSunLight->setDirection(osg::Vec3f(1.f,-1.f,-1.f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -836,16 +848,18 @@ namespace MWRender
|
||||||
|
|
||||||
void RenderingManager::updateTextureFiltering()
|
void RenderingManager::updateTextureFiltering()
|
||||||
{
|
{
|
||||||
if (mTerrain.get())
|
mViewer->stopThreading();
|
||||||
mTerrain->updateCache();
|
|
||||||
|
|
||||||
mResourceSystem->getSceneManager()->setFilterSettings(
|
mResourceSystem->getSceneManager()->setFilterSettings(
|
||||||
Settings::Manager::getString("texture mag filter", "General"),
|
Settings::Manager::getString("texture mag filter", "General"),
|
||||||
Settings::Manager::getString("texture min filter", "General"),
|
Settings::Manager::getString("texture min filter", "General"),
|
||||||
Settings::Manager::getString("texture mipmap", "General"),
|
Settings::Manager::getString("texture mipmap", "General"),
|
||||||
Settings::Manager::getInt("anisotropy", "General"),
|
Settings::Manager::getInt("anisotropy", "General")
|
||||||
mViewer
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
mTerrain->updateTextureFiltering();
|
||||||
|
|
||||||
|
mViewer->startThreading();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderingManager::updateAmbient()
|
void RenderingManager::updateAmbient()
|
||||||
|
|
|
@ -44,11 +44,11 @@ void RotateController::operator()(osg::Node *node, osg::NodeVisitor *nv)
|
||||||
osg::Quat RotateController::getWorldOrientation(osg::Node *node)
|
osg::Quat RotateController::getWorldOrientation(osg::Node *node)
|
||||||
{
|
{
|
||||||
// this could be optimized later, we just need the world orientation, not the full matrix
|
// this could be optimized later, we just need the world orientation, not the full matrix
|
||||||
osg::MatrixList worldMats = node->getWorldMatrices(mRelativeTo);
|
osg::NodePathList nodepaths = node->getParentalNodePaths(mRelativeTo);
|
||||||
osg::Quat worldOrient;
|
osg::Quat worldOrient;
|
||||||
if (!worldMats.empty())
|
if (!nodepaths.empty())
|
||||||
{
|
{
|
||||||
osg::Matrixf worldMat = worldMats[0];
|
osg::Matrixf worldMat = osg::computeLocalToWorld(nodepaths[0]);
|
||||||
worldOrient = worldMat.getRotate();
|
worldOrient = worldMat.getRotate();
|
||||||
}
|
}
|
||||||
return worldOrient;
|
return worldOrient;
|
||||||
|
|
|
@ -1125,6 +1125,9 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
|
||||||
, mSunEnabled(true)
|
, mSunEnabled(true)
|
||||||
{
|
{
|
||||||
osg::ref_ptr<CameraRelativeTransform> skyroot (new CameraRelativeTransform);
|
osg::ref_ptr<CameraRelativeTransform> skyroot (new CameraRelativeTransform);
|
||||||
|
// Assign empty program to specify we don't want shaders
|
||||||
|
// The shaders generated by the SceneManager can't handle everything we need
|
||||||
|
skyroot->getOrCreateStateSet()->setAttributeAndModes(new osg::Program(), osg::StateAttribute::OVERRIDE);
|
||||||
|
|
||||||
skyroot->setNodeMask(Mask_Sky);
|
skyroot->setNodeMask(Mask_Sky);
|
||||||
parentNode->addChild(skyroot);
|
parentNode->addChild(skyroot);
|
||||||
|
|
|
@ -9,21 +9,9 @@
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
|
|
||||||
TerrainStorage::TerrainStorage(const VFS::Manager* vfs, bool preload)
|
TerrainStorage::TerrainStorage(const VFS::Manager* vfs, const std::string& normalMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps)
|
||||||
: ESMTerrain::Storage(vfs)
|
: ESMTerrain::Storage(vfs, normalMapPattern, autoUseNormalMaps, specularMapPattern, autoUseSpecularMaps)
|
||||||
{
|
{
|
||||||
if (preload)
|
|
||||||
{
|
|
||||||
const MWWorld::ESMStore &esmStore =
|
|
||||||
MWBase::Environment::get().getWorld()->getStore();
|
|
||||||
|
|
||||||
MWWorld::Store<ESM::Land>::iterator it = esmStore.get<ESM::Land>().begin();
|
|
||||||
for (; it != esmStore.get<ESM::Land>().end(); ++it)
|
|
||||||
{
|
|
||||||
const ESM::Land* land = &*it;
|
|
||||||
land->loadData(ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY)
|
void TerrainStorage::getBounds(float& minX, float& maxX, float& minY, float& maxY)
|
||||||
|
|
|
@ -14,9 +14,7 @@ namespace MWRender
|
||||||
virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
|
virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
|
||||||
public:
|
public:
|
||||||
|
|
||||||
///@param preload Preload all Land records at startup? If using the multithreaded terrain component, this
|
TerrainStorage(const VFS::Manager* vfs, const std::string& normalMapPattern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false);
|
||||||
/// should be set to "true" in order to avoid race conditions.
|
|
||||||
TerrainStorage(const VFS::Manager* vfs, bool preload);
|
|
||||||
|
|
||||||
/// Get bounds of the whole terrain in cell units
|
/// Get bounds of the whole terrain in cell units
|
||||||
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
|
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);
|
||||||
|
|
|
@ -18,6 +18,7 @@ void overrideTexture(const std::string &texture, Resource::ResourceSystem *resou
|
||||||
osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D(resourceSystem->getImageManager()->getImage(correctedTexture));
|
osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D(resourceSystem->getImageManager()->getImage(correctedTexture));
|
||||||
tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||||
tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||||
|
tex->setName("diffuseMap");
|
||||||
|
|
||||||
osg::ref_ptr<osg::StateSet> stateset;
|
osg::ref_ptr<osg::StateSet> stateset;
|
||||||
if (node->getStateSet())
|
if (node->getStateSet())
|
||||||
|
|
|
@ -9,13 +9,11 @@
|
||||||
#include <osg/Geometry>
|
#include <osg/Geometry>
|
||||||
#include <osg/Material>
|
#include <osg/Material>
|
||||||
#include <osg/PositionAttitudeTransform>
|
#include <osg/PositionAttitudeTransform>
|
||||||
#include <osg/Depth>
|
|
||||||
#include <osg/ClipNode>
|
#include <osg/ClipNode>
|
||||||
#include <osg/MatrixTransform>
|
#include <osg/MatrixTransform>
|
||||||
#include <osg/FrontFace>
|
#include <osg/FrontFace>
|
||||||
#include <osg/Shader>
|
#include <osg/Shader>
|
||||||
#include <osg/GLExtensions>
|
#include <osg/GLExtensions>
|
||||||
#include <osg/UserDataContainer>
|
|
||||||
|
|
||||||
#include <osgDB/ReadFile>
|
#include <osgDB/ReadFile>
|
||||||
|
|
||||||
|
@ -306,7 +304,12 @@ public:
|
||||||
setUpdateCallback(new NoTraverseCallback);
|
setUpdateCallback(new NoTraverseCallback);
|
||||||
|
|
||||||
// No need for fog here, we are already applying fog on the water surface itself as well as underwater fog
|
// No need for fog here, we are already applying fog on the water surface itself as well as underwater fog
|
||||||
getOrCreateStateSet()->setMode(GL_FOG, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
|
// assign large value to effectively turn off fog
|
||||||
|
// shaders don't respect glDisable(GL_FOG)
|
||||||
|
osg::ref_ptr<osg::Fog> fog (new osg::Fog);
|
||||||
|
fog->setStart(10000000);
|
||||||
|
fog->setEnd(10000000);
|
||||||
|
getOrCreateStateSet()->setAttributeAndModes(fog, osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
|
||||||
|
|
||||||
mClipCullNode = new ClipCullNode;
|
mClipCullNode = new ClipCullNode;
|
||||||
addChild(mClipCullNode);
|
addChild(mClipCullNode);
|
||||||
|
@ -318,7 +321,6 @@ public:
|
||||||
mRefractionTexture->setInternalFormat(GL_RGB);
|
mRefractionTexture->setInternalFormat(GL_RGB);
|
||||||
mRefractionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
mRefractionTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||||
mRefractionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
mRefractionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||||
mRefractionTexture->getOrCreateUserDataContainer()->addDescription("dont_override_filter");
|
|
||||||
|
|
||||||
attach(osg::Camera::COLOR_BUFFER, mRefractionTexture);
|
attach(osg::Camera::COLOR_BUFFER, mRefractionTexture);
|
||||||
|
|
||||||
|
@ -330,7 +332,6 @@ public:
|
||||||
mRefractionDepthTexture->setSourceType(GL_UNSIGNED_INT);
|
mRefractionDepthTexture->setSourceType(GL_UNSIGNED_INT);
|
||||||
mRefractionDepthTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
mRefractionDepthTexture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||||
mRefractionDepthTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
mRefractionDepthTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||||
mRefractionDepthTexture->getOrCreateUserDataContainer()->addDescription("dont_override_filter");
|
|
||||||
|
|
||||||
attach(osg::Camera::DEPTH_BUFFER, mRefractionDepthTexture);
|
attach(osg::Camera::DEPTH_BUFFER, mRefractionDepthTexture);
|
||||||
}
|
}
|
||||||
|
@ -375,7 +376,9 @@ public:
|
||||||
setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
|
||||||
setReferenceFrame(osg::Camera::RELATIVE_RF);
|
setReferenceFrame(osg::Camera::RELATIVE_RF);
|
||||||
|
|
||||||
setCullMask(Mask_Effect|Mask_Scene|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Player|Mask_Lighting);
|
bool reflectActors = Settings::Manager::getBool("reflect actors", "Water");
|
||||||
|
|
||||||
|
setCullMask(Mask_Effect|Mask_Scene|Mask_Terrain|Mask_ParticleSystem|Mask_Sky|Mask_Player|Mask_Lighting|(reflectActors ? Mask_Actor : 0));
|
||||||
setNodeMask(Mask_RenderToTexture);
|
setNodeMask(Mask_RenderToTexture);
|
||||||
|
|
||||||
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
|
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
|
||||||
|
@ -391,7 +394,6 @@ public:
|
||||||
mReflectionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
mReflectionTexture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||||
mReflectionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
mReflectionTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||||
mReflectionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
mReflectionTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||||
mReflectionTexture->getOrCreateUserDataContainer()->addDescription("dont_override_filter");
|
|
||||||
|
|
||||||
attach(osg::Camera::COLOR_BUFFER, mReflectionTexture);
|
attach(osg::Camera::COLOR_BUFFER, mReflectionTexture);
|
||||||
|
|
||||||
|
@ -563,7 +565,7 @@ void Water::createSimpleWaterStateSet(osg::Node* node, float alpha)
|
||||||
textures.push_back(tex);
|
textures.push_back(tex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!textures.size())
|
if (textures.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float fps = mFallback->getFallbackFloat("Water_SurfaceFPS");
|
float fps = mFallback->getFallbackFloat("Water_SurfaceFPS");
|
||||||
|
|
|
@ -114,10 +114,10 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
|
||||||
osg::Node* weaponNode = getWeaponNode();
|
osg::Node* weaponNode = getWeaponNode();
|
||||||
if (!weaponNode)
|
if (!weaponNode)
|
||||||
return;
|
return;
|
||||||
osg::MatrixList mats = weaponNode->getWorldMatrices();
|
osg::NodePathList nodepaths = weaponNode->getParentalNodePaths();
|
||||||
if (mats.empty())
|
if (nodepaths.empty())
|
||||||
return;
|
return;
|
||||||
osg::Vec3f launchPos = mats[0].getTrans();
|
osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans();
|
||||||
|
|
||||||
float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat();
|
float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat();
|
||||||
float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat();
|
float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat();
|
||||||
|
@ -140,10 +140,10 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> ammoNode = mAmmunition->getNode();
|
osg::ref_ptr<osg::Node> ammoNode = mAmmunition->getNode();
|
||||||
osg::MatrixList mats = ammoNode->getWorldMatrices();
|
osg::NodePathList nodepaths = ammoNode->getParentalNodePaths();
|
||||||
if (mats.empty())
|
if (nodepaths.empty())
|
||||||
return;
|
return;
|
||||||
osg::Vec3f launchPos = mats[0].getTrans();
|
osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans();
|
||||||
|
|
||||||
float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat();
|
float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat();
|
||||||
float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat();
|
float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat();
|
||||||
|
|
|
@ -449,5 +449,7 @@ op 0x2000300: EnableLevelupMenu
|
||||||
op 0x2000301: ToggleScripts
|
op 0x2000301: ToggleScripts
|
||||||
op 0x2000302: Fixme
|
op 0x2000302: Fixme
|
||||||
op 0x2000303: Fixme, explicit
|
op 0x2000303: Fixme, explicit
|
||||||
|
op 0x2000304: Show
|
||||||
|
op 0x2000305: Show, explicit
|
||||||
|
|
||||||
opcodes 0x2000304-0x3ffffff unused
|
opcodes 0x2000304-0x3ffffff unused
|
||||||
|
|
|
@ -138,8 +138,7 @@ namespace MWScript
|
||||||
|
|
||||||
InterpreterContext::InterpreterContext (
|
InterpreterContext::InterpreterContext (
|
||||||
MWScript::Locals *locals, MWWorld::Ptr reference, const std::string& targetId)
|
MWScript::Locals *locals, MWWorld::Ptr reference, const std::string& targetId)
|
||||||
: mLocals (locals), mReference (reference),
|
: mLocals (locals), mReference (reference), mTargetId (targetId)
|
||||||
mActivationHandled (false), mTargetId (targetId)
|
|
||||||
{
|
{
|
||||||
// If we run on a reference (local script, dialogue script or console with object
|
// If we run on a reference (local script, dialogue script or console with object
|
||||||
// selected), store the ID of that reference store it so it can be inherited by
|
// selected), store the ID of that reference store it so it can be inherited by
|
||||||
|
@ -477,37 +476,10 @@ namespace MWScript
|
||||||
return static_cast<float>(std::sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2]));
|
return static_cast<float>(std::sqrt(diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InterpreterContext::hasBeenActivated (const MWWorld::Ptr& ptr)
|
|
||||||
{
|
|
||||||
if (!mActivated.isEmpty() && mActivated==ptr)
|
|
||||||
{
|
|
||||||
mActivationHandled = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InterpreterContext::hasActivationBeenHandled() const
|
|
||||||
{
|
|
||||||
return mActivationHandled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterpreterContext::activate (const MWWorld::Ptr& ptr)
|
|
||||||
{
|
|
||||||
mActivated = ptr;
|
|
||||||
mActivationHandled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterpreterContext::executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor)
|
void InterpreterContext::executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor)
|
||||||
{
|
{
|
||||||
boost::shared_ptr<MWWorld::Action> action = (ptr.getClass().activate(ptr, actor));
|
boost::shared_ptr<MWWorld::Action> action = (ptr.getClass().activate(ptr, actor));
|
||||||
action->execute (actor);
|
action->execute (actor);
|
||||||
if (mActivated == ptr)
|
|
||||||
{
|
|
||||||
mActivationHandled = true;
|
|
||||||
mActivated = MWWorld::Ptr();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float InterpreterContext::getSecondsPassed() const
|
float InterpreterContext::getSecondsPassed() const
|
||||||
|
|
|
@ -27,9 +27,6 @@ namespace MWScript
|
||||||
Locals *mLocals;
|
Locals *mLocals;
|
||||||
mutable MWWorld::Ptr mReference;
|
mutable MWWorld::Ptr mReference;
|
||||||
|
|
||||||
MWWorld::Ptr mActivated;
|
|
||||||
bool mActivationHandled;
|
|
||||||
|
|
||||||
std::string mTargetId;
|
std::string mTargetId;
|
||||||
|
|
||||||
/// If \a id is empty, a reference the script is run from is returned or in case
|
/// If \a id is empty, a reference the script is run from is returned or in case
|
||||||
|
@ -131,16 +128,6 @@ namespace MWScript
|
||||||
virtual float getDistance (const std::string& name, const std::string& id = "") const;
|
virtual float getDistance (const std::string& name, const std::string& id = "") const;
|
||||||
///< @note if \a id is empty, assumes an implicit reference
|
///< @note if \a id is empty, assumes an implicit reference
|
||||||
|
|
||||||
bool hasBeenActivated (const MWWorld::Ptr& ptr);
|
|
||||||
///< \attention Calling this function for the right reference will mark the action as
|
|
||||||
/// been handled.
|
|
||||||
|
|
||||||
bool hasActivationBeenHandled() const;
|
|
||||||
|
|
||||||
void activate (const MWWorld::Ptr& ptr);
|
|
||||||
///< Store reference acted upon. The actual execution of the action does not
|
|
||||||
/// take place here.
|
|
||||||
|
|
||||||
void executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor);
|
void executeActivation(MWWorld::Ptr ptr, MWWorld::Ptr actor);
|
||||||
///< Execute the activation action for this ptr. If ptr is mActivated, mark activation as handled.
|
///< Execute the activation action for this ptr. If ptr is mActivated, mark activation as handled.
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,32 @@ namespace MWScript
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float Locals::getFloatVar(const std::string &script, const std::string &var)
|
||||||
|
{
|
||||||
|
ensure (script);
|
||||||
|
|
||||||
|
const Compiler::Locals& locals = MWBase::Environment::get().getScriptManager()->getLocals(script);
|
||||||
|
int index = locals.getIndex(var);
|
||||||
|
char type = locals.getType(var);
|
||||||
|
if(index != -1)
|
||||||
|
{
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case 's':
|
||||||
|
return mShorts.at (index);
|
||||||
|
|
||||||
|
case 'l':
|
||||||
|
return mLongs.at (index);
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
return mFloats.at(index);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool Locals::setVarByInt(const std::string& script, const std::string& var, int val)
|
bool Locals::setVarByInt(const std::string& script, const std::string& var, int val)
|
||||||
{
|
{
|
||||||
ensure (script);
|
ensure (script);
|
||||||
|
|
|
@ -51,6 +51,12 @@ namespace MWScript
|
||||||
/// \note Locals will be automatically configured first, if necessary
|
/// \note Locals will be automatically configured first, if necessary
|
||||||
int getIntVar (const std::string& script, const std::string& var);
|
int getIntVar (const std::string& script, const std::string& var);
|
||||||
|
|
||||||
|
/// if var does not exist, returns 0
|
||||||
|
/// @note var needs to be in lowercase
|
||||||
|
///
|
||||||
|
/// \note Locals will be automatically configured first, if necessary
|
||||||
|
float getFloatVar (const std::string& script, const std::string& var);
|
||||||
|
|
||||||
/// \note If locals have not been configured yet, no data is written.
|
/// \note If locals have not been configured yet, no data is written.
|
||||||
///
|
///
|
||||||
/// \return Locals written?
|
/// \return Locals written?
|
||||||
|
|
|
@ -142,7 +142,7 @@ namespace MWScript
|
||||||
|
|
||||||
MWWorld::Ptr ptr = context.getReference();
|
MWWorld::Ptr ptr = context.getReference();
|
||||||
|
|
||||||
runtime.push (context.hasBeenActivated (ptr));
|
runtime.push (ptr.getRefData().onActivate());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -158,7 +158,8 @@ namespace MWScript
|
||||||
|
|
||||||
MWWorld::Ptr ptr = R()(runtime);
|
MWWorld::Ptr ptr = R()(runtime);
|
||||||
|
|
||||||
context.executeActivation(ptr, MWMechanics::getPlayer());
|
if (ptr.getRefData().activateByScript())
|
||||||
|
context.executeActivation(ptr, MWMechanics::getPlayer());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -848,6 +849,70 @@ namespace MWScript
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <class R>
|
||||||
|
class OpShow : public Interpreter::Opcode0
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
|
{
|
||||||
|
MWWorld::Ptr ptr = R()(runtime, false);
|
||||||
|
std::string var = runtime.getStringLiteral(runtime[0].mInteger);
|
||||||
|
runtime.pop();
|
||||||
|
|
||||||
|
std::stringstream output;
|
||||||
|
|
||||||
|
if (!ptr.isEmpty())
|
||||||
|
{
|
||||||
|
const std::string& script = ptr.getClass().getScript(ptr);
|
||||||
|
if (script.empty())
|
||||||
|
{
|
||||||
|
output << ptr.getCellRef().getRefId() << " has no script " << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const Compiler::Locals& locals =
|
||||||
|
MWBase::Environment::get().getScriptManager()->getLocals(script);
|
||||||
|
char type = locals.getType(var);
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case 'l':
|
||||||
|
case 's':
|
||||||
|
output << ptr.getCellRef().getRefId() << "." << var << ": " << ptr.getRefData().getLocals().getIntVar(script, var);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
output << ptr.getCellRef().getRefId() << "." << var << ": " << ptr.getRefData().getLocals().getFloatVar(script, var);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
output << "unknown local '" << var << "' for '" << ptr.getCellRef().getRefId() << "'";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||||
|
char type = world->getGlobalVariableType (var);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case 's':
|
||||||
|
output << runtime.getContext().getGlobalShort (var);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
output << runtime.getContext().getGlobalLong (var);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
output << runtime.getContext().getGlobalFloat (var);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
output << "unknown global variable";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runtime.getContext().report(output.str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <class R>
|
template <class R>
|
||||||
class OpShowVars : public Interpreter::Opcode0
|
class OpShowVars : public Interpreter::Opcode0
|
||||||
{
|
{
|
||||||
|
@ -1265,6 +1330,8 @@ namespace MWScript
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeEnableTeleporting, new OpEnableTeleporting<true>);
|
interpreter.installSegment5 (Compiler::Misc::opcodeEnableTeleporting, new OpEnableTeleporting<true>);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeShowVars, new OpShowVars<ImplicitRef>);
|
interpreter.installSegment5 (Compiler::Misc::opcodeShowVars, new OpShowVars<ImplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeShowVarsExplicit, new OpShowVars<ExplicitRef>);
|
interpreter.installSegment5 (Compiler::Misc::opcodeShowVarsExplicit, new OpShowVars<ExplicitRef>);
|
||||||
|
interpreter.installSegment5 (Compiler::Misc::opcodeShow, new OpShow<ImplicitRef>);
|
||||||
|
interpreter.installSegment5 (Compiler::Misc::opcodeShowExplicit, new OpShow<ExplicitRef>);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeToggleGodMode, new OpToggleGodMode);
|
interpreter.installSegment5 (Compiler::Misc::opcodeToggleGodMode, new OpToggleGodMode);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeToggleScripts, new OpToggleScripts);
|
interpreter.installSegment5 (Compiler::Misc::opcodeToggleScripts, new OpToggleScripts);
|
||||||
interpreter.installSegment5 (Compiler::Misc::opcodeDisableLevitation, new OpEnableLevitation<false>);
|
interpreter.installSegment5 (Compiler::Misc::opcodeDisableLevitation, new OpEnableLevitation<false>);
|
||||||
|
|
|
@ -318,8 +318,8 @@ namespace MWScript
|
||||||
ptr = MWBase::Environment::get().getWorld()->moveObject(ptr,store,x,y,z);
|
ptr = MWBase::Environment::get().getWorld()->moveObject(ptr,store,x,y,z);
|
||||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr);
|
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr);
|
||||||
|
|
||||||
float ax = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[0]);
|
float ax = ptr.getRefData().getPosition().rot[0];
|
||||||
float ay = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[1]);
|
float ay = ptr.getRefData().getPosition().rot[1];
|
||||||
// Note that you must specify ZRot in minutes (1 degree = 60 minutes; north = 0, east = 5400, south = 10800, west = 16200)
|
// Note that you must specify ZRot in minutes (1 degree = 60 minutes; north = 0, east = 5400, south = 10800, west = 16200)
|
||||||
// except for when you position the player, then degrees must be used.
|
// except for when you position the player, then degrees must be used.
|
||||||
// See "Morrowind Scripting for Dummies (9th Edition)" pages 50 and 54 for reference.
|
// See "Morrowind Scripting for Dummies (9th Edition)" pages 50 and 54 for reference.
|
||||||
|
@ -374,14 +374,14 @@ namespace MWScript
|
||||||
}
|
}
|
||||||
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr);
|
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr);
|
||||||
|
|
||||||
float ax = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[0]);
|
float ax = ptr.getRefData().getPosition().rot[0];
|
||||||
float ay = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[1]);
|
float ay = ptr.getRefData().getPosition().rot[1];
|
||||||
// Note that you must specify ZRot in minutes (1 degree = 60 minutes; north = 0, east = 5400, south = 10800, west = 16200)
|
// Note that you must specify ZRot in minutes (1 degree = 60 minutes; north = 0, east = 5400, south = 10800, west = 16200)
|
||||||
// except for when you position the player, then degrees must be used.
|
// except for when you position the player, then degrees must be used.
|
||||||
// See "Morrowind Scripting for Dummies (9th Edition)" pages 50 and 54 for reference.
|
// See "Morrowind Scripting for Dummies (9th Edition)" pages 50 and 54 for reference.
|
||||||
if(ptr != MWMechanics::getPlayer())
|
if(ptr != MWMechanics::getPlayer())
|
||||||
zRot = zRot/60.0f;
|
zRot = zRot/60.0f;
|
||||||
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,zRot);
|
MWBase::Environment::get().getWorld()->rotateObject(ptr,ax,ay,osg::DegreesToRadians(zRot));
|
||||||
ptr.getClass().adjustPosition(ptr, false);
|
ptr.getClass().adjustPosition(ptr, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -434,7 +434,7 @@ namespace MWScript
|
||||||
pos.rot[2] = osg::DegreesToRadians(zRotDegrees);
|
pos.rot[2] = osg::DegreesToRadians(zRotDegrees);
|
||||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID);
|
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID);
|
||||||
ref.getPtr().getCellRef().setPosition(pos);
|
ref.getPtr().getCellRef().setPosition(pos);
|
||||||
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos);
|
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(ref.getPtr(),store,pos);
|
||||||
placed.getClass().adjustPosition(placed, true);
|
placed.getClass().adjustPosition(placed, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -482,7 +482,7 @@ namespace MWScript
|
||||||
pos.rot[2] = osg::DegreesToRadians(zRotDegrees);
|
pos.rot[2] = osg::DegreesToRadians(zRotDegrees);
|
||||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID);
|
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID);
|
||||||
ref.getPtr().getCellRef().setPosition(pos);
|
ref.getPtr().getCellRef().setPosition(pos);
|
||||||
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,pos);
|
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->placeObject(ref.getPtr(),store,pos);
|
||||||
placed.getClass().adjustPosition(placed, true);
|
placed.getClass().adjustPosition(placed, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -516,38 +516,10 @@ namespace MWScript
|
||||||
|
|
||||||
for (int i=0; i<count; ++i)
|
for (int i=0; i<count; ++i)
|
||||||
{
|
{
|
||||||
ESM::Position ipos = actor.getRefData().getPosition();
|
|
||||||
osg::Vec3f pos(ipos.asVec3());
|
|
||||||
osg::Quat rot(ipos.rot[2], osg::Vec3f(0,0,-1));
|
|
||||||
if(direction == 0) pos = pos + (rot * osg::Vec3f(0,1,0)) * distance;
|
|
||||||
else if(direction == 1) pos = pos - (rot * osg::Vec3f(0,1,0)) * distance;
|
|
||||||
else if(direction == 2) pos = pos - (rot * osg::Vec3f(1,0,0)) * distance;
|
|
||||||
else if(direction == 3) pos = pos + (rot * osg::Vec3f(1,0,0)) * distance;
|
|
||||||
else throw std::runtime_error ("direction must be 0,1,2 or 3");
|
|
||||||
|
|
||||||
ipos.pos[0] = pos.x();
|
|
||||||
ipos.pos[1] = pos.y();
|
|
||||||
ipos.pos[2] = pos.z();
|
|
||||||
|
|
||||||
if (actor.getClass().isActor())
|
|
||||||
{
|
|
||||||
// TODO: should this depend on the 'direction' parameter?
|
|
||||||
ipos.rot[0] = 0;
|
|
||||||
ipos.rot[1] = 0;
|
|
||||||
ipos.rot[2] = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ipos.rot[0] = actor.getRefData().getPosition().rot[0];
|
|
||||||
ipos.rot[1] = actor.getRefData().getPosition().rot[1];
|
|
||||||
ipos.rot[2] = actor.getRefData().getPosition().rot[2];
|
|
||||||
}
|
|
||||||
// create item
|
// create item
|
||||||
MWWorld::CellStore* store = actor.getCell();
|
|
||||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), itemID, 1);
|
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), itemID, 1);
|
||||||
ref.getPtr().getCellRef().setPosition(ipos);
|
|
||||||
|
|
||||||
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos);
|
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), actor, actor.getCell(), direction, distance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -366,7 +366,7 @@ namespace MWSound
|
||||||
else
|
else
|
||||||
filelist = mMusicFiles[mCurrentPlaylist];
|
filelist = mMusicFiles[mCurrentPlaylist];
|
||||||
|
|
||||||
if(!filelist.size())
|
if(filelist.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int i = Misc::Rng::rollDice(filelist.size());
|
int i = Misc::Rng::rollDice(filelist.size());
|
||||||
|
|
|
@ -493,7 +493,8 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
|
||||||
// but some mods may be using it as a reload detector.
|
// but some mods may be using it as a reload detector.
|
||||||
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();
|
MWBase::Environment::get().getScriptManager()->getGlobalScripts().addStartup();
|
||||||
|
|
||||||
// Do not trigger erroneous cellChanged events
|
// Since we passed "changeEvent=false" to changeCell, we shouldn't have triggered the cell change flag.
|
||||||
|
// But make sure the flag is cleared anyway in case it was set from an earlier game.
|
||||||
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
|
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
|
|
|
@ -166,6 +166,8 @@ namespace MWWorld
|
||||||
, mBulletShapeManager(bulletShapeManager)
|
, mBulletShapeManager(bulletShapeManager)
|
||||||
, mTerrain(terrain)
|
, mTerrain(terrain)
|
||||||
, mExpiryDelay(0.0)
|
, mExpiryDelay(0.0)
|
||||||
|
, mMinCacheSize(0)
|
||||||
|
, mMaxCacheSize(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,6 +199,23 @@ namespace MWWorld
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (mPreloadCells.size() >= mMaxCacheSize)
|
||||||
|
{
|
||||||
|
// throw out oldest cell to make room
|
||||||
|
PreloadMap::iterator oldestCell = mPreloadCells.begin();
|
||||||
|
double oldestTimestamp = DBL_MAX;
|
||||||
|
for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end(); ++it)
|
||||||
|
{
|
||||||
|
if (it->second.mTimeStamp < oldestTimestamp)
|
||||||
|
{
|
||||||
|
oldestTimestamp = it->second.mTimeStamp;
|
||||||
|
oldestCell = it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mPreloadCells.erase(oldestCell);
|
||||||
|
}
|
||||||
|
|
||||||
osg::ref_ptr<PreloadItem> item (new PreloadItem(cell, mResourceSystem->getSceneManager(), mBulletShapeManager, mResourceSystem->getKeyframeManager(), mTerrain));
|
osg::ref_ptr<PreloadItem> item (new PreloadItem(cell, mResourceSystem->getSceneManager(), mBulletShapeManager, mResourceSystem->getKeyframeManager(), mTerrain));
|
||||||
mWorkQueue->addWorkItem(item);
|
mWorkQueue->addWorkItem(item);
|
||||||
|
|
||||||
|
@ -210,11 +229,9 @@ namespace MWWorld
|
||||||
|
|
||||||
void CellPreloader::updateCache(double timestamp)
|
void CellPreloader::updateCache(double timestamp)
|
||||||
{
|
{
|
||||||
// TODO: add settings for a minimum/maximum size of the cache
|
|
||||||
|
|
||||||
for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();)
|
for (PreloadMap::iterator it = mPreloadCells.begin(); it != mPreloadCells.end();)
|
||||||
{
|
{
|
||||||
if (it->second.mTimeStamp < timestamp - mExpiryDelay)
|
if (mPreloadCells.size() >= mMinCacheSize && it->second.mTimeStamp < timestamp - mExpiryDelay)
|
||||||
mPreloadCells.erase(it++);
|
mPreloadCells.erase(it++);
|
||||||
else
|
else
|
||||||
++it;
|
++it;
|
||||||
|
@ -229,6 +246,16 @@ namespace MWWorld
|
||||||
mExpiryDelay = expiryDelay;
|
mExpiryDelay = expiryDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CellPreloader::setMinCacheSize(unsigned int num)
|
||||||
|
{
|
||||||
|
mMinCacheSize = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CellPreloader::setMaxCacheSize(unsigned int num)
|
||||||
|
{
|
||||||
|
mMaxCacheSize = num;
|
||||||
|
}
|
||||||
|
|
||||||
void CellPreloader::setWorkQueue(osg::ref_ptr<SceneUtil::WorkQueue> workQueue)
|
void CellPreloader::setWorkQueue(osg::ref_ptr<SceneUtil::WorkQueue> workQueue)
|
||||||
{
|
{
|
||||||
mWorkQueue = workQueue;
|
mWorkQueue = workQueue;
|
||||||
|
|
|
@ -38,6 +38,12 @@ namespace MWWorld
|
||||||
/// How long to keep a preloaded cell in cache after it's no longer requested.
|
/// How long to keep a preloaded cell in cache after it's no longer requested.
|
||||||
void setExpiryDelay(double expiryDelay);
|
void setExpiryDelay(double expiryDelay);
|
||||||
|
|
||||||
|
/// The minimum number of preloaded cells before unused cells get thrown out.
|
||||||
|
void setMinCacheSize(unsigned int num);
|
||||||
|
|
||||||
|
/// The maximum number of preloaded cells.
|
||||||
|
void setMaxCacheSize(unsigned int num);
|
||||||
|
|
||||||
void setWorkQueue(osg::ref_ptr<SceneUtil::WorkQueue> workQueue);
|
void setWorkQueue(osg::ref_ptr<SceneUtil::WorkQueue> workQueue);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -46,6 +52,8 @@ namespace MWWorld
|
||||||
Terrain::World* mTerrain;
|
Terrain::World* mTerrain;
|
||||||
osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;
|
osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;
|
||||||
double mExpiryDelay;
|
double mExpiryDelay;
|
||||||
|
unsigned int mMinCacheSize;
|
||||||
|
unsigned int mMaxCacheSize;
|
||||||
|
|
||||||
struct PreloadEntry
|
struct PreloadEntry
|
||||||
{
|
{
|
||||||
|
|
|
@ -889,11 +889,19 @@ namespace MWWorld
|
||||||
return mFogState.get();
|
return mFogState.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clearCorpse(const MWWorld::Ptr& ptr)
|
||||||
|
{
|
||||||
|
const MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);
|
||||||
|
static const float fCorpseClearDelay = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fCorpseClearDelay")->getFloat();
|
||||||
|
if (creatureStats.isDead() && !ptr.getClass().isPersistent(ptr) && creatureStats.getTimeOfDeath() + fCorpseClearDelay <= MWBase::Environment::get().getWorld()->getTimeStamp())
|
||||||
|
MWBase::Environment::get().getWorld()->deleteObject(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
void CellStore::respawn()
|
void CellStore::respawn()
|
||||||
{
|
{
|
||||||
if (mState == State_Loaded)
|
if (mState == State_Loaded)
|
||||||
{
|
{
|
||||||
static int iMonthsToRespawn = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iMonthsToRespawn")->getInt();
|
static const int iMonthsToRespawn = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iMonthsToRespawn")->getInt();
|
||||||
if (MWBase::Environment::get().getWorld()->getTimeStamp() - mLastRespawn > 24*30*iMonthsToRespawn)
|
if (MWBase::Environment::get().getWorld()->getTimeStamp() - mLastRespawn > 24*30*iMonthsToRespawn)
|
||||||
{
|
{
|
||||||
mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp();
|
mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp();
|
||||||
|
@ -902,21 +910,25 @@ namespace MWWorld
|
||||||
Ptr ptr (&*it, this);
|
Ptr ptr (&*it, this);
|
||||||
ptr.getClass().respawn(ptr);
|
ptr.getClass().respawn(ptr);
|
||||||
}
|
}
|
||||||
for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it)
|
}
|
||||||
{
|
|
||||||
Ptr ptr (&*it, this);
|
for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it)
|
||||||
ptr.getClass().respawn(ptr);
|
{
|
||||||
}
|
Ptr ptr (&*it, this);
|
||||||
for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)
|
clearCorpse(ptr);
|
||||||
{
|
ptr.getClass().respawn(ptr);
|
||||||
Ptr ptr (&*it, this);
|
}
|
||||||
ptr.getClass().respawn(ptr);
|
for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)
|
||||||
}
|
{
|
||||||
for (CellRefList<ESM::CreatureLevList>::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it)
|
Ptr ptr (&*it, this);
|
||||||
{
|
clearCorpse(ptr);
|
||||||
Ptr ptr (&*it, this);
|
ptr.getClass().respawn(ptr);
|
||||||
ptr.getClass().respawn(ptr);
|
}
|
||||||
}
|
for (CellRefList<ESM::CreatureLevList>::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it)
|
||||||
|
{
|
||||||
|
Ptr ptr (&*it, this);
|
||||||
|
// no need to clearCorpse, handled as part of mCreatures
|
||||||
|
ptr.getClass().respawn(ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,11 +66,6 @@ MWWorld::LocalScripts::LocalScripts (const MWWorld::ESMStore& store) : mStore (s
|
||||||
mIter = mScripts.end();
|
mIter = mScripts.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MWWorld::LocalScripts::setIgnore (const ConstPtr& ptr)
|
|
||||||
{
|
|
||||||
mIgnore = ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MWWorld::LocalScripts::startIteration()
|
void MWWorld::LocalScripts::startIteration()
|
||||||
{
|
{
|
||||||
mIter = mScripts.begin();
|
mIter = mScripts.begin();
|
||||||
|
@ -81,11 +76,8 @@ bool MWWorld::LocalScripts::getNext(std::pair<std::string, Ptr>& script)
|
||||||
while (mIter!=mScripts.end())
|
while (mIter!=mScripts.end())
|
||||||
{
|
{
|
||||||
std::list<std::pair<std::string, Ptr> >::iterator iter = mIter++;
|
std::list<std::pair<std::string, Ptr> >::iterator iter = mIter++;
|
||||||
if (mIgnore.isEmpty() || iter->second!=mIgnore)
|
script = *iter;
|
||||||
{
|
return true;
|
||||||
script = *iter;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,17 +17,12 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
std::list<std::pair<std::string, Ptr> > mScripts;
|
std::list<std::pair<std::string, Ptr> > mScripts;
|
||||||
std::list<std::pair<std::string, Ptr> >::iterator mIter;
|
std::list<std::pair<std::string, Ptr> >::iterator mIter;
|
||||||
MWWorld::ConstPtr mIgnore;
|
|
||||||
const MWWorld::ESMStore& mStore;
|
const MWWorld::ESMStore& mStore;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
LocalScripts (const MWWorld::ESMStore& store);
|
LocalScripts (const MWWorld::ESMStore& store);
|
||||||
|
|
||||||
void setIgnore (const ConstPtr& ptr);
|
|
||||||
///< Mark a single reference for ignoring during iteration over local scripts (will revoke
|
|
||||||
/// previous ignores).
|
|
||||||
|
|
||||||
void startIteration();
|
void startIteration();
|
||||||
///< Set the iterator to the begin of the script list.
|
///< Set the iterator to the begin of the script list.
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,18 @@
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
enum RefDataFlags
|
||||||
|
{
|
||||||
|
Flag_SuppressActivate = 1, // If set, activation will be suppressed and redirected to the OnActivate flag, which can then be handled by a script.
|
||||||
|
Flag_OnActivate = 2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
|
||||||
void RefData::copy (const RefData& refData)
|
void RefData::copy (const RefData& refData)
|
||||||
{
|
{
|
||||||
mBaseNode = refData.mBaseNode;
|
mBaseNode = refData.mBaseNode;
|
||||||
|
@ -19,6 +29,7 @@ namespace MWWorld
|
||||||
mPosition = refData.mPosition;
|
mPosition = refData.mPosition;
|
||||||
mChanged = refData.mChanged;
|
mChanged = refData.mChanged;
|
||||||
mDeletedByContentFile = refData.mDeletedByContentFile;
|
mDeletedByContentFile = refData.mDeletedByContentFile;
|
||||||
|
mFlags = refData.mFlags;
|
||||||
|
|
||||||
mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0;
|
mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +43,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
RefData::RefData()
|
RefData::RefData()
|
||||||
: mBaseNode(0), mDeletedByContentFile(false), mEnabled (true), mCount (1), mCustomData (0), mChanged(false)
|
: mBaseNode(0), mDeletedByContentFile(false), mEnabled (true), mCount (1), mCustomData (0), mChanged(false), mFlags(0)
|
||||||
{
|
{
|
||||||
for (int i=0; i<3; ++i)
|
for (int i=0; i<3; ++i)
|
||||||
{
|
{
|
||||||
|
@ -45,7 +56,7 @@ namespace MWWorld
|
||||||
: mBaseNode(0), mDeletedByContentFile(false), mEnabled (true),
|
: mBaseNode(0), mDeletedByContentFile(false), mEnabled (true),
|
||||||
mCount (1), mPosition (cellRef.mPos),
|
mCount (1), mPosition (cellRef.mPos),
|
||||||
mCustomData (0),
|
mCustomData (0),
|
||||||
mChanged(false) // Loading from ESM/ESP files -> assume unchanged
|
mChanged(false), mFlags(0) // Loading from ESM/ESP files -> assume unchanged
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,8 +66,12 @@ namespace MWWorld
|
||||||
mCount (objectState.mCount),
|
mCount (objectState.mCount),
|
||||||
mPosition (objectState.mPosition),
|
mPosition (objectState.mPosition),
|
||||||
mCustomData (0),
|
mCustomData (0),
|
||||||
mChanged(true) // Loading from a savegame -> assume changed
|
mChanged(true), mFlags(objectState.mFlags) // Loading from a savegame -> assume changed
|
||||||
{
|
{
|
||||||
|
// "Note that the ActivationFlag_UseEnabled is saved to the reference,
|
||||||
|
// which will result in permanently suppressed activation if the reference script is removed.
|
||||||
|
// This occurred when removing the animated containers mod, and the fix in MCP is to reset UseEnabled to true on loading a game."
|
||||||
|
mFlags &= (~Flag_SuppressActivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
RefData::RefData (const RefData& refData)
|
RefData::RefData (const RefData& refData)
|
||||||
|
@ -80,6 +95,7 @@ namespace MWWorld
|
||||||
objectState.mEnabled = mEnabled;
|
objectState.mEnabled = mEnabled;
|
||||||
objectState.mCount = mCount;
|
objectState.mCount = mCount;
|
||||||
objectState.mPosition = mPosition;
|
objectState.mPosition = mPosition;
|
||||||
|
objectState.mFlags = mFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefData& RefData::operator= (const RefData& refData)
|
RefData& RefData::operator= (const RefData& refData)
|
||||||
|
@ -219,4 +235,38 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
return mChanged;
|
return mChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RefData::activate()
|
||||||
|
{
|
||||||
|
if (!(mFlags & Flag_SuppressActivate))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mFlags |= Flag_OnActivate;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RefData::onActivate()
|
||||||
|
{
|
||||||
|
mFlags |= Flag_SuppressActivate;
|
||||||
|
|
||||||
|
if (mFlags & Flag_OnActivate)
|
||||||
|
{
|
||||||
|
mFlags &= (~Flag_OnActivate);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RefData::activateByScript()
|
||||||
|
{
|
||||||
|
if (mFlags & Flag_SuppressActivate)
|
||||||
|
{
|
||||||
|
mFlags &= (~Flag_SuppressActivate);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,8 @@ namespace MWWorld
|
||||||
|
|
||||||
bool mChanged;
|
bool mChanged;
|
||||||
|
|
||||||
|
unsigned int mFlags;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
RefData();
|
RefData();
|
||||||
|
@ -122,6 +124,12 @@ namespace MWWorld
|
||||||
|
|
||||||
const CustomData *getCustomData() const;
|
const CustomData *getCustomData() const;
|
||||||
|
|
||||||
|
bool activate();
|
||||||
|
|
||||||
|
bool onActivate();
|
||||||
|
|
||||||
|
bool activateByScript();
|
||||||
|
|
||||||
bool hasChanged() const;
|
bool hasChanged() const;
|
||||||
///< Has this RefData changed since it was originally loaded?
|
///< Has this RefData changed since it was originally loaded?
|
||||||
};
|
};
|
||||||
|
|
|
@ -240,7 +240,7 @@ namespace MWWorld
|
||||||
mActiveCells.erase(*iter);
|
mActiveCells.erase(*iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::loadCell (CellStore *cell, Loading::Listener* loadingListener)
|
void Scene::loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn)
|
||||||
{
|
{
|
||||||
std::pair<CellStoreCollection::iterator, bool> result = mActiveCells.insert(cell);
|
std::pair<CellStoreCollection::iterator, bool> result = mActiveCells.insert(cell);
|
||||||
|
|
||||||
|
@ -268,14 +268,22 @@ namespace MWWorld
|
||||||
mPhysics->addHeightField (data->mHeights, cell->getCell()->getGridX(), cell->getCell()->getGridY(),
|
mPhysics->addHeightField (data->mHeights, cell->getCell()->getGridX(), cell->getCell()->getGridY(),
|
||||||
worldsize / (verts-1), verts);
|
worldsize / (verts-1), verts);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static std::vector<float> defaultHeight;
|
||||||
|
defaultHeight.resize(verts*verts, ESM::Land::DEFAULT_HEIGHT);
|
||||||
|
mPhysics->addHeightField (&defaultHeight[0], cell->getCell()->getGridX(), cell->getCell()->getGridY(),
|
||||||
|
worldsize / (verts-1), verts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cell->respawn();
|
|
||||||
|
|
||||||
// register local scripts
|
// register local scripts
|
||||||
// do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice
|
// do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice
|
||||||
MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell);
|
MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell);
|
||||||
|
|
||||||
|
if (respawn)
|
||||||
|
cell->respawn();
|
||||||
|
|
||||||
// ... then references. This is important for adjustPosition to work correctly.
|
// ... then references. This is important for adjustPosition to work correctly.
|
||||||
/// \todo rescale depending on the state of a new GMST
|
/// \todo rescale depending on the state of a new GMST
|
||||||
insertCell (*cell, true, loadingListener);
|
insertCell (*cell, true, loadingListener);
|
||||||
|
@ -329,7 +337,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::changeCellGrid (int X, int Y)
|
void Scene::changeCellGrid (int X, int Y, bool changeEvent)
|
||||||
{
|
{
|
||||||
Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
||||||
Loading::ScopedLoad load(loadingListener);
|
Loading::ScopedLoad load(loadingListener);
|
||||||
|
@ -403,7 +411,7 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y);
|
CellStore *cell = MWBase::Environment::get().getWorld()->getExterior(x, y);
|
||||||
|
|
||||||
loadCell (cell, loadingListener);
|
loadCell (cell, loadingListener, changeEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -411,7 +419,8 @@ namespace MWWorld
|
||||||
CellStore* current = MWBase::Environment::get().getWorld()->getExterior(X,Y);
|
CellStore* current = MWBase::Environment::get().getWorld()->getExterior(X,Y);
|
||||||
MWBase::Environment::get().getWindowManager()->changeCell(current);
|
MWBase::Environment::get().getWindowManager()->changeCell(current);
|
||||||
|
|
||||||
mCellChanged = true;
|
if (changeEvent)
|
||||||
|
mCellChanged = true;
|
||||||
|
|
||||||
mPreloader->updateCache(mRendering.getReferenceTime());
|
mPreloader->updateCache(mRendering.getReferenceTime());
|
||||||
}
|
}
|
||||||
|
@ -463,9 +472,11 @@ namespace MWWorld
|
||||||
|
|
||||||
mPhysics->setUnrefQueue(rendering.getUnrefQueue());
|
mPhysics->setUnrefQueue(rendering.getUnrefQueue());
|
||||||
|
|
||||||
float cacheExpiryDelay = Settings::Manager::getFloat("cache expiry delay", "Cells");
|
rendering.getResourceSystem()->setExpiryDelay(Settings::Manager::getFloat("cache expiry delay", "Cells"));
|
||||||
rendering.getResourceSystem()->setExpiryDelay(cacheExpiryDelay);
|
|
||||||
mPreloader->setExpiryDelay(cacheExpiryDelay);
|
mPreloader->setExpiryDelay(Settings::Manager::getFloat("preload cell expiry delay", "Cells"));
|
||||||
|
mPreloader->setMinCacheSize(Settings::Manager::getInt("preload cell cache min", "Cells"));
|
||||||
|
mPreloader->setMaxCacheSize(Settings::Manager::getInt("preload cell cache max", "Cells"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Scene::~Scene()
|
Scene::~Scene()
|
||||||
|
@ -482,7 +493,7 @@ namespace MWWorld
|
||||||
return mActiveCells;
|
return mActiveCells;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position)
|
void Scene::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool changeEvent)
|
||||||
{
|
{
|
||||||
CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName);
|
CellStore *cell = MWBase::Environment::get().getWorld()->getInterior(cellName);
|
||||||
bool loadcell = (mCurrentCell == NULL);
|
bool loadcell = (mCurrentCell == NULL);
|
||||||
|
@ -528,7 +539,7 @@ namespace MWWorld
|
||||||
loadingListener->setProgressRange(refsToLoad);
|
loadingListener->setProgressRange(refsToLoad);
|
||||||
|
|
||||||
// Load cell.
|
// Load cell.
|
||||||
loadCell (cell, loadingListener);
|
loadCell (cell, loadingListener, changeEvent);
|
||||||
|
|
||||||
changePlayerCell(cell, position, true);
|
changePlayerCell(cell, position, true);
|
||||||
|
|
||||||
|
@ -538,21 +549,24 @@ namespace MWWorld
|
||||||
// Sky system
|
// Sky system
|
||||||
MWBase::Environment::get().getWorld()->adjustSky();
|
MWBase::Environment::get().getWorld()->adjustSky();
|
||||||
|
|
||||||
mCellChanged = true; MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);
|
if (changeEvent)
|
||||||
|
mCellChanged = true;
|
||||||
|
|
||||||
|
MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5);
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
|
MWBase::Environment::get().getWindowManager()->changeCell(mCurrentCell);
|
||||||
|
|
||||||
mPreloader->updateCache(mRendering.getReferenceTime());
|
mPreloader->updateCache(mRendering.getReferenceTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos)
|
void Scene::changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent)
|
||||||
{
|
{
|
||||||
int x = 0;
|
int x = 0;
|
||||||
int y = 0;
|
int y = 0;
|
||||||
|
|
||||||
MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y);
|
MWBase::Environment::get().getWorld()->positionToIndex (position.pos[0], position.pos[1], x, y);
|
||||||
|
|
||||||
changeCellGrid(x, y);
|
changeCellGrid(x, y, changeEvent);
|
||||||
|
|
||||||
CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y);
|
CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y);
|
||||||
changePlayerCell(current, position, adjustPlayerPos);
|
changePlayerCell(current, position, adjustPlayerPos);
|
||||||
|
|
|
@ -71,7 +71,7 @@ namespace MWWorld
|
||||||
void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener);
|
void insertCell (CellStore &cell, bool rescale, Loading::Listener* loadingListener);
|
||||||
|
|
||||||
// Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center
|
// Load and unload cells as necessary to create a cell grid with "X" and "Y" in the center
|
||||||
void changeCellGrid (int X, int Y);
|
void changeCellGrid (int X, int Y, bool changeEvent = true);
|
||||||
|
|
||||||
void getGridCenter(int& cellX, int& cellY);
|
void getGridCenter(int& cellX, int& cellY);
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ namespace MWWorld
|
||||||
|
|
||||||
void unloadCell (CellStoreCollection::iterator iter);
|
void unloadCell (CellStoreCollection::iterator iter);
|
||||||
|
|
||||||
void loadCell (CellStore *cell, Loading::Listener* loadingListener);
|
void loadCell (CellStore *cell, Loading::Listener* loadingListener, bool respawn);
|
||||||
|
|
||||||
void playerMoved (const osg::Vec3f& pos);
|
void playerMoved (const osg::Vec3f& pos);
|
||||||
|
|
||||||
|
@ -103,11 +103,13 @@ namespace MWWorld
|
||||||
bool hasCellChanged() const;
|
bool hasCellChanged() const;
|
||||||
///< Has the set of active cells changed, since the last frame?
|
///< Has the set of active cells changed, since the last frame?
|
||||||
|
|
||||||
void changeToInteriorCell (const std::string& cellName, const ESM::Position& position);
|
void changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool changeEvent=true);
|
||||||
///< Move to interior cell.
|
///< Move to interior cell.
|
||||||
|
/// @param changeEvent Set cellChanged flag?
|
||||||
|
|
||||||
void changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos);
|
void changeToExteriorCell (const ESM::Position& position, bool adjustPlayerPos, bool changeEvent=true);
|
||||||
///< Move to exterior cell.
|
///< Move to exterior cell.
|
||||||
|
/// @param changeEvent Set cellChanged flag?
|
||||||
|
|
||||||
void changeToVoid();
|
void changeToVoid();
|
||||||
///< Change into a void
|
///< Change into a void
|
||||||
|
|
|
@ -962,11 +962,11 @@ namespace MWWorld
|
||||||
return mTimeScale->getFloat();
|
return mTimeScale->getFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position)
|
void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool changeEvent)
|
||||||
{
|
{
|
||||||
mPhysics->clearQueuedMovement();
|
mPhysics->clearQueuedMovement();
|
||||||
|
|
||||||
if (mCurrentWorldSpace != cellName)
|
if (changeEvent && mCurrentWorldSpace != cellName)
|
||||||
{
|
{
|
||||||
// changed worldspace
|
// changed worldspace
|
||||||
mProjectileManager->clear();
|
mProjectileManager->clear();
|
||||||
|
@ -976,34 +976,34 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
removeContainerScripts(getPlayerPtr());
|
removeContainerScripts(getPlayerPtr());
|
||||||
mWorldScene->changeToInteriorCell(cellName, position);
|
mWorldScene->changeToInteriorCell(cellName, position, changeEvent);
|
||||||
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::changeToExteriorCell (const ESM::Position& position)
|
void World::changeToExteriorCell (const ESM::Position& position, bool changeEvent)
|
||||||
{
|
{
|
||||||
mPhysics->clearQueuedMovement();
|
mPhysics->clearQueuedMovement();
|
||||||
|
|
||||||
if (mCurrentWorldSpace != "sys::default") // FIXME
|
if (changeEvent && mCurrentWorldSpace != "sys::default") // FIXME
|
||||||
{
|
{
|
||||||
// changed worldspace
|
// changed worldspace
|
||||||
mProjectileManager->clear();
|
mProjectileManager->clear();
|
||||||
mRendering->notifyWorldSpaceChanged();
|
mRendering->notifyWorldSpaceChanged();
|
||||||
}
|
}
|
||||||
removeContainerScripts(getPlayerPtr());
|
removeContainerScripts(getPlayerPtr());
|
||||||
mWorldScene->changeToExteriorCell(position, true);
|
mWorldScene->changeToExteriorCell(position, true, changeEvent);
|
||||||
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange)
|
void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool changeEvent)
|
||||||
{
|
{
|
||||||
if (!detectWorldSpaceChange)
|
if (!changeEvent)
|
||||||
mCurrentWorldSpace = cellId.mWorldspace;
|
mCurrentWorldSpace = cellId.mWorldspace;
|
||||||
|
|
||||||
if (cellId.mPaged)
|
if (cellId.mPaged)
|
||||||
changeToExteriorCell (position);
|
changeToExteriorCell (position, changeEvent);
|
||||||
else
|
else
|
||||||
changeToInteriorCell (cellId.mWorldspace, position);
|
changeToInteriorCell (cellId.mWorldspace, position, changeEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::markCellAsUnchanged()
|
void World::markCellAsUnchanged()
|
||||||
|
@ -1051,9 +1051,9 @@ namespace MWWorld
|
||||||
if(!node) node = anim->getNode("Bip01 Head");
|
if(!node) node = anim->getNode("Bip01 Head");
|
||||||
if(node)
|
if(node)
|
||||||
{
|
{
|
||||||
osg::MatrixList mats = node->getWorldMatrices();
|
osg::NodePathList nodepaths = node->getParentalNodePaths();
|
||||||
if(!mats.empty())
|
if(!nodepaths.empty())
|
||||||
return mats[0];
|
return osg::computeLocalToWorld(nodepaths[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return osg::Matrixf::translate(actor.getRefData().getPosition().asVec3());
|
return osg::Matrixf::translate(actor.getRefData().getPosition().asVec3());
|
||||||
|
@ -1325,11 +1325,50 @@ namespace MWWorld
|
||||||
rotateObjectImp(ptr, osg::Vec3f(x, y, z), adjust);
|
rotateObjectImp(ptr, osg::Vec3f(x, y, z), adjust);
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Ptr World::safePlaceObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos)
|
MWWorld::Ptr World::placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos)
|
||||||
{
|
{
|
||||||
return copyObjectToCell(ptr,cell,pos,ptr.getRefData().getCount(),false);
|
return copyObjectToCell(ptr,cell,pos,ptr.getRefData().getCount(),false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MWWorld::Ptr World::safePlaceObject(const ConstPtr &ptr, const ConstPtr &referenceObject, MWWorld::CellStore* referenceCell, int direction, float distance)
|
||||||
|
{
|
||||||
|
ESM::Position ipos = referenceObject.getRefData().getPosition();
|
||||||
|
osg::Vec3f pos(ipos.asVec3());
|
||||||
|
osg::Quat orientation(ipos.rot[2], osg::Vec3f(0,0,-1));
|
||||||
|
|
||||||
|
int fallbackDirections[4] = {direction, (direction+3)%4, (direction+2)%4, (direction+1)%4};
|
||||||
|
|
||||||
|
for (int i=0; i<4; ++i)
|
||||||
|
{
|
||||||
|
direction = fallbackDirections[i];
|
||||||
|
// check if spawn point is safe, fall back to another direction if not
|
||||||
|
osg::Vec3f spawnPoint = pos;
|
||||||
|
if (direction == 0) spawnPoint = pos + (orientation * osg::Vec3f(0,1,0)) * distance;
|
||||||
|
else if(direction == 1) spawnPoint = pos - (orientation * osg::Vec3f(0,1,0)) * distance;
|
||||||
|
else if(direction == 2) spawnPoint = pos - (orientation * osg::Vec3f(1,0,0)) * distance;
|
||||||
|
else if(direction == 3) spawnPoint = pos + (orientation * osg::Vec3f(1,0,0)) * distance;
|
||||||
|
spawnPoint.z() += 30; // move up a little to account for slopes, will snap down later
|
||||||
|
|
||||||
|
if (!castRay(spawnPoint.x(), spawnPoint.y(), spawnPoint.z(),
|
||||||
|
pos.x(), pos.y(), pos.z() + 20))
|
||||||
|
{
|
||||||
|
// safe
|
||||||
|
ipos.pos[0] = spawnPoint.x();
|
||||||
|
ipos.pos[1] = spawnPoint.y();
|
||||||
|
ipos.pos[2] = spawnPoint.z();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ipos.rot[0] = referenceObject.getRefData().getPosition().rot[0];
|
||||||
|
ipos.rot[1] = referenceObject.getRefData().getPosition().rot[1];
|
||||||
|
ipos.rot[2] = referenceObject.getRefData().getPosition().rot[2];
|
||||||
|
|
||||||
|
MWWorld::Ptr placed = copyObjectToCell(ptr, referenceCell, ipos, ptr.getRefData().getCount(), false);
|
||||||
|
placed.getClass().adjustPosition(placed, true); // snap to ground
|
||||||
|
return placed;
|
||||||
|
}
|
||||||
|
|
||||||
void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const
|
void World::indexToPosition (int cellX, int cellY, float &x, float &y, bool centre) const
|
||||||
{
|
{
|
||||||
const int cellSize = 8192;
|
const int cellSize = 8192;
|
||||||
|
@ -3025,23 +3064,9 @@ namespace MWWorld
|
||||||
if (selectedCreature.empty())
|
if (selectedCreature.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ESM::Position ipos = mPlayer->getPlayer().getRefData().getPosition();
|
|
||||||
osg::Vec3f pos(ipos.asVec3());
|
|
||||||
osg::Quat rot(-ipos.rot[2], osg::Vec3f(0,0,1));
|
|
||||||
const float distance = 50;
|
|
||||||
pos = pos + (rot * osg::Vec3f(0,1,0)) * distance;
|
|
||||||
ipos.pos[0] = pos.x();
|
|
||||||
ipos.pos[1] = pos.y();
|
|
||||||
ipos.pos[2] = pos.z();
|
|
||||||
ipos.rot[0] = 0;
|
|
||||||
ipos.rot[1] = 0;
|
|
||||||
ipos.rot[2] = 0;
|
|
||||||
|
|
||||||
MWWorld::CellStore* cell = mPlayer->getPlayer().getCell();
|
|
||||||
MWWorld::ManualRef ref(getStore(), selectedCreature, 1);
|
MWWorld::ManualRef ref(getStore(), selectedCreature, 1);
|
||||||
ref.getPtr().getCellRef().setPosition(ipos);
|
|
||||||
|
|
||||||
safePlaceObject(ref.getPtr(), cell, ipos);
|
safePlaceObject(ref.getPtr(), getPlayerPtr(), getPlayerPtr().getCell(), 0, 220.f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3148,25 +3173,16 @@ namespace MWWorld
|
||||||
|
|
||||||
void World::activate(const Ptr &object, const Ptr &actor)
|
void World::activate(const Ptr &object, const Ptr &actor)
|
||||||
{
|
{
|
||||||
MWScript::InterpreterContext interpreterContext (&object.getRefData().getLocals(), object);
|
|
||||||
interpreterContext.activate (object);
|
|
||||||
|
|
||||||
std::string script = object.getClass().getScript (object);
|
|
||||||
|
|
||||||
breakInvisibility(actor);
|
breakInvisibility(actor);
|
||||||
|
|
||||||
if (mScriptsEnabled)
|
if (mScriptsEnabled)
|
||||||
{
|
{
|
||||||
if (!script.empty())
|
if (object.getRefData().activate())
|
||||||
{
|
{
|
||||||
getLocalScripts().setIgnore (object);
|
boost::shared_ptr<MWWorld::Action> action = (object.getClass().activate(object, actor));
|
||||||
MWBase::Environment::get().getScriptManager()->run (script, interpreterContext);
|
action->execute (actor);
|
||||||
}
|
}
|
||||||
if (!interpreterContext.hasActivationBeenHandled())
|
|
||||||
interpreterContext.executeActivation(object, actor);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
interpreterContext.executeActivation(object, actor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ResetActorsVisitor
|
struct ResetActorsVisitor
|
||||||
|
|
|
@ -324,15 +324,16 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual float getTimeScaleFactor() const;
|
virtual float getTimeScaleFactor() const;
|
||||||
|
|
||||||
virtual void changeToInteriorCell (const std::string& cellName,
|
virtual void changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool changeEvent = true);
|
||||||
const ESM::Position& position);
|
|
||||||
///< Move to interior cell.
|
///< Move to interior cell.
|
||||||
|
///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
|
||||||
|
|
||||||
virtual void changeToExteriorCell (const ESM::Position& position);
|
virtual void changeToExteriorCell (const ESM::Position& position, bool changeEvent = true);
|
||||||
///< Move to exterior cell.
|
///< Move to exterior cell.
|
||||||
|
///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
|
||||||
|
|
||||||
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange=true);
|
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool changeEvent=true);
|
||||||
///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes
|
///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
|
||||||
|
|
||||||
virtual const ESM::Cell *getExterior (const std::string& cellName) const;
|
virtual const ESM::Cell *getExterior (const std::string& cellName) const;
|
||||||
///< Return a cell matching the given name or a 0-pointer, if there is no such cell.
|
///< Return a cell matching the given name or a 0-pointer, if there is no such cell.
|
||||||
|
@ -366,8 +367,12 @@ namespace MWWorld
|
||||||
/// \param adjust indicates rotation should be set or adjusted
|
/// \param adjust indicates rotation should be set or adjusted
|
||||||
virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false);
|
virtual void rotateObject (const Ptr& ptr,float x,float y,float z, bool adjust = false);
|
||||||
|
|
||||||
virtual MWWorld::Ptr safePlaceObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos);
|
virtual MWWorld::Ptr placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos);
|
||||||
///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr.
|
///< Place an object. Makes a copy of the Ptr.
|
||||||
|
|
||||||
|
virtual MWWorld::Ptr safePlaceObject (const MWWorld::ConstPtr& ptr, const MWWorld::ConstPtr& referenceObject, MWWorld::CellStore* referenceCell, int direction, float distance);
|
||||||
|
///< Place an object in a safe place next to \a referenceObject. \a direction and \a distance specify the wanted placement
|
||||||
|
/// relative to \a referenceObject (but the object may be placed somewhere else if the wanted location is obstructed).
|
||||||
|
|
||||||
virtual float getMaxActivationDistance();
|
virtual float getMaxActivationDistance();
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,10 @@ add_component_dir (resource
|
||||||
scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem resourcemanager
|
scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem resourcemanager
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_component_dir (shader
|
||||||
|
shadermanager shadervisitor
|
||||||
|
)
|
||||||
|
|
||||||
add_component_dir (sceneutil
|
add_component_dir (sceneutil
|
||||||
clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller
|
clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller
|
||||||
lightmanager lightutil positionattitudetransform workqueue unrefqueue
|
lightmanager lightutil positionattitudetransform workqueue unrefqueue
|
||||||
|
|
|
@ -300,6 +300,7 @@ namespace Compiler
|
||||||
extensions.registerInstruction ("disableteleporting", "", opcodeDisableTeleporting);
|
extensions.registerInstruction ("disableteleporting", "", opcodeDisableTeleporting);
|
||||||
extensions.registerInstruction ("enableteleporting", "", opcodeEnableTeleporting);
|
extensions.registerInstruction ("enableteleporting", "", opcodeEnableTeleporting);
|
||||||
extensions.registerInstruction ("showvars", "", opcodeShowVars, opcodeShowVarsExplicit);
|
extensions.registerInstruction ("showvars", "", opcodeShowVars, opcodeShowVarsExplicit);
|
||||||
|
extensions.registerInstruction ("show", "c", opcodeShow, opcodeShowExplicit);
|
||||||
extensions.registerInstruction ("sv", "", opcodeShowVars, opcodeShowVarsExplicit);
|
extensions.registerInstruction ("sv", "", opcodeShowVars, opcodeShowVarsExplicit);
|
||||||
extensions.registerInstruction("tgm", "", opcodeToggleGodMode);
|
extensions.registerInstruction("tgm", "", opcodeToggleGodMode);
|
||||||
extensions.registerInstruction("togglegodmode", "", opcodeToggleGodMode);
|
extensions.registerInstruction("togglegodmode", "", opcodeToggleGodMode);
|
||||||
|
|
|
@ -277,6 +277,8 @@ namespace Compiler
|
||||||
const int opcodeEnableTeleporting = 0x2000216;
|
const int opcodeEnableTeleporting = 0x2000216;
|
||||||
const int opcodeShowVars = 0x200021d;
|
const int opcodeShowVars = 0x200021d;
|
||||||
const int opcodeShowVarsExplicit = 0x200021e;
|
const int opcodeShowVarsExplicit = 0x200021e;
|
||||||
|
const int opcodeShow = 0x2000304;
|
||||||
|
const int opcodeShowExplicit = 0x2000305;
|
||||||
const int opcodeToggleGodMode = 0x200021f;
|
const int opcodeToggleGodMode = 0x200021f;
|
||||||
const int opcodeToggleScripts = 0x2000301;
|
const int opcodeToggleScripts = 0x2000301;
|
||||||
const int opcodeDisableLevitation = 0x2000220;
|
const int opcodeDisableLevitation = 0x2000220;
|
||||||
|
|
|
@ -87,6 +87,10 @@ void ESM::CreatureStats::load (ESMReader &esm)
|
||||||
mDeathAnimation = 0;
|
mDeathAnimation = 0;
|
||||||
esm.getHNOT (mDeathAnimation, "DANM");
|
esm.getHNOT (mDeathAnimation, "DANM");
|
||||||
|
|
||||||
|
mTimeOfDeath.mDay = 0;
|
||||||
|
mTimeOfDeath.mHour = 0;
|
||||||
|
esm.getHNOT (mTimeOfDeath, "DTIM");
|
||||||
|
|
||||||
mSpells.load(esm);
|
mSpells.load(esm);
|
||||||
mActiveSpells.load(esm);
|
mActiveSpells.load(esm);
|
||||||
mAiSequence.load(esm);
|
mAiSequence.load(esm);
|
||||||
|
@ -193,6 +197,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
|
||||||
if (mDeathAnimation)
|
if (mDeathAnimation)
|
||||||
esm.writeHNT ("DANM", mDeathAnimation);
|
esm.writeHNT ("DANM", mDeathAnimation);
|
||||||
|
|
||||||
|
if (mTimeOfDeath.mHour != 0 && mTimeOfDeath.mDay != 0)
|
||||||
|
esm.writeHNT ("DTIM", mTimeOfDeath);
|
||||||
|
|
||||||
mSpells.save(esm);
|
mSpells.save(esm);
|
||||||
mActiveSpells.save(esm);
|
mActiveSpells.save(esm);
|
||||||
mAiSequence.save(esm);
|
mAiSequence.save(esm);
|
||||||
|
|
|
@ -57,6 +57,7 @@ namespace ESM
|
||||||
bool mRecalcDynamicStats;
|
bool mRecalcDynamicStats;
|
||||||
int mDrawState;
|
int mDrawState;
|
||||||
unsigned char mDeathAnimation;
|
unsigned char mDeathAnimation;
|
||||||
|
ESM::TimeStamp mTimeOfDeath;
|
||||||
|
|
||||||
int mLevel;
|
int mLevel;
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,9 @@ struct Land
|
||||||
DATA_VTEX = 16
|
DATA_VTEX = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// default height to use in case there is no Land record
|
||||||
|
static const int DEFAULT_HEIGHT = -2048;
|
||||||
|
|
||||||
// number of vertices per side
|
// number of vertices per side
|
||||||
static const int LAND_SIZE = 65;
|
static const int LAND_SIZE = 65;
|
||||||
|
|
||||||
|
|
|
@ -53,14 +53,14 @@ void ESM::Header::load (ESMReader &esm)
|
||||||
{
|
{
|
||||||
esm.getSubHeader();
|
esm.getSubHeader();
|
||||||
mSCRD.resize(esm.getSubSize());
|
mSCRD.resize(esm.getSubSize());
|
||||||
if (mSCRD.size())
|
if (!mSCRD.empty())
|
||||||
esm.getExact(&mSCRD[0], mSCRD.size());
|
esm.getExact(&mSCRD[0], mSCRD.size());
|
||||||
}
|
}
|
||||||
if (esm.isNextSub("SCRS"))
|
if (esm.isNextSub("SCRS"))
|
||||||
{
|
{
|
||||||
esm.getSubHeader();
|
esm.getSubHeader();
|
||||||
mSCRS.resize(esm.getSubSize());
|
mSCRS.resize(esm.getSubSize());
|
||||||
if (mSCRS.size())
|
if (!mSCRS.empty())
|
||||||
esm.getExact(&mSCRS[0], mSCRS.size());
|
esm.getExact(&mSCRS[0], mSCRS.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,9 @@ void ESM::ObjectState::load (ESMReader &esm)
|
||||||
if (esm.isNextSub("LROT"))
|
if (esm.isNextSub("LROT"))
|
||||||
esm.skipHSub(); // local rotation, no longer used
|
esm.skipHSub(); // local rotation, no longer used
|
||||||
|
|
||||||
|
mFlags = 0;
|
||||||
|
esm.getHNOT (mFlags, "FLAG");
|
||||||
|
|
||||||
// obsolete
|
// obsolete
|
||||||
int unused;
|
int unused;
|
||||||
esm.getHNOT(unused, "LTIM");
|
esm.getHNOT(unused, "LTIM");
|
||||||
|
@ -55,6 +58,9 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const
|
||||||
if (!inInventory)
|
if (!inInventory)
|
||||||
esm.writeHNT ("POS_", mPosition, 24);
|
esm.writeHNT ("POS_", mPosition, 24);
|
||||||
|
|
||||||
|
if (mFlags != 0)
|
||||||
|
esm.writeHNT ("FLAG", mFlags);
|
||||||
|
|
||||||
if (!mHasCustomState)
|
if (!mHasCustomState)
|
||||||
esm.writeHNT ("HCUS", false);
|
esm.writeHNT ("HCUS", false);
|
||||||
}
|
}
|
||||||
|
@ -70,6 +76,7 @@ void ESM::ObjectState::blank()
|
||||||
mPosition.pos[i] = 0;
|
mPosition.pos[i] = 0;
|
||||||
mPosition.rot[i] = 0;
|
mPosition.rot[i] = 0;
|
||||||
}
|
}
|
||||||
|
mFlags = 0;
|
||||||
mHasCustomState = true;
|
mHasCustomState = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace ESM
|
||||||
unsigned char mEnabled;
|
unsigned char mEnabled;
|
||||||
int mCount;
|
int mCount;
|
||||||
ESM::Position mPosition;
|
ESM::Position mPosition;
|
||||||
|
unsigned int mFlags;
|
||||||
|
|
||||||
// Is there any class-specific state following the ObjectState
|
// Is there any class-specific state following the ObjectState
|
||||||
bool mHasCustomState;
|
bool mHasCustomState;
|
||||||
|
|
|
@ -16,10 +16,14 @@
|
||||||
namespace ESMTerrain
|
namespace ESMTerrain
|
||||||
{
|
{
|
||||||
|
|
||||||
const float defaultHeight = -2048;
|
const float defaultHeight = ESM::Land::DEFAULT_HEIGHT;
|
||||||
|
|
||||||
Storage::Storage(const VFS::Manager *vfs)
|
Storage::Storage(const VFS::Manager *vfs, const std::string& normalMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps)
|
||||||
: mVFS(vfs)
|
: mVFS(vfs)
|
||||||
|
, mNormalMapPattern(normalMapPattern)
|
||||||
|
, mAutoUseNormalMaps(autoUseNormalMaps)
|
||||||
|
, mSpecularMapPattern(specularMapPattern)
|
||||||
|
, mAutoUseSpecularMaps(autoUseSpecularMaps)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,9 +512,11 @@ namespace ESMTerrain
|
||||||
return found->second;
|
return found->second;
|
||||||
|
|
||||||
Terrain::LayerInfo info;
|
Terrain::LayerInfo info;
|
||||||
info.mParallax = false;
|
//info.mParallax = false;
|
||||||
info.mSpecular = false;
|
info.mSpecular = false;
|
||||||
info.mDiffuseMap = texture;
|
info.mDiffuseMap = texture;
|
||||||
|
|
||||||
|
/*
|
||||||
std::string texture_ = texture;
|
std::string texture_ = texture;
|
||||||
boost::replace_last(texture_, ".", "_nh.");
|
boost::replace_last(texture_, ".", "_nh.");
|
||||||
|
|
||||||
|
@ -519,20 +525,24 @@ namespace ESMTerrain
|
||||||
info.mNormalMap = texture_;
|
info.mNormalMap = texture_;
|
||||||
info.mParallax = true;
|
info.mParallax = true;
|
||||||
}
|
}
|
||||||
else
|
*/
|
||||||
|
if (mAutoUseNormalMaps)
|
||||||
{
|
{
|
||||||
texture_ = texture;
|
std::string texture_ = texture;
|
||||||
boost::replace_last(texture_, ".", "_n.");
|
boost::replace_last(texture_, ".", mNormalMapPattern + ".");
|
||||||
if (mVFS->exists(texture_))
|
if (mVFS->exists(texture_))
|
||||||
info.mNormalMap = texture_;
|
info.mNormalMap = texture_;
|
||||||
}
|
}
|
||||||
|
|
||||||
texture_ = texture;
|
if (mAutoUseSpecularMaps)
|
||||||
boost::replace_last(texture_, ".", "_diffusespec.");
|
|
||||||
if (mVFS->exists(texture_))
|
|
||||||
{
|
{
|
||||||
info.mDiffuseMap = texture_;
|
std::string texture_ = texture;
|
||||||
info.mSpecular = true;
|
boost::replace_last(texture_, ".", mSpecularMapPattern + ".");
|
||||||
|
if (mVFS->exists(texture_))
|
||||||
|
{
|
||||||
|
info.mDiffuseMap = texture_;
|
||||||
|
info.mSpecular = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mLayerInfoMap[texture] = info;
|
mLayerInfoMap[texture] = info;
|
||||||
|
@ -544,7 +554,7 @@ namespace ESMTerrain
|
||||||
{
|
{
|
||||||
Terrain::LayerInfo info;
|
Terrain::LayerInfo info;
|
||||||
info.mDiffuseMap = "textures\\_land_default.dds";
|
info.mDiffuseMap = "textures\\_land_default.dds";
|
||||||
info.mParallax = false;
|
//info.mParallax = false;
|
||||||
info.mSpecular = false;
|
info.mSpecular = false;
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace ESMTerrain
|
||||||
virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0;
|
virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Storage(const VFS::Manager* vfs);
|
Storage(const VFS::Manager* vfs, const std::string& normalMapPattern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false);
|
||||||
|
|
||||||
/// Data is loaded first, if necessary. Will return a 0-pointer if there is no data for
|
/// Data is loaded first, if necessary. Will return a 0-pointer if there is no data for
|
||||||
/// any of the data types specified via \a flags. Will also return a 0-pointer if there
|
/// any of the data types specified via \a flags. Will also return a 0-pointer if there
|
||||||
|
@ -109,6 +109,12 @@ namespace ESMTerrain
|
||||||
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;
|
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;
|
||||||
OpenThreads::Mutex mLayerInfoMutex;
|
OpenThreads::Mutex mLayerInfoMutex;
|
||||||
|
|
||||||
|
std::string mNormalMapPattern;
|
||||||
|
bool mAutoUseNormalMaps;
|
||||||
|
|
||||||
|
std::string mSpecularMapPattern;
|
||||||
|
bool mAutoUseSpecularMaps;
|
||||||
|
|
||||||
Terrain::LayerInfo getLayerInfo(const std::string& texture);
|
Terrain::LayerInfo getLayerInfo(const std::string& texture);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,13 +26,14 @@ namespace Interpreter{
|
||||||
return a.length() > b.length();
|
return a.length() > b.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fixDefinesReal(std::string text, char eschar, bool isBook, Context& context)
|
std::string fixDefinesReal(std::string text, bool dialogue, Context& context)
|
||||||
{
|
{
|
||||||
unsigned int start = 0;
|
unsigned int start = 0;
|
||||||
std::ostringstream retval;
|
std::ostringstream retval;
|
||||||
for(unsigned int i = 0; i < text.length(); i++)
|
for(unsigned int i = 0; i < text.length(); i++)
|
||||||
{
|
{
|
||||||
if(text[i] == eschar)
|
char eschar = text[i];
|
||||||
|
if(eschar == '%' || eschar == '^')
|
||||||
{
|
{
|
||||||
retval << text.substr(start, i - start);
|
retval << text.substr(start, i - start);
|
||||||
std::string temp = Misc::StringUtils::lowerCase(text.substr(i+1, 100));
|
std::string temp = Misc::StringUtils::lowerCase(text.substr(i+1, 100));
|
||||||
|
@ -113,7 +114,7 @@ namespace Interpreter{
|
||||||
retval << context.getCurrentCellName();
|
retval << context.getCurrentCellName();
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(eschar == '%' && !isBook) { // In Dialogue, not messagebox
|
else if(dialogue) { // In Dialogue, not messagebox
|
||||||
if( (found = check(temp, "faction", &i, &start))){
|
if( (found = check(temp, "faction", &i, &start))){
|
||||||
retval << context.getNPCFaction();
|
retval << context.getNPCFaction();
|
||||||
}
|
}
|
||||||
|
@ -207,15 +208,15 @@ namespace Interpreter{
|
||||||
return retval.str ();
|
return retval.str ();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fixDefinesDialog(std::string text, Context& context){
|
std::string fixDefinesDialog(const std::string& text, Context& context){
|
||||||
return fixDefinesReal(text, '%', false, context);
|
return fixDefinesReal(text, true, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fixDefinesMsgBox(std::string text, Context& context){
|
std::string fixDefinesMsgBox(const std::string& text, Context& context){
|
||||||
return fixDefinesReal(text, '^', false, context);
|
return fixDefinesReal(text, false, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fixDefinesBook(std::string text, Context& context){
|
std::string fixDefinesBook(const std::string& text, Context& context){
|
||||||
return fixDefinesReal(text, '%', true, context);
|
return fixDefinesReal(text, false, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
|
|
||||||
namespace Interpreter{
|
namespace Interpreter{
|
||||||
std::string fixDefinesDialog(std::string text, Context& context);
|
std::string fixDefinesDialog(const std::string& text, Context& context);
|
||||||
std::string fixDefinesMsgBox(std::string text, Context& context);
|
std::string fixDefinesMsgBox(const std::string& text, Context& context);
|
||||||
std::string fixDefinesBook(std::string text, Context& context);
|
std::string fixDefinesBook(const std::string& text, Context& context);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -154,7 +154,7 @@ void NiFloatData::read(NIFStream *nif)
|
||||||
|
|
||||||
void NiPixelData::read(NIFStream *nif)
|
void NiPixelData::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
nif->getInt(); // always 0 or 1
|
fmt = (Format)nif->getUInt();
|
||||||
|
|
||||||
rmask = nif->getInt(); // usually 0xff
|
rmask = nif->getInt(); // usually 0xff
|
||||||
gmask = nif->getInt(); // usually 0xff00
|
gmask = nif->getInt(); // usually 0xff00
|
||||||
|
@ -169,19 +169,23 @@ void NiPixelData::read(NIFStream *nif)
|
||||||
mips = nif->getInt();
|
mips = nif->getInt();
|
||||||
|
|
||||||
// Bytes per pixel, should be bpp * 8
|
// Bytes per pixel, should be bpp * 8
|
||||||
/*int bytes =*/ nif->getInt();
|
/* int bytes = */ nif->getInt();
|
||||||
|
|
||||||
for(int i=0; i<mips; i++)
|
for(int i=0; i<mips; i++)
|
||||||
{
|
{
|
||||||
// Image size and offset in the following data field
|
// Image size and offset in the following data field
|
||||||
/*int x =*/ nif->getInt();
|
Mipmap m;
|
||||||
/*int y =*/ nif->getInt();
|
m.width = nif->getUInt();
|
||||||
/*int offset =*/ nif->getInt();
|
m.height = nif->getUInt();
|
||||||
|
m.dataOffset = nif->getUInt();
|
||||||
|
mipmaps.push_back(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip the data
|
// Read the data
|
||||||
unsigned int dataSize = nif->getInt();
|
unsigned int dataSize = nif->getInt();
|
||||||
nif->skip(dataSize);
|
data.reserve(dataSize);
|
||||||
|
for (unsigned i=0; i<dataSize; ++i)
|
||||||
|
data.push_back((unsigned char)nif->getChar());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NiColorData::read(NIFStream *nif)
|
void NiColorData::read(NIFStream *nif)
|
||||||
|
|
|
@ -105,9 +105,30 @@ public:
|
||||||
class NiPixelData : public Record
|
class NiPixelData : public Record
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum Format
|
||||||
|
{
|
||||||
|
NIPXFMT_RGB8,
|
||||||
|
NIPXFMT_RGBA8,
|
||||||
|
NIPXFMT_PAL8,
|
||||||
|
NIPXFMT_DXT1,
|
||||||
|
NIPXFMT_DXT3,
|
||||||
|
NIPXFMT_DXT5,
|
||||||
|
NIPXFMT_DXT5_ALT
|
||||||
|
};
|
||||||
|
Format fmt;
|
||||||
|
|
||||||
unsigned int rmask, gmask, bmask, amask;
|
unsigned int rmask, gmask, bmask, amask;
|
||||||
int bpp, mips;
|
int bpp, mips;
|
||||||
|
|
||||||
|
struct Mipmap
|
||||||
|
{
|
||||||
|
int width, height;
|
||||||
|
int dataOffset;
|
||||||
|
};
|
||||||
|
std::vector<Mipmap> mipmaps;
|
||||||
|
|
||||||
|
std::vector<unsigned char> data;
|
||||||
|
|
||||||
void read(NIFStream *nif);
|
void read(NIFStream *nif);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,14 +19,20 @@ void NiTextureEffect::read(NIFStream *nif)
|
||||||
{
|
{
|
||||||
NiDynamicEffect::read(nif);
|
NiDynamicEffect::read(nif);
|
||||||
|
|
||||||
/*
|
// Model Projection Matrix
|
||||||
3 x Vector4 = [1,0,0,0]
|
nif->skip(3 * 3 * sizeof(float));
|
||||||
int = 2
|
|
||||||
int = 0 or 3
|
// Model Projection Transform
|
||||||
int = 2
|
nif->skip(3 * sizeof(float));
|
||||||
int = 2
|
|
||||||
*/
|
// Texture Filtering
|
||||||
nif->skip(16*4);
|
nif->skip(4);
|
||||||
|
|
||||||
|
clamp = nif->getUInt();
|
||||||
|
|
||||||
|
textureType = (TextureType)nif->getUInt();
|
||||||
|
|
||||||
|
coordGenType = (CoordGenType)nif->getUInt();
|
||||||
|
|
||||||
texture.read(nif);
|
texture.read(nif);
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue