1
0
Fork 1
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:
Marc Zinnschlag 2016-03-01 12:17:53 +01:00
commit 6f619ea85f
138 changed files with 4601 additions and 1029 deletions

View file

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

View file

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

View file

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

View file

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

View 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);
}
}

View 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

View file

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

View 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);
}

View 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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("");

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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