Merge branch 'master' into move

Conflicts:
	apps/opencs/view/render/object.cpp
move
Marc Zinnschlag 9 years ago
commit 6f619ea85f

@ -5,4 +5,4 @@ mkdir build
cd build
export CODE_COVERAGE=1
if [ "${CC}" = "clang" ]; then export CODE_COVERAGE=0; fi
${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DUSE_SYSTEM_TINYXML=TRUE
${ANALYZE}cmake .. -DBUILD_WITH_CODE_COVERAGE=${CODE_COVERAGE} -DBUILD_UNITTESTS=1 -DCMAKE_INSTALL_PREFIX=/usr -DBINDIR=/usr/games -DCMAKE_BUILD_TYPE="None" -DUSE_SYSTEM_TINYXML=TRUE

@ -190,10 +190,6 @@ if (WIN32)
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN)
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 )
if (OPENGL_ES)
@ -377,7 +373,7 @@ endif()
# CXX Compiler settings
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)
execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE CLANG_VERSION)
@ -566,7 +562,9 @@ endif(WIN32)
# Extern
add_subdirectory (extern/osg-ffmpeg-videoplayer)
add_subdirectory (extern/oics)
add_subdirectory (extern/osgQt)
if (USE_QT)
add_subdirectory (extern/osgQt)
endif()
# Components
add_subdirectory (components)

@ -26,7 +26,7 @@ opencs_units_noqt (model/world
universalid record commands columnbase columnimp scriptcontext cell refidcollection
refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata cellcoordinates cellselection resources resourcesmanager scope
pathgrid landtexture land nestedtablewrapper nestedcollection nestedcoladapterimp nestedinfocollection
idcompletionmanager metadata defaultgmsts
idcompletionmanager metadata defaultgmsts infoselectwrapper
)
opencs_hdrs_noqt (model/world
@ -42,7 +42,7 @@ opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck referencecheck referenceablecheck scriptcheck bodypartcheck
startscriptcheck search searchoperation searchstage pathgridcheck soundgencheck magiceffectcheck
mergestages gmstcheck
mergestages gmstcheck topicinfocheck journalcheck
)
opencs_hdrs_noqt (model/tools

@ -1,4 +1,4 @@
#ifndef CSV_PREFS_STATE_H
#ifndef CSM_PREFS_STATE_H
#define CSM_PREFS_STATE_H
#include <map>

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

@ -0,0 +1,35 @@
#ifndef CSM_TOOLS_JOURNALCHECK_H
#define CSM_TOOLS_JOURNALCHECK_H
#include <components/esm/loaddial.hpp>
#include "../world/idcollection.hpp"
#include "../world/infocollection.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: make sure that journal infos are good
class JournalCheckStage : public CSMDoc::Stage
{
public:
JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue>& journals,
const CSMWorld::InfoCollection& journalInfos);
virtual int setup();
///< \return number of steps
virtual void perform(int stage, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages
private:
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
const CSMWorld::InfoCollection& mJournalInfos;
};
}
#endif

@ -30,6 +30,8 @@
#include "magiceffectcheck.hpp"
#include "mergeoperation.hpp"
#include "gmstcheck.hpp"
#include "topicinfocheck.hpp"
#include "journalcheck.hpp"
CSMDoc::OperationHolder *CSMTools::Tools::get (int type)
{
@ -111,9 +113,24 @@ CSMDoc::OperationHolder *CSMTools::Tools::getVerifier()
mData.getReferenceables(),
mData.getResources (CSMWorld::UniversalId::Type_Icons),
mData.getResources (CSMWorld::UniversalId::Type_Textures)));
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);
}

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

@ -0,0 +1,95 @@
#ifndef CSM_TOOLS_TOPICINFOCHECK_HPP
#define CSM_TOOLS_TOPICINFOCHECK_HPP
#include <set>
#include <components/esm/loadclas.hpp>
#include <components/esm/loaddial.hpp>
#include <components/esm/loadfact.hpp>
#include <components/esm/loadglob.hpp>
#include <components/esm/loadgmst.hpp>
#include <components/esm/loadrace.hpp>
#include <components/esm/loadregn.hpp>
#include "../world/cell.hpp"
#include "../world/idcollection.hpp"
#include "../world/infocollection.hpp"
#include "../world/refiddata.hpp"
#include "../world/resources.hpp"
#include "../doc/stage.hpp"
namespace CSMTools
{
/// \brief VerifyStage: check topics
class TopicInfoCheckStage : public CSMDoc::Stage
{
public:
TopicInfoCheckStage(
const CSMWorld::InfoCollection& topicInfos,
const CSMWorld::IdCollection<CSMWorld::Cell>& cells,
const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& factions,
const CSMWorld::IdCollection<ESM::GameSetting>& gmsts,
const CSMWorld::IdCollection<ESM::Global>& globals,
const CSMWorld::IdCollection<ESM::Dialogue>& journals,
const CSMWorld::IdCollection<ESM::Race>& races,
const CSMWorld::IdCollection<ESM::Region>& regions,
const CSMWorld::IdCollection<ESM::Dialogue>& topics,
const CSMWorld::RefIdData& referencables,
const CSMWorld::Resources& soundFiles);
virtual int setup();
///< \return number of steps
virtual void perform(int step, CSMDoc::Messages& messages);
///< Messages resulting from this stage will be appended to \a messages
private:
const CSMWorld::InfoCollection& mTopicInfos;
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
const CSMWorld::IdCollection<ESM::Class>& mClasses;
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
const CSMWorld::IdCollection<ESM::GameSetting>& mGameSettings;
const CSMWorld::IdCollection<ESM::Global>& mGlobals;
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
const CSMWorld::IdCollection<ESM::Race>& mRaces;
const CSMWorld::IdCollection<ESM::Region>& mRegions;
const CSMWorld::IdCollection<ESM::Dialogue>& mTopics;
const CSMWorld::RefIdData& mReferencables;
const CSMWorld::Resources& mSoundFiles;
std::set<std::string> mCellNames;
// These return false when not successful and write an error
bool verifyActor(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
bool verifyCell(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
bool verifyFactionRank(const std::string& name, int rank, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages);
bool verifyItem(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
bool verifySelectStruct(const ESM::DialInfo::SelectStruct& select, const CSMWorld::UniversalId& id,
CSMDoc::Messages& messages);
bool verifySound(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
template <typename T>
bool verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
// Common error messages
void writeMissingIdError(const std::string& specifier, const std::string& missingId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
void writeDeletedRecordError(const std::string& specifier, const std::string& recordId,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
void writeInvalidTypeError(const std::string& specifier, const std::string& invalidId,
CSMWorld::UniversalId::Type invalidType, const std::string& expectedType,
const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
};
}
#endif

@ -3,6 +3,7 @@
#include <components/misc/stringops.hpp>
#include "universalid.hpp"
#include "infoselectwrapper.hpp"
namespace CSMWorld
{
@ -273,8 +274,8 @@ namespace CSMWorld
{ ColumnId_InfoList, "Info List" },
{ ColumnId_InfoCondition, "Info Conditions" },
{ ColumnId_InfoCondFunc, "Function" },
{ ColumnId_InfoCondVar, "Func/Variable" },
{ ColumnId_InfoCondComp, "Comp" },
{ ColumnId_InfoCondVar, "Variable/Object" },
{ ColumnId_InfoCondComp, "Relation" },
{ ColumnId_InfoCondValue, "Values" },
{ ColumnId_OriginalCell, "Original Cell" },
@ -546,18 +547,6 @@ namespace
"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)
{
switch (column)
@ -585,10 +574,8 @@ namespace
case CSMWorld::Columns::ColumnId_EffectId: return sEffectId;
case CSMWorld::Columns::ColumnId_PartRefType: return sPartRefType;
case CSMWorld::Columns::ColumnId_AiPackageType: return sAiPackageType;
case CSMWorld::Columns::ColumnId_InfoCondFunc: return sInfoCondFunc;
// FIXME: don't have dynamic value enum delegate, use Display_String for now
//case CSMWorld::Columns::ColumnId_InfoCond: return sInfoCond;
case CSMWorld::Columns::ColumnId_InfoCondComp: return sInfoCondComp;
case CSMWorld::Columns::ColumnId_InfoCondFunc: return CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings;
case CSMWorld::Columns::ColumnId_InfoCondComp: return CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings;
default: return 0;
}

@ -68,9 +68,6 @@ void CSMWorld::ModifyCommand::undo()
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())
{
CSMWorld::IdTree *tree = dynamic_cast<CSMWorld::IdTree *>(&mModel);
@ -114,7 +111,7 @@ void CSMWorld::CreateCommand::setType (UniversalId::Type type)
void CSMWorld::CreateCommand::redo()
{
mModel.addRecord (mId, mType);
mModel.addRecordWithData (mId, mValues, mType);
applyModifications();
}

@ -271,7 +271,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
new NestedChildColumn (Columns::ColumnId_InfoCondFunc, ColumnBase::Display_InfoCondFunc));
// FIXME: don't have dynamic value enum delegate, use Display_String for now
mTopicInfos.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_String));
new NestedChildColumn (Columns::ColumnId_InfoCondVar, ColumnBase::Display_InfoCondVar));
mTopicInfos.getNestableColumn(index)->addColumn(
new NestedChildColumn (Columns::ColumnId_InfoCondComp, ColumnBase::Display_InfoCondComp));
mTopicInfos.getNestableColumn(index)->addColumn(

@ -60,6 +60,10 @@ std::vector<CSMWorld::ColumnBase::Display> CSMWorld::IdCompletionManager::getDis
{
types.push_back(current->first);
}
// Hack for Display_InfoCondVar
types.push_back(CSMWorld::ColumnBase::Display_InfoCondVar);
return types;
}
@ -104,7 +108,7 @@ void CSMWorld::IdCompletionManager::generateCompleters(CSMWorld::Data &data)
QAbstractItemView *popup = new CSVWidget::CompleterPopup();
completer->setPopup(popup); // The completer takes ownership of the popup
completer->setMaxVisibleItems(10);
mCompleters[current->first] = completer;
}
}

@ -149,6 +149,21 @@ void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type
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,
const std::string& destination,
CSMWorld::UniversalId::Type type)

@ -53,6 +53,10 @@ namespace CSMWorld
void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None);
///< \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,
const std::string& destination,
UniversalId::Type type = UniversalId::Type_None);

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

@ -0,0 +1,243 @@
#ifndef CSM_WORLD_INFOSELECTWRAPPER_H
#define CSM_WORLD_INFOSELECTWRAPPER_H
#include <components/esm/loadinfo.hpp>
namespace CSMWorld
{
// ESM::DialInfo::SelectStruct.mSelectRule
// 012345...
// ^^^ ^^
// ||| ||
// ||| |+------------- condition variable string
// ||| +-------------- comparison type, ['0'..'5']; e.g. !=, <, >=, etc
// ||+---------------- function index (encoded, where function == '1')
// |+----------------- function, ['1'..'C']; e.g. Global, Local, Not ID, etc
// +------------------ unknown
//
// Wrapper for DialInfo::SelectStruct
class ConstInfoSelectWrapper
{
public:
// Order matters
enum FunctionName
{
Function_RankLow=0,
Function_RankHigh,
Function_RankRequirement,
Function_Reputation,
Function_Health_Percent,
Function_PcReputation,
Function_PcLevel,
Function_PcHealthPercent,
Function_PcMagicka,
Function_PcFatigue,
Function_PcStrength,
Function_PcBlock,
Function_PcArmorer,
Function_PcMediumArmor,
Function_PcHeavyArmor,
Function_PcBluntWeapon,
Function_PcLongBlade,
Function_PcAxe,
Function_PcSpear,
Function_PcAthletics,
Function_PcEnchant,
Function_PcDestruction,
Function_PcAlteration,
Function_PcIllusion,
Function_PcConjuration,
Function_PcMysticism,
Function_PcRestoration,
Function_PcAlchemy,
Function_PcUnarmored,
Function_PcSecurity,
Function_PcSneak,
Function_PcAcrobatics,
Function_PcLightArmor,
Function_PcShortBlade,
Function_PcMarksman,
Function_PcMerchantile,
Function_PcSpeechcraft,
Function_PcHandToHand,
Function_PcGender,
Function_PcExpelled,
Function_PcCommonDisease,
Function_PcBlightDisease,
Function_PcClothingModifier,
Function_PcCrimeLevel,
Function_SameSex,
Function_SameRace,
Function_SameFaction,
Function_FactionRankDifference,
Function_Detected,
Function_Alarmed,
Function_Choice,
Function_PcIntelligence,
Function_PcWillpower,
Function_PcAgility,
Function_PcSpeed,
Function_PcEndurance,
Function_PcPersonality,
Function_PcLuck,
Function_PcCorpus,
Function_Weather,
Function_PcVampire,
Function_Level,
Function_Attacked,
Function_TalkedToPc,
Function_PcHealth,
Function_CreatureTarget,
Function_FriendHit,
Function_Fight,
Function_Hello,
Function_Alarm,
Function_Flee,
Function_ShouldAttack,
Function_Werewolf,
Function_PcWerewolfKills=73,
Function_Global,
Function_Local,
Function_Journal,
Function_Item,
Function_Dead,
Function_NotId,
Function_NotFaction,
Function_NotClass,
Function_NotRace,
Function_NotCell,
Function_NotLocal,
Function_None
};
enum RelationType
{
Relation_Equal,
Relation_NotEqual,
Relation_Greater,
Relation_GreaterOrEqual,
Relation_Less,
Relation_LessOrEqual,
Relation_None
};
enum ComparisonType
{
Comparison_Boolean,
Comparison_Integer,
Comparison_Numeric,
Comparison_None
};
static const size_t RuleMinSize;
static const size_t FunctionPrefixOffset;
static const size_t FunctionIndexOffset;
static const size_t RelationIndexOffset;
static const size_t VarNameOffset;
static const char* FunctionEnumStrings[];
static const char* RelationEnumStrings[];
static const char* ComparisonEnumStrings[];
static std::string convertToString(FunctionName name);
static std::string convertToString(RelationType type);
static std::string convertToString(ComparisonType type);
ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select);
FunctionName getFunctionName() const;
RelationType getRelationType() const;
ComparisonType getComparisonType() const;
bool hasVariable() const;
const std::string& getVariableName() const;
bool conditionIsAlwaysTrue() const;
bool conditionIsNeverTrue() const;
bool variantTypeIsValid() const;
const ESM::Variant& getVariant() const;
std::string toString() const;
protected:
void readRule();
void readFunctionName();
void readRelationType();
void readVariableName();
void updateHasVariable();
void updateComparisonType();
std::pair<int, int> getConditionIntRange() const;
std::pair<float, float> getConditionFloatRange() const;
std::pair<int, int> getValidIntRange() const;
std::pair<float, float> getValidFloatRange() const;
template <typename Type1, typename Type2>
bool rangeContains(Type1 value, std::pair<Type2,Type2> range) const;
template <typename Type1, typename Type2>
bool rangesOverlap(std::pair<Type1,Type1> range1, std::pair<Type2,Type2> range2) const;
template <typename Type1, typename Type2>
bool rangeFullyContains(std::pair<Type1,Type1> containing, std::pair<Type2,Type2> test) const;
template <typename Type1, typename Type2>
bool rangesMatch(std::pair<Type1,Type1> range1, std::pair<Type2,Type2> range2) const;
template <typename Type1, typename Type2>
bool conditionIsAlwaysTrue(std::pair<Type1,Type1> conditionRange, std::pair<Type2,Type2> validRange) const;
template <typename Type1, typename Type2>
bool conditionIsNeverTrue(std::pair<Type1,Type1> conditionRange, std::pair<Type2,Type2> validRange) const;
FunctionName mFunctionName;
RelationType mRelationType;
ComparisonType mComparisonType;
bool mHasVariable;
std::string mVariableName;
private:
const ESM::DialInfo::SelectStruct& mConstSelect;
};
// Wrapper for DialInfo::SelectStruct that can modify the wrapped select struct
class InfoSelectWrapper : public ConstInfoSelectWrapper
{
public:
InfoSelectWrapper(ESM::DialInfo::SelectStruct& select);
// Wrapped SelectStruct will not be modified until update() is called
void setFunctionName(FunctionName name);
void setRelationType(RelationType type);
void setVariableName(const std::string& name);
// Modified wrapped SelectStruct
void update();
// This sets properties based on the function name to its defaults and updates the wrapped object
void setDefaults();
ESM::Variant& getVariant();
private:
ESM::DialInfo::SelectStruct& mSelect;
void writeRule();
};
}
#endif

@ -6,6 +6,7 @@
#include "idcollection.hpp"
#include "pathgrid.hpp"
#include "info.hpp"
#include "infoselectwrapper.hpp"
namespace CSMWorld
{
@ -529,16 +530,6 @@ namespace CSMWorld
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 () {}
void InfoConditionAdapter::addRow(Record<Info>& record, int position) const
@ -547,11 +538,11 @@ namespace CSMWorld
std::vector<ESM::DialInfo::SelectStruct>& conditions = info.mSelects;
// blank row
// default row
ESM::DialInfo::SelectStruct condStruct;
condStruct.mSelectRule = "00000";
condStruct.mSelectRule = "01000";
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);
@ -589,89 +580,6 @@ namespace CSMWorld
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,
int subRowIndex, int subColIndex) const
{
@ -682,70 +590,36 @@ namespace CSMWorld
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))
throw std::runtime_error ("index out of range");
ConstInfoSelectWrapper infoSelectWrapper(conditions[subRowIndex]);
switch (subColIndex)
{
case 0:
{
char condType = conditions[subRowIndex].mSelectRule[1];
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?
}
return infoSelectWrapper.getFunctionName();
}
case 1:
{
if (conditions[subRowIndex].mSelectRule[1] == '1')
{
// throws an exception if the encoding is not found
return sEncToInfoFunc.at(conditions[subRowIndex].mSelectRule.substr(2, 2)).c_str();
}
if (infoSelectWrapper.hasVariable())
return QString(infoSelectWrapper.getVariableName().c_str());
else
return QString(conditions[subRowIndex].mSelectRule.substr(5).c_str());
return "";
}
case 2:
{
char compType = conditions[subRowIndex].mSelectRule[4];
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?
}
return infoSelectWrapper.getRelationType();
}
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_Short:
case ESM::VT_Long:
{
return conditions[subRowIndex].mValue.getInteger();
return infoSelectWrapper.getVariant().getInteger();
}
case ESM::VT_Float:
{
return conditions[subRowIndex].mValue.getFloat();
return infoSelectWrapper.getVariant().getFloat();
}
default: return QVariant();
}
@ -764,101 +638,63 @@ namespace CSMWorld
if (subRowIndex < 0 || subRowIndex >= static_cast<int> (conditions.size()))
throw std::runtime_error ("index out of range");
InfoSelectWrapper infoSelectWrapper(conditions[subRowIndex]);
bool conversionResult = false;
switch (subColIndex)
{
case 0:
case 0: // Function
{
// See sInfoCondFunc in columns.cpp for the enum values
switch (value.toInt())
infoSelectWrapper.setFunctionName(static_cast<ConstInfoSelectWrapper::FunctionName>(value.toInt()));
if (infoSelectWrapper.getComparisonType() != ConstInfoSelectWrapper::Comparison_Numeric &&
infoSelectWrapper.getVariant().getType() != ESM::VT_Int)
{
// 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
infoSelectWrapper.getVariant().setType(ESM::VT_Int);
}
infoSelectWrapper.update();
break;
}
case 1:
case 1: // Variable
{
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())
return; // return without saving; TODO: maybe log an error here
}
else
{
// FIXME: validate the string values before saving, based on the current function
std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 5);
conditions[subRowIndex].mSelectRule = rule.append(value.toString().toUtf8().constData());
}
infoSelectWrapper.setVariableName(value.toString().toUtf8().constData());
infoSelectWrapper.update();
break;
}
case 2:
case 2: // Relation
{
// See sInfoCondComp in columns.cpp for the enum values
switch (value.toInt())
{
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
}
infoSelectWrapper.setRelationType(static_cast<ConstInfoSelectWrapper::RelationType>(value.toInt()));
infoSelectWrapper.update();
break;
}
case 3:
case 3: // Value
{
switch (conditions[subRowIndex].mValue.getType())
switch (infoSelectWrapper.getComparisonType())
{
case ESM::VT_String:
case ConstInfoSelectWrapper::Comparison_Numeric:
{
conditions[subRowIndex].mValue.setString (value.toString().toUtf8().constData());
break;
}
case ESM::VT_Int:
case ESM::VT_Short:
case ESM::VT_Long:
{
conditions[subRowIndex].mValue.setInteger (value.toInt());
// 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;
}
case ESM::VT_Float:
case ConstInfoSelectWrapper::Comparison_Boolean:
case ConstInfoSelectWrapper::Comparison_Integer:
{
conditions[subRowIndex].mValue.setFloat (value.toFloat());
if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare("0") == 0)
{
infoSelectWrapper.getVariant().setType(ESM::VT_Int);
infoSelectWrapper.getVariant().setInteger(value.toInt());
}
break;
}
default: break;

@ -108,7 +108,7 @@ void CSMWorld::IngredEffectRefIdAdapter::setNestedTable (const RefIdColumn* colu
ESM::Ingredient ingredient = record.get();
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);
}
@ -120,11 +120,11 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::IngredEffectRefIdAdapter::nestedTabl
static_cast<const Record<ESM::Ingredient>&> (data.getRecord (RefIdData::LocalIndex (index, mType)));
// return the whole struct
std::vector<typename ESM::Ingredient::IRDTstruct> wrap;
std::vector<ESM::Ingredient::IRDTstruct> wrap;
wrap.push_back(record.get().mData);
// 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,
@ -1129,7 +1129,7 @@ void CSMWorld::CreatureAttributesRefIdAdapter::setNestedTable (const RefIdColumn
// store the whole struct
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);
}
@ -1141,10 +1141,10 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::CreatureAttributesRefIdAdapter::nest
static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
// return the whole struct
std::vector<typename ESM::Creature::NPDTstruct> wrap;
std::vector<ESM::Creature::NPDTstruct> wrap;
wrap.push_back(record.get().mData);
// 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,
@ -1235,7 +1235,7 @@ void CSMWorld::CreatureAttackRefIdAdapter::setNestedTable (const RefIdColumn* co
// store the whole struct
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);
}
@ -1247,10 +1247,10 @@ CSMWorld::NestedTableWrapperBase* CSMWorld::CreatureAttackRefIdAdapter::nestedTa
static_cast<const Record<ESM::Creature>&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature)));
// return the whole struct
std::vector<typename ESM::Creature::NPDTstruct> wrap;
std::vector<ESM::Creature::NPDTstruct> wrap;
wrap.push_back(record.get().mData);
// 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,

@ -3,6 +3,7 @@
#include <osg/Group>
#include <components/misc/stringops.hpp>
#include <components/esm/loadcell.hpp>
#include <components/esm/loadland.hpp>
#include "../../model/world/idtable.hpp"
@ -308,12 +309,19 @@ void CSVRender::Cell::setCellArrows (int mask)
void CSVRender::Cell::setCellMarker()
{
bool cellExists = false;
bool isInteriorCell = false;
int cellIndex = mData.getCells().searchId(mId);
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

@ -1,6 +1,7 @@
#include "idcompletiondelegate.hpp"
#include "../../model/world/idcompletionmanager.hpp"
#include "../../model/world/infoselectwrapper.hpp"
#include "../widget/droplineedit.hpp"
@ -27,6 +28,56 @@ QWidget *CSVWorld::IdCompletionDelegate::createEditor(QWidget *parent,
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();
CSVWidget::DropLineEdit *editor = new CSVWidget::DropLineEdit(display, parent);
editor->setCompleter(completionManager.getCompleter(display).get());

@ -76,8 +76,6 @@ void OMW::Engine::executeLocalScripts()
&script.second.getRefData().getLocals(), script.second);
mEnvironment.getScriptManager()->run (script.first, interpreterContext);
}
localScripts.setIgnore (MWWorld::Ptr());
}
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 min filter", "General"),
Settings::Manager::getString("texture mipmap", "General"),
Settings::Manager::getInt("anisotropy", "General"),
NULL
Settings::Manager::getInt("anisotropy", "General")
);
// Create input and UI first to set up a bootstrapping environment for

@ -231,15 +231,16 @@ namespace MWBase
virtual float getTimeScaleFactor() const = 0;
virtual void changeToInteriorCell (const std::string& cellName,
const ESM::Position& position) = 0;
virtual void changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool changeEvent=true) = 0;
///< 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.
///< @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;
///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool changeEvent=true) = 0;
///< @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;
///< 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 MWWorld::Ptr safePlaceObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0;
///< place an object in a "safe" location (ie not in the void, etc).
virtual MWWorld::Ptr placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos) = 0;
///< 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)
const = 0;

@ -306,18 +306,7 @@ namespace MWClass
}
// Apply "On hit" enchanted weapons
std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : "";
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::applyOnStrikeEnchantment(ptr, victim, weapon, hitPosition);
}
else if (isBipedal(ptr))
{
@ -770,7 +759,18 @@ namespace MWClass
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())
{

@ -44,7 +44,28 @@ namespace MWClass
ensureCustomData(ptr);
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()
@ -56,8 +77,9 @@ namespace MWClass
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>();
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();
@ -68,6 +90,7 @@ namespace MWClass
MWWorld::ManualRef ref(store, it->mId);
ref.getPtr().getClass().getModelsToPreload(ref.getPtr(), models);
}
*/
}
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();
MWWorld::ManualRef ref(store, id);
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.mSpawn = false;
}

@ -637,18 +637,7 @@ namespace MWClass
damage *= store.find("fCombatKODamageMult")->getFloat();
// Apply "On hit" enchanted weapons
std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : "";
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::applyOnStrikeEnchantment(ptr, victim, weapon, hitPosition);
MWMechanics::applyElementalShields(ptr, victim);
@ -1303,7 +1292,18 @@ namespace MWClass
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())
{

@ -158,7 +158,7 @@ public:
}
// resolve overlapping keywords
while (matches.size())
while (!matches.empty())
{
int longestKeywordSize = 0;
typename std::vector<Match>::iterator longestKeyword = matches.begin();

@ -67,46 +67,29 @@ namespace MWGui
{
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}");
return;
}
// check if mortar & pestle is available (always needed)
if (result == MWMechanics::Alchemy::Result_NoMortarAndPestle)
{
break;
case MWMechanics::Alchemy::Result_NoMortarAndPestle:
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage45}");
return;
}
// make sure 2 or more ingredients were selected
if (result == MWMechanics::Alchemy::Result_LessThanTwoIngredients)
{
break;
case MWMechanics::Alchemy::Result_LessThanTwoIngredients:
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage6a}");
return;
}
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)
{
break;
case MWMechanics::Alchemy::Result_Success:
MWBase::Environment::get().getWindowManager()->messageBox("#{sPotionSuccess}");
MWBase::Environment::get().getSoundManager()->playSound("potion success", 1.f, 1.f);
}
else if (result == MWMechanics::Alchemy::Result_RandomFailure)
{
// potion failed
break;
case MWMechanics::Alchemy::Result_NoEffects:
case MWMechanics::Alchemy::Result_RandomFailure:
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage8}");
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)
if (mIngredients[i]->isUserString("ToolTipType"))
{

@ -240,7 +240,7 @@ namespace MWGui
mCommandLine->setCaption(newCaption);
// List candidates if repeatedly pressing tab
if (oldCaption == newCaption && matches.size())
if (oldCaption == newCaption && !matches.empty())
{
int i = 0;
printOK("");

@ -37,7 +37,7 @@ ContainerItemModel::ContainerItemModel(const std::vector<MWWorld::Ptr>& itemSour
: mItemSources(itemSources)
, mWorldItems(worldItems)
{
assert (mItemSources.size());
assert (!mItemSources.empty());
}
ContainerItemModel::ContainerItemModel (const MWWorld::Ptr& source)

@ -27,13 +27,6 @@
namespace
{
std::string fpsLevelToStr(int level)
{
if (level == 0)
return "#{sOff}";
else //if (level == 1)
return "#{sOn}";
}
std::string textureMipmappingToStr(const std::string& val)
{
@ -182,13 +175,9 @@ namespace MWGui
getWidget(mOkButton, "OkButton");
getWidget(mResolutionList, "ResolutionList");
getWidget(mFullscreenButton, "FullscreenButton");
getWidget(mVSyncButton, "VSyncButton");
getWidget(mWindowBorderButton, "WindowBorderButton");
getWidget(mTextureFilteringButton, "TextureFilteringButton");
getWidget(mAnisotropyBox, "AnisotropyBox");
getWidget(mShadersButton, "ShadersButton");
getWidget(mShadowsEnabledButton, "ShadowsEnabledButton");
getWidget(mShadowsTextureSize, "ShadowsTextureSize");
getWidget(mControlsBox, "ControlsBox");
getWidget(mResetControlsButton, "ResetControlsButton");
getWidget(mKeyboardSwitch, "KeyboardButton");
@ -218,8 +207,6 @@ namespace MWGui
mWaterTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onWaterTextureSizeChanged);
mShadowsTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onShadowTextureSizeChanged);
mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked);
mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked);
@ -260,13 +247,6 @@ namespace MWGui
if (waterTextureSize >= 2048)
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"));
mKeyboardSwitch->setStateSelected(true);
@ -346,12 +326,6 @@ namespace MWGui
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)
{
std::string on = MWBase::Environment::get().getWindowManager()->getGameSettingString("sOn", "On");
@ -368,21 +342,6 @@ namespace MWGui
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)
{
// check if this resolution is supported in fullscreen

@ -28,17 +28,12 @@ namespace MWGui
// graphics
MyGUI::ListBox* mResolutionList;
MyGUI::Button* mFullscreenButton;
MyGUI::Button* mVSyncButton;
MyGUI::Button* mWindowBorderButton;
MyGUI::ComboBox* mTextureFilteringButton;
MyGUI::Widget* mAnisotropyBox;
MyGUI::Button* mShadersButton;
MyGUI::ComboBox* mWaterTextureSize;
MyGUI::Button* mShadowsEnabledButton;
MyGUI::ComboBox* mShadowsTextureSize;
// controls
MyGUI::ScrollView* mControlsBox;
MyGUI::Button* mResetControlsButton;
@ -58,8 +53,6 @@ namespace MWGui
void onWaterTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
void onShadowTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos);
void onRebindAction(MyGUI::Widget* _sender);
void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel);
void onResetDefaultBindings(MyGUI::Widget* _sender);

@ -1377,7 +1377,7 @@ namespace MWMechanics
{
if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdFollow)
{
MWWorld::Ptr followTarget = static_cast<MWMechanics::AiFollow*>(*it)->getTarget();
MWWorld::Ptr followTarget = (*it)->getTarget();
if (followTarget.isEmpty())
continue;
if (followTarget == actor)

@ -119,7 +119,7 @@ namespace MWMechanics
return TypeIdEscort;
}
MWWorld::Ptr AiEscort::getTarget()
MWWorld::Ptr AiEscort::getTarget() const
{
return MWBase::Environment::get().getWorld()->getPtr(mActorId, false);
}

@ -37,7 +37,7 @@ namespace MWMechanics
virtual int getTypeId() const;
MWWorld::Ptr getTarget();
MWWorld::Ptr getTarget() const;
virtual bool sideWithTarget() const { return true; }
void writeState(ESM::AiSequence::AiSequence &sequence) const;

@ -200,7 +200,7 @@ void AiFollow::writeState(ESM::AiSequence::AiSequence &sequence) const
sequence.mPackages.push_back(package);
}
MWWorld::Ptr AiFollow::getTarget()
MWWorld::Ptr AiFollow::getTarget() const
{
if (mActorId == -2)
return MWWorld::Ptr();

@ -31,7 +31,7 @@ namespace MWMechanics
AiFollow(const ESM::AiSequence::AiFollow* follow);
MWWorld::Ptr getTarget();
MWWorld::Ptr getTarget() const;
virtual bool sideWithTarget() const { return true; }
virtual bool followTargetThroughDoors() const { return true; }
@ -60,7 +60,7 @@ namespace MWMechanics
float mY;
float mZ;
std::string mActorRefId;
int mActorId;
mutable int mActorId;
std::string mCellId;
bool mActive; // have we spotted the target?
int mFollowIndex;

@ -20,7 +20,7 @@
MWMechanics::AiPackage::~AiPackage() {}
MWWorld::Ptr MWMechanics::AiPackage::getTarget()
MWWorld::Ptr MWMechanics::AiPackage::getTarget() const
{
return MWWorld::Ptr();
}

@ -72,7 +72,7 @@ namespace MWMechanics
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)
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)
virtual bool sideWithTarget() const;

@ -68,9 +68,8 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const
{
if (getTypeId() != AiPackage::TypeIdCombat)
return false;
const AiCombat *combat = static_cast<const AiCombat *>(mPackages.front());
targetActor = combat->getTarget();
targetActor = mPackages.front()->getTarget();
return !targetActor.isEmpty();
}
@ -114,8 +113,7 @@ bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const
{
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
{
const AiCombat *combat = static_cast<const AiCombat *>(*it);
if (combat->getTarget() == actor)
if ((*it)->getTarget() == actor)
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)
{
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
}

@ -255,7 +255,7 @@ namespace MWMechanics
// Construct a new path if there isn't one
if(!storage.mPathFinder.isPathConstructed())
{
if (mAllowedNodes.size())
if (!mAllowedNodes.empty())
{
setPathToAnAllowedNode(actor, storage, pos);
}

@ -2140,10 +2140,10 @@ void CharacterController::updateHeadTracking(float duration)
if (!mHeadTrackTarget.isEmpty())
{
osg::MatrixList mats = head->getWorldMatrices();
if (mats.empty())
osg::NodePathList nodepaths = head->getParentalNodePaths();
if (nodepaths.empty())
return;
osg::Matrixf mat = mats[0];
osg::Matrixf mat = osg::computeLocalToWorld(nodepaths[0]);
osg::Vec3f headPos = mat.getTrans();
osg::Vec3f direction;
@ -2154,9 +2154,9 @@ void CharacterController::updateHeadTracking(float duration)
node = anim->getNode("Bip01 Head");
if (node != NULL)
{
osg::MatrixList mats = node->getWorldMatrices();
if (mats.size())
direction = mats[0].getTrans() - headPos;
osg::NodePathList nodepaths = node->getParentalNodePaths();
if (!nodepaths.empty())
direction = osg::computeLocalToWorld(nodepaths[0]).getTrans() - headPos;
}
else
// no head node to look at, fall back to look at center of collision box

@ -29,28 +29,28 @@ float signedAngleRadians (const osg::Vec3f& v1, const osg::Vec3f& v2, const osg:
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)
}
namespace MWMechanics
{
std::string enchantmentName = !object.isEmpty() ? object.getClass().getEnchantment(object) : "";
if (!enchantmentName.empty())
bool applyOnStrikeEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const osg::Vec3f& hitPosition)
{
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
enchantmentName);
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
std::string enchantmentName = !object.isEmpty() ? object.getClass().getEnchantment(object) : "";
if (!enchantmentName.empty())
{
MWMechanics::CastSpell cast(attacker, victim);
cast.mHitPosition = hitPosition;
cast.cast(object);
return true;
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;
}
return false;
}
}
namespace MWMechanics
{
bool blockMeleeAttack(const MWWorld::Ptr &attacker, const MWWorld::Ptr &blocker, const MWWorld::Ptr &weapon, float damage, float attackStrength)
{
@ -215,9 +215,9 @@ namespace MWMechanics
damage *= gmst.find("fCombatKODamageMult")->getFloat();
// Apply "On hit" effect of the weapon
bool appliedEnchantment = applyEnchantment(attacker, victim, weapon, hitPosition);
bool appliedEnchantment = applyOnStrikeEnchantment(attacker, victim, weapon, hitPosition);
if (weapon != projectile)
appliedEnchantment = applyEnchantment(attacker, victim, projectile, hitPosition);
appliedEnchantment = applyOnStrikeEnchantment(attacker, victim, projectile, hitPosition);
if (damage > 0)
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);

@ -6,6 +6,8 @@
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?
bool blockMeleeAttack (const MWWorld::Ptr& attacker, const MWWorld::Ptr& blocker, const MWWorld::Ptr& weapon, float damage, float attackStrength);

@ -22,7 +22,7 @@ namespace MWMechanics
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
mHitRecovery(false), mBlock(false), mMovementFlags(0),
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)
mAiSettings[i] = 0;
@ -187,6 +187,9 @@ namespace MWMechanics
if (index==0 && mDynamic[index].getCurrent()<1)
{
if (!mDead)
mTimeOfDeath = MWBase::Environment::get().getWorld()->getTimeStamp();
mDead = true;
mDynamic[index].setModifier(0);
@ -503,6 +506,7 @@ namespace MWMechanics
state.mLevel = mLevel;
state.mActorId = mActorId;
state.mDeathAnimation = mDeathAnimation;
state.mTimeOfDeath = mTimeOfDeath.toEsm();
mSpells.writeState(state.mSpells);
mActiveSpells.writeState(state.mActiveSpells);
@ -549,6 +553,7 @@ namespace MWMechanics
mLevel = state.mLevel;
mActorId = state.mActorId;
mDeathAnimation = state.mDeathAnimation;
mTimeOfDeath = MWWorld::TimeStamp(state.mTimeOfDeath);
mSpells.readState(state.mSpells);
mActiveSpells.readState(state.mActiveSpells);
@ -622,6 +627,11 @@ namespace MWMechanics
mDeathAnimation = index;
}
MWWorld::TimeStamp CreatureStats::getTimeOfDeath() const
{
return mTimeOfDeath;
}
std::map<CreatureStats::SummonKey, int>& CreatureStats::getSummonedCreatureMap()
{
return mSummonedCreatures;

@ -65,6 +65,8 @@ namespace MWMechanics
// The index of the death animation that was played
unsigned char mDeathAnimation;
MWWorld::TimeStamp mTimeOfDeath;
public:
typedef std::pair<int, std::string> SummonKey; // <ESM::MagicEffect index, spell ID>
private:
@ -259,6 +261,8 @@ namespace MWMechanics
unsigned char getDeathAnimation() const;
void setDeathAnimation(unsigned char index);
MWWorld::TimeStamp getTimeOfDeath() const;
int getActorId();
///< Will generate an actor ID, if the actor does not have one yet.

@ -1330,7 +1330,7 @@ namespace MWMechanics
{
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
{
MWWorld::Ptr target = static_cast<AiCombat*>(*it)->getTarget();
MWWorld::Ptr target = (*it)->getTarget();
if (!target.isEmpty() && target.getClass().isNpc())
isFightingNpc = true;
}

@ -120,14 +120,10 @@ namespace MWMechanics
const MWWorld::Class& cls = actor.getClass();
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)
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;

@ -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
@ -694,7 +687,7 @@ namespace MWMechanics
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);
if (enchantmentName.empty())
@ -761,15 +754,20 @@ namespace MWMechanics
inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch);
}
std::string projectileModel;
std::string sound;
float speed = 0;
getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed);
if (!projectileModel.empty())
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
false, enchantment->mEffects, mCaster, mSourceName,
// Not needed, enchantments can only be cast by actors
osg::Vec3f(1,0,0));
if (launchProjectile)
{
std::string projectileModel;
std::string sound;
float speed = 0;
getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed);
if (!projectileModel.empty())
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
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;
}

@ -81,7 +81,8 @@ namespace MWMechanics
bool cast (const ESM::Spell* spell);
/// @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
bool cast (const ESM::Ingredient* ingredient);

@ -114,31 +114,16 @@ namespace MWMechanics
bool found = creatureMap.find(std::make_pair(it->first, it->second)) != creatureMap.end();
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];
std::string creatureID =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(creatureGmst)->getString();
if (!creatureID.empty())
{
MWWorld::CellStore* store = mActor.getCell();
int creatureActorId = -1;
try
{
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1);
ref.getPtr().getCellRef().setPosition(ipos);
MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr());
@ -147,7 +132,7 @@ namespace MWMechanics
summonedCreatureStats.getAiSequence().stack(package, ref.getPtr());
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);
if (anim)

@ -52,8 +52,8 @@ namespace
class GlowUpdater : public SceneUtil::StateSetUpdater
{
public:
GlowUpdater(osg::Vec4f color, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures)
: mTexUnit(1) // FIXME: might not always be 1
GlowUpdater(int texUnit, osg::Vec4f color, const std::vector<osg::ref_ptr<osg::Texture2D> >& textures)
: mTexUnit(texUnit)
, mColor(color)
, mTextures(textures)
{
@ -1041,6 +1041,25 @@ namespace MWRender
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)
{
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::Texture2D> tex (new osg::Texture2D(image));
tex->setName("envMap");
tex->setWrap(osg::Texture::WRAP_S, osg::Texture2D::REPEAT);
tex->setWrap(osg::Texture::WRAP_T, osg::Texture2D::REPEAT);
mResourceSystem->getSceneManager()->applyFilterSettings(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);
// 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
@ -1244,10 +1278,14 @@ namespace MWRender
stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
mObjectRoot->setStateSet(stateset);
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
}
else
{
mObjectRoot->setStateSet(NULL);
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
}
setRenderBin();
@ -1315,9 +1353,24 @@ namespace MWRender
if (found != getNodeMap().end())
{
osg::MatrixTransform* node = found->second;
mHeadController = new RotateController(mObjectRoot.get());
node->addUpdateCallback(mHeadController);
mActiveControllers.insert(std::make_pair(node, mHeadController));
bool foundKeyframeCtrl = false;
osg::Callback* cb = node->getUpdateCallback();
while (cb)
{
if (dynamic_cast<NifOsg::KeyframeController*>(cb))
{
foundKeyframeCtrl = true;
break;
}
}
if (foundKeyframeCtrl)
{
mHeadController = new RotateController(mObjectRoot.get());
node->addUpdateCallback(mHeadController);
mActiveControllers.insert(std::make_pair(node, mHeadController));
}
}
}
}

@ -88,10 +88,10 @@ namespace MWRender
const osg::Node* trackNode = mTrackingNode;
if (!trackNode)
return osg::Vec3d();
osg::MatrixList mats = trackNode->getWorldMatrices();
if (!mats.size())
osg::NodePathList nodepaths = trackNode->getParentalNodePaths();
if (nodepaths.empty())
return osg::Vec3d();
const osg::Matrix& worldMat = mats[0];
osg::Matrix worldMat = osg::computeLocalToWorld(nodepaths[0]);
osg::Vec3d position = worldMat.getTrans();
if (!isFirstPerson())

@ -2,6 +2,7 @@
#include <iostream>
#include <osg/Fog>
#include <osg/Texture2D>
#include <osg/Camera>
#include <osg/PositionAttitudeTransform>
@ -98,10 +99,16 @@ namespace MWRender
osg::ref_ptr<SceneUtil::LightManager> lightManager = new SceneUtil::LightManager;
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_NORMALIZE, 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;
lightmodel->setAmbientIntensity(osg::Vec4(0.25, 0.25, 0.25, 1.0));
@ -123,7 +130,6 @@ namespace MWRender
lightSource->setStateSetModes(*stateset, osg::StateAttribute::ON);
lightManager->setStateSet(stateset);
lightManager->addChild(lightSource);
mCamera->addChild(lightManager);
@ -359,10 +365,10 @@ namespace MWRender
traverse(node, nv);
// Now update camera utilizing the updated head position
osg::MatrixList mats = mNodeToFollow->getWorldMatrices();
if (!mats.size())
osg::NodePathList nodepaths = mNodeToFollow->getParentalNodePaths();
if (nodepaths.empty())
return;
osg::Matrix worldMat = mats[0];
osg::Matrix worldMat = osg::computeLocalToWorld(nodepaths[0]);
osg::Vec3 headOffset = worldMat.getTrans();
cam->setViewMatrixAsLookAt(headOffset + mPosOffset, headOffset + mLookAtOffset, osg::Vec3(0,0,1));

@ -402,7 +402,7 @@ namespace MWRender
|| bounds.mMinY > bounds.mMaxY)
throw std::runtime_error("invalid map bounds");
if (!map.mImageData.size())
if (map.mImageData.empty())
return;
Files::IMemStream istream(&map.mImageData[0], map.mImageData.size());

@ -180,7 +180,12 @@ osg::ref_ptr<osg::Camera> LocalMap::createOrthographicCamera(float x, float y, f
stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);
stateset->setMode(GL_NORMALIZE, 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;
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)
{
const std::vector<char>& data = esm.mImageData;
if (!data.size())
if (data.empty())
{
initFogOfWar();
return;

@ -168,6 +168,14 @@ namespace MWRender
, mFieldOfViewOverridden(false)
{
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;
sceneRoot->setLightingMask(Mask_Lighting);
@ -189,7 +197,9 @@ namespace MWRender
mWater.reset(new Water(mRootNode, sceneRoot, mResourceSystem, mViewer->getIncrementalCompileOperation(), fallback, resourcePath));
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()));
@ -333,7 +343,9 @@ namespace MWRender
{
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));
}
@ -836,16 +848,18 @@ namespace MWRender
void RenderingManager::updateTextureFiltering()
{
if (mTerrain.get())
mTerrain->updateCache();
mViewer->stopThreading();
mResourceSystem->getSceneManager()->setFilterSettings(
Settings::Manager::getString("texture mag filter", "General"),
Settings::Manager::getString("texture min filter", "General"),
Settings::Manager::getString("texture mipmap", "General"),
Settings::Manager::getInt("anisotropy", "General"),
mViewer
Settings::Manager::getInt("anisotropy", "General")
);
mTerrain->updateTextureFiltering();
mViewer->startThreading();
}
void RenderingManager::updateAmbient()

@ -44,11 +44,11 @@ void RotateController::operator()(osg::Node *node, osg::NodeVisitor *nv)
osg::Quat RotateController::getWorldOrientation(osg::Node *node)
{
// 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;
if (!worldMats.empty())
if (!nodepaths.empty())
{
osg::Matrixf worldMat = worldMats[0];
osg::Matrixf worldMat = osg::computeLocalToWorld(nodepaths[0]);
worldOrient = worldMat.getRotate();
}
return worldOrient;

@ -1125,6 +1125,9 @@ SkyManager::SkyManager(osg::Group* parentNode, Resource::SceneManager* sceneMana
, mSunEnabled(true)
{
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);
parentNode->addChild(skyroot);

@ -9,21 +9,9 @@
namespace MWRender
{
TerrainStorage::TerrainStorage(const VFS::Manager* vfs, bool preload)
: ESMTerrain::Storage(vfs)
TerrainStorage::TerrainStorage(const VFS::Manager* vfs, const std::string& normalMapPattern, bool autoUseNormalMaps, const std::string& specularMapPattern, bool autoUseSpecularMaps)
: 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)

@ -14,9 +14,7 @@ namespace MWRender
virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
public:
///@param preload Preload all Land records at startup? If using the multithreaded terrain component, this
/// should be set to "true" in order to avoid race conditions.
TerrainStorage(const VFS::Manager* vfs, bool preload);
TerrainStorage(const VFS::Manager* vfs, const std::string& normalMapPattern = "", bool autoUseNormalMaps = false, const std::string& specularMapPattern = "", bool autoUseSpecularMaps = false);
/// Get bounds of the whole terrain in cell units
virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY);

@ -18,6 +18,7 @@ void overrideTexture(const std::string &texture, Resource::ResourceSystem *resou
osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D(resourceSystem->getImageManager()->getImage(correctedTexture));
tex->setWrap(osg::Texture::WRAP_S, 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;
if (node->getStateSet())

@ -9,13 +9,11 @@
#include <osg/Geometry>
#include <osg/Material>
#include <osg/PositionAttitudeTransform>
#include <osg/Depth>
#include <osg/ClipNode>
#include <osg/MatrixTransform>
#include <osg/FrontFace>
#include <osg/Shader>
#include <osg/GLExtensions>
#include <osg/UserDataContainer>
#include <osgDB/ReadFile>
@ -306,7 +304,12 @@ public:
setUpdateCallback(new NoTraverseCallback);
// 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;
addChild(mClipCullNode);
@ -318,7 +321,6 @@ public:
mRefractionTexture->setInternalFormat(GL_RGB);
mRefractionTexture->setFilter(osg::Texture::MIN_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);
@ -330,7 +332,6 @@ public:
mRefractionDepthTexture->setSourceType(GL_UNSIGNED_INT);
mRefractionDepthTexture->setFilter(osg::Texture::MIN_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);
}
@ -375,7 +376,9 @@ public:
setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);
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);
unsigned int rttSize = Settings::Manager::getInt("rtt size", "Water");
@ -391,7 +394,6 @@ public:
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_T, osg::Texture::CLAMP_TO_EDGE);
mReflectionTexture->getOrCreateUserDataContainer()->addDescription("dont_override_filter");
attach(osg::Camera::COLOR_BUFFER, mReflectionTexture);
@ -563,7 +565,7 @@ void Water::createSimpleWaterStateSet(osg::Node* node, float alpha)
textures.push_back(tex);
}
if (!textures.size())
if (textures.empty())
return;
float fps = mFallback->getFallbackFloat("Water_SurfaceFPS");

@ -114,10 +114,10 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
osg::Node* weaponNode = getWeaponNode();
if (!weaponNode)
return;
osg::MatrixList mats = weaponNode->getWorldMatrices();
if (mats.empty())
osg::NodePathList nodepaths = weaponNode->getParentalNodePaths();
if (nodepaths.empty())
return;
osg::Vec3f launchPos = mats[0].getTrans();
osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans();
float fThrownWeaponMinSpeed = gmst.find("fThrownWeaponMinSpeed")->getFloat();
float fThrownWeaponMaxSpeed = gmst.find("fThrownWeaponMaxSpeed")->getFloat();
@ -140,10 +140,10 @@ void WeaponAnimation::releaseArrow(MWWorld::Ptr actor, float attackStrength)
return;
osg::ref_ptr<osg::Node> ammoNode = mAmmunition->getNode();
osg::MatrixList mats = ammoNode->getWorldMatrices();
if (mats.empty())
osg::NodePathList nodepaths = ammoNode->getParentalNodePaths();
if (nodepaths.empty())
return;
osg::Vec3f launchPos = mats[0].getTrans();
osg::Vec3f launchPos = osg::computeLocalToWorld(nodepaths[0]).getTrans();
float fProjectileMinSpeed = gmst.find("fProjectileMinSpeed")->getFloat();
float fProjectileMaxSpeed = gmst.find("fProjectileMaxSpeed")->getFloat();

@ -449,5 +449,7 @@ op 0x2000300: EnableLevelupMenu
op 0x2000301: ToggleScripts
op 0x2000302: Fixme
op 0x2000303: Fixme, explicit
op 0x2000304: Show
op 0x2000305: Show, explicit
opcodes 0x2000304-0x3ffffff unused

@ -138,8 +138,7 @@ namespace MWScript
InterpreterContext::InterpreterContext (
MWScript::Locals *locals, MWWorld::Ptr reference, const std::string& targetId)
: mLocals (locals), mReference (reference),
mActivationHandled (false), mTargetId (targetId)
: mLocals (locals), mReference (reference), mTargetId (targetId)
{
// 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
@ -477,37 +476,10 @@ namespace MWScript
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)
{
boost::shared_ptr<MWWorld::Action> action = (ptr.getClass().activate(ptr, actor));
action->execute (actor);
if (mActivated == ptr)
{
mActivationHandled = true;
mActivated = MWWorld::Ptr();
}
}
float InterpreterContext::getSecondsPassed() const

@ -27,9 +27,6 @@ namespace MWScript
Locals *mLocals;
mutable MWWorld::Ptr mReference;
MWWorld::Ptr mActivated;
bool mActivationHandled;
std::string mTargetId;
/// 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;
///< @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);
///< Execute the activation action for this ptr. If ptr is mActivated, mark activation as handled.

@ -97,6 +97,32 @@ namespace MWScript
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)
{
ensure (script);

@ -51,6 +51,12 @@ namespace MWScript
/// \note Locals will be automatically configured first, if necessary
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.
///
/// \return Locals written?

@ -142,7 +142,7 @@ namespace MWScript
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);
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>
class OpShowVars : public Interpreter::Opcode0
{
@ -1265,6 +1330,8 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Misc::opcodeEnableTeleporting, new OpEnableTeleporting<true>);
interpreter.installSegment5 (Compiler::Misc::opcodeShowVars, new OpShowVars<ImplicitRef>);
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::opcodeToggleScripts, new OpToggleScripts);
interpreter.installSegment5 (Compiler::Misc::opcodeDisableLevitation, new OpEnableLevitation<false>);

@ -318,8 +318,8 @@ namespace MWScript
ptr = MWBase::Environment::get().getWorld()->moveObject(ptr,store,x,y,z);
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr);
float ax = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[0]);
float ay = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[1]);
float ax = ptr.getRefData().getPosition().rot[0];
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)
// 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.
@ -374,14 +374,14 @@ namespace MWScript
}
dynamic_cast<MWScript::InterpreterContext&>(runtime.getContext()).updatePtr(base,ptr);
float ax = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[0]);
float ay = osg::RadiansToDegrees(ptr.getRefData().getPosition().rot[1]);
float ax = ptr.getRefData().getPosition().rot[0];
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)
// 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.
if(ptr != MWMechanics::getPlayer())
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);
}
};
@ -434,7 +434,7 @@ namespace MWScript
pos.rot[2] = osg::DegreesToRadians(zRotDegrees);
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID);
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);
}
}
@ -482,7 +482,7 @@ namespace MWScript
pos.rot[2] = osg::DegreesToRadians(zRotDegrees);
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID);
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);
}
};
@ -516,38 +516,10 @@ namespace MWScript
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
MWWorld::CellStore* store = actor.getCell();
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), itemID, 1);
ref.getPtr().getCellRef().setPosition(ipos);
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos);
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), actor, actor.getCell(), direction, distance);
}
}
};

@ -366,7 +366,7 @@ namespace MWSound
else
filelist = mMusicFiles[mCurrentPlaylist];
if(!filelist.size())
if(filelist.empty())
return;
int i = Misc::Rng::rollDice(filelist.size());

@ -493,7 +493,8 @@ void MWState::StateManager::loadGame (const Character *character, const std::str
// but some mods may be using it as a reload detector.
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();
}
catch (const std::exception& e)

@ -166,6 +166,8 @@ namespace MWWorld
, mBulletShapeManager(bulletShapeManager)
, mTerrain(terrain)
, mExpiryDelay(0.0)
, mMinCacheSize(0)
, mMaxCacheSize(0)
{
}
@ -197,6 +199,23 @@ namespace MWWorld
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));
mWorkQueue->addWorkItem(item);
@ -210,11 +229,9 @@ namespace MWWorld
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();)
{
if (it->second.mTimeStamp < timestamp - mExpiryDelay)
if (mPreloadCells.size() >= mMinCacheSize && it->second.mTimeStamp < timestamp - mExpiryDelay)
mPreloadCells.erase(it++);
else
++it;
@ -229,6 +246,16 @@ namespace MWWorld
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)
{
mWorkQueue = workQueue;

@ -38,6 +38,12 @@ namespace MWWorld
/// How long to keep a preloaded cell in cache after it's no longer requested.
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);
private:
@ -46,6 +52,8 @@ namespace MWWorld
Terrain::World* mTerrain;
osg::ref_ptr<SceneUtil::WorkQueue> mWorkQueue;
double mExpiryDelay;
unsigned int mMinCacheSize;
unsigned int mMaxCacheSize;
struct PreloadEntry
{

@ -889,11 +889,19 @@ namespace MWWorld
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()
{
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)
{
mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp();
@ -902,21 +910,25 @@ namespace MWWorld
Ptr ptr (&*it, this);
ptr.getClass().respawn(ptr);
}
for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it)
{
Ptr ptr (&*it, this);
ptr.getClass().respawn(ptr);
}
for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)
{
Ptr ptr (&*it, this);
ptr.getClass().respawn(ptr);
}
for (CellRefList<ESM::CreatureLevList>::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it)
{
Ptr ptr (&*it, this);
ptr.getClass().respawn(ptr);
}
}
for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it)
{
Ptr ptr (&*it, this);
clearCorpse(ptr);
ptr.getClass().respawn(ptr);
}
for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)
{
Ptr ptr (&*it, this);
clearCorpse(ptr);
ptr.getClass().respawn(ptr);
}
for (CellRefList<ESM::CreatureLevList>::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it)
{
Ptr ptr (&*it, this);
// no need to clearCorpse, handled as part of mCreatures
ptr.getClass().respawn(ptr);
}
}
}

@ -66,11 +66,6 @@ MWWorld::LocalScripts::LocalScripts (const MWWorld::ESMStore& store) : mStore (s
mIter = mScripts.end();
}
void MWWorld::LocalScripts::setIgnore (const ConstPtr& ptr)
{
mIgnore = ptr;
}
void MWWorld::LocalScripts::startIteration()
{
mIter = mScripts.begin();
@ -81,11 +76,8 @@ bool MWWorld::LocalScripts::getNext(std::pair<std::string, Ptr>& script)
while (mIter!=mScripts.end())
{
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;
}

@ -17,17 +17,12 @@ namespace MWWorld
{
std::list<std::pair<std::string, Ptr> > mScripts;
std::list<std::pair<std::string, Ptr> >::iterator mIter;
MWWorld::ConstPtr mIgnore;
const MWWorld::ESMStore& mStore;
public:
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();
///< Set the iterator to the begin of the script list.

@ -8,8 +8,18 @@
#include "../mwbase/environment.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
{
void RefData::copy (const RefData& refData)
{
mBaseNode = refData.mBaseNode;
@ -19,6 +29,7 @@ namespace MWWorld
mPosition = refData.mPosition;
mChanged = refData.mChanged;
mDeletedByContentFile = refData.mDeletedByContentFile;
mFlags = refData.mFlags;
mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0;
}
@ -32,7 +43,7 @@ namespace MWWorld
}
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)
{
@ -45,7 +56,7 @@ namespace MWWorld
: mBaseNode(0), mDeletedByContentFile(false), mEnabled (true),
mCount (1), mPosition (cellRef.mPos),
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),
mPosition (objectState.mPosition),
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)
@ -80,6 +95,7 @@ namespace MWWorld
objectState.mEnabled = mEnabled;
objectState.mCount = mCount;
objectState.mPosition = mPosition;
objectState.mFlags = mFlags;
}
RefData& RefData::operator= (const RefData& refData)
@ -219,4 +235,38 @@ namespace MWWorld
{
return mChanged;
}
bool RefData::activate()
{
if (!(mFlags & Flag_SuppressActivate))
return true;
else
{
mFlags |= Flag_OnActivate;
return false;
}
}
bool RefData::onActivate()
{
mFlags |= Flag_SuppressActivate;
if (mFlags & Flag_OnActivate)
{
mFlags &= (~Flag_OnActivate);
return true;
}
return false;
}
bool RefData::activateByScript()
{
if (mFlags & Flag_SuppressActivate)
{
mFlags &= (~Flag_SuppressActivate);
return true;
}
else
return false;
}
}

@ -50,6 +50,8 @@ namespace MWWorld
bool mChanged;
unsigned int mFlags;
public:
RefData();
@ -122,6 +124,12 @@ namespace MWWorld
const CustomData *getCustomData() const;
bool activate();
bool onActivate();
bool activateByScript();
bool hasChanged() const;
///< Has this RefData changed since it was originally loaded?
};

@ -240,7 +240,7 @@ namespace MWWorld
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);
@ -268,14 +268,22 @@ namespace MWWorld
mPhysics->addHeightField (data->mHeights, cell->getCell()->getGridX(), cell->getCell()->getGridY(),
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
// do this before insertCell, to make sure we don't add scripts from levelled creature spawning twice
MWBase::Environment::get().getWorld()->getLocalScripts().addCell (cell);
if (respawn)
cell->respawn();
// ... then references. This is important for adjustPosition to work correctly.
/// \todo rescale depending on the state of a new GMST
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::ScopedLoad load(loadingListener);
@ -403,7 +411,7 @@ namespace MWWorld
{
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);
MWBase::Environment::get().getWindowManager()->changeCell(current);
mCellChanged = true;
if (changeEvent)
mCellChanged = true;
mPreloader->updateCache(mRendering.getReferenceTime());
}
@ -463,9 +472,11 @@ namespace MWWorld
mPhysics->setUnrefQueue(rendering.getUnrefQueue());
float cacheExpiryDelay = Settings::Manager::getFloat("cache expiry delay", "Cells");
rendering.getResourceSystem()->setExpiryDelay(cacheExpiryDelay);
mPreloader->setExpiryDelay(cacheExpiryDelay);
rendering.getResourceSystem()->setExpiryDelay(Settings::Manager::getFloat("cache expiry delay", "Cells"));
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()
@ -482,7 +493,7 @@ namespace MWWorld
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);
bool loadcell = (mCurrentCell == NULL);
@ -528,7 +539,7 @@ namespace MWWorld
loadingListener->setProgressRange(refsToLoad);
// Load cell.
loadCell (cell, loadingListener);
loadCell (cell, loadingListener, changeEvent);
changePlayerCell(cell, position, true);
@ -538,21 +549,24 @@ namespace MWWorld
// Sky system
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);
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 y = 0;
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);
changePlayerCell(current, position, adjustPlayerPos);

@ -71,7 +71,7 @@ namespace MWWorld
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
void changeCellGrid (int X, int Y);
void changeCellGrid (int X, int Y, bool changeEvent = true);
void getGridCenter(int& cellX, int& cellY);
@ -90,7 +90,7 @@ namespace MWWorld
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);
@ -103,11 +103,13 @@ namespace MWWorld
bool hasCellChanged() const;
///< 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.
/// @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.
/// @param changeEvent Set cellChanged flag?
void changeToVoid();
///< Change into a void

@ -962,11 +962,11 @@ namespace MWWorld
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();
if (mCurrentWorldSpace != cellName)
if (changeEvent && mCurrentWorldSpace != cellName)
{
// changed worldspace
mProjectileManager->clear();
@ -976,34 +976,34 @@ namespace MWWorld
}
removeContainerScripts(getPlayerPtr());
mWorldScene->changeToInteriorCell(cellName, position);
mWorldScene->changeToInteriorCell(cellName, position, changeEvent);
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
}
void World::changeToExteriorCell (const ESM::Position& position)
void World::changeToExteriorCell (const ESM::Position& position, bool changeEvent)
{
mPhysics->clearQueuedMovement();
if (mCurrentWorldSpace != "sys::default") // FIXME
if (changeEvent && mCurrentWorldSpace != "sys::default") // FIXME
{
// changed worldspace
mProjectileManager->clear();
mRendering->notifyWorldSpaceChanged();
}
removeContainerScripts(getPlayerPtr());
mWorldScene->changeToExteriorCell(position, true);
mWorldScene->changeToExteriorCell(position, true, changeEvent);
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;
if (cellId.mPaged)
changeToExteriorCell (position);
changeToExteriorCell (position, changeEvent);
else
changeToInteriorCell (cellId.mWorldspace, position);
changeToInteriorCell (cellId.mWorldspace, position, changeEvent);
}
void World::markCellAsUnchanged()
@ -1051,9 +1051,9 @@ namespace MWWorld
if(!node) node = anim->getNode("Bip01 Head");
if(node)
{
osg::MatrixList mats = node->getWorldMatrices();
if(!mats.empty())
return mats[0];
osg::NodePathList nodepaths = node->getParentalNodePaths();
if(!nodepaths.empty())
return osg::computeLocalToWorld(nodepaths[0]);
}
}
return osg::Matrixf::translate(actor.getRefData().getPosition().asVec3());
@ -1325,11 +1325,50 @@ namespace MWWorld
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);
}
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
{
const int cellSize = 8192;
@ -3025,23 +3064,9 @@ namespace MWWorld
if (selectedCreature.empty())
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);
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)
{
MWScript::InterpreterContext interpreterContext (&object.getRefData().getLocals(), object);
interpreterContext.activate (object);
std::string script = object.getClass().getScript (object);
breakInvisibility(actor);
if (mScriptsEnabled)
{
if (!script.empty())
if (object.getRefData().activate())
{
getLocalScripts().setIgnore (object);
MWBase::Environment::get().getScriptManager()->run (script, interpreterContext);
boost::shared_ptr<MWWorld::Action> action = (object.getClass().activate(object, actor));
action->execute (actor);
}
if (!interpreterContext.hasActivationBeenHandled())
interpreterContext.executeActivation(object, actor);
}
else
interpreterContext.executeActivation(object, actor);
}
struct ResetActorsVisitor

@ -324,15 +324,16 @@ namespace MWWorld
virtual float getTimeScaleFactor() const;
virtual void changeToInteriorCell (const std::string& cellName,
const ESM::Position& position);
virtual void changeToInteriorCell (const std::string& cellName, const ESM::Position& position, bool changeEvent = true);
///< 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.
///< @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);
///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool changeEvent=true);
///< @param changeEvent If false, do not trigger cell change flag or detect worldspace changes
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.
@ -366,8 +367,12 @@ namespace MWWorld
/// \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 MWWorld::Ptr safePlaceObject(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.
virtual MWWorld::Ptr placeObject(const MWWorld::ConstPtr& ptr, MWWorld::CellStore* cell, ESM::Position pos);
///< 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();

@ -44,6 +44,10 @@ add_component_dir (resource
scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem resourcemanager
)
add_component_dir (shader
shadermanager shadervisitor
)
add_component_dir (sceneutil
clone attach visitor util statesetupdater controller skeleton riggeometry lightcontroller
lightmanager lightutil positionattitudetransform workqueue unrefqueue

@ -300,6 +300,7 @@ namespace Compiler
extensions.registerInstruction ("disableteleporting", "", opcodeDisableTeleporting);
extensions.registerInstruction ("enableteleporting", "", opcodeEnableTeleporting);
extensions.registerInstruction ("showvars", "", opcodeShowVars, opcodeShowVarsExplicit);
extensions.registerInstruction ("show", "c", opcodeShow, opcodeShowExplicit);
extensions.registerInstruction ("sv", "", opcodeShowVars, opcodeShowVarsExplicit);
extensions.registerInstruction("tgm", "", opcodeToggleGodMode);
extensions.registerInstruction("togglegodmode", "", opcodeToggleGodMode);

@ -277,6 +277,8 @@ namespace Compiler
const int opcodeEnableTeleporting = 0x2000216;
const int opcodeShowVars = 0x200021d;
const int opcodeShowVarsExplicit = 0x200021e;
const int opcodeShow = 0x2000304;
const int opcodeShowExplicit = 0x2000305;
const int opcodeToggleGodMode = 0x200021f;
const int opcodeToggleScripts = 0x2000301;
const int opcodeDisableLevitation = 0x2000220;

@ -87,6 +87,10 @@ void ESM::CreatureStats::load (ESMReader &esm)
mDeathAnimation = 0;
esm.getHNOT (mDeathAnimation, "DANM");
mTimeOfDeath.mDay = 0;
mTimeOfDeath.mHour = 0;
esm.getHNOT (mTimeOfDeath, "DTIM");
mSpells.load(esm);
mActiveSpells.load(esm);
mAiSequence.load(esm);
@ -193,6 +197,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
if (mDeathAnimation)
esm.writeHNT ("DANM", mDeathAnimation);
if (mTimeOfDeath.mHour != 0 && mTimeOfDeath.mDay != 0)
esm.writeHNT ("DTIM", mTimeOfDeath);
mSpells.save(esm);
mActiveSpells.save(esm);
mAiSequence.save(esm);

@ -57,6 +57,7 @@ namespace ESM
bool mRecalcDynamicStats;
int mDrawState;
unsigned char mDeathAnimation;
ESM::TimeStamp mTimeOfDeath;
int mLevel;

@ -46,6 +46,9 @@ struct Land
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
static const int LAND_SIZE = 65;

@ -53,14 +53,14 @@ void ESM::Header::load (ESMReader &esm)
{
esm.getSubHeader();
mSCRD.resize(esm.getSubSize());
if (mSCRD.size())
if (!mSCRD.empty())
esm.getExact(&mSCRD[0], mSCRD.size());
}
if (esm.isNextSub("SCRS"))
{
esm.getSubHeader();
mSCRS.resize(esm.getSubSize());
if (mSCRS.size())
if (!mSCRS.empty())
esm.getExact(&mSCRS[0], mSCRS.size());
}
}

@ -27,6 +27,9 @@ void ESM::ObjectState::load (ESMReader &esm)
if (esm.isNextSub("LROT"))
esm.skipHSub(); // local rotation, no longer used
mFlags = 0;
esm.getHNOT (mFlags, "FLAG");
// obsolete
int unused;
esm.getHNOT(unused, "LTIM");
@ -55,6 +58,9 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const
if (!inInventory)
esm.writeHNT ("POS_", mPosition, 24);
if (mFlags != 0)
esm.writeHNT ("FLAG", mFlags);
if (!mHasCustomState)
esm.writeHNT ("HCUS", false);
}
@ -70,6 +76,7 @@ void ESM::ObjectState::blank()
mPosition.pos[i] = 0;
mPosition.rot[i] = 0;
}
mFlags = 0;
mHasCustomState = true;
}

@ -24,6 +24,7 @@ namespace ESM
unsigned char mEnabled;
int mCount;
ESM::Position mPosition;
unsigned int mFlags;
// Is there any class-specific state following the ObjectState
bool mHasCustomState;

@ -16,10 +16,14 @@
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)
, mNormalMapPattern(normalMapPattern)
, mAutoUseNormalMaps(autoUseNormalMaps)
, mSpecularMapPattern(specularMapPattern)
, mAutoUseSpecularMaps(autoUseSpecularMaps)
{
}
@ -508,9 +512,11 @@ namespace ESMTerrain
return found->second;
Terrain::LayerInfo info;
info.mParallax = false;
//info.mParallax = false;
info.mSpecular = false;
info.mDiffuseMap = texture;
/*
std::string texture_ = texture;
boost::replace_last(texture_, ".", "_nh.");
@ -519,20 +525,24 @@ namespace ESMTerrain
info.mNormalMap = texture_;
info.mParallax = true;
}
else
*/
if (mAutoUseNormalMaps)
{
texture_ = texture;
boost::replace_last(texture_, ".", "_n.");
std::string texture_ = texture;
boost::replace_last(texture_, ".", mNormalMapPattern + ".");
if (mVFS->exists(texture_))
info.mNormalMap = texture_;
}
texture_ = texture;
boost::replace_last(texture_, ".", "_diffusespec.");
if (mVFS->exists(texture_))
if (mAutoUseSpecularMaps)
{
info.mDiffuseMap = texture_;
info.mSpecular = true;
std::string texture_ = texture;
boost::replace_last(texture_, ".", mSpecularMapPattern + ".");
if (mVFS->exists(texture_))
{
info.mDiffuseMap = texture_;
info.mSpecular = true;
}
}
mLayerInfoMap[texture] = info;
@ -544,7 +554,7 @@ namespace ESMTerrain
{
Terrain::LayerInfo info;
info.mDiffuseMap = "textures\\_land_default.dds";
info.mParallax = false;
//info.mParallax = false;
info.mSpecular = false;
return info;
}

@ -27,7 +27,7 @@ namespace ESMTerrain
virtual const ESM::LandTexture* getLandTexture(int index, short plugin) = 0;
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
/// 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;
OpenThreads::Mutex mLayerInfoMutex;
std::string mNormalMapPattern;
bool mAutoUseNormalMaps;
std::string mSpecularMapPattern;
bool mAutoUseSpecularMaps;
Terrain::LayerInfo getLayerInfo(const std::string& texture);
};

@ -26,13 +26,14 @@ namespace Interpreter{
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;
std::ostringstream retval;
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);
std::string temp = Misc::StringUtils::lowerCase(text.substr(i+1, 100));
@ -113,7 +114,7 @@ namespace Interpreter{
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))){
retval << context.getNPCFaction();
}
@ -207,15 +208,15 @@ namespace Interpreter{
return retval.str ();
}
std::string fixDefinesDialog(std::string text, Context& context){
return fixDefinesReal(text, '%', false, context);
std::string fixDefinesDialog(const std::string& text, Context& context){
return fixDefinesReal(text, true, context);
}
std::string fixDefinesMsgBox(std::string text, Context& context){
return fixDefinesReal(text, '^', false, context);
std::string fixDefinesMsgBox(const std::string& text, Context& context){
return fixDefinesReal(text, false, context);
}
std::string fixDefinesBook(std::string text, Context& context){
return fixDefinesReal(text, '%', true, context);
std::string fixDefinesBook(const std::string& text, Context& context){
return fixDefinesReal(text, false, context);
}
}

@ -5,9 +5,9 @@
#include "context.hpp"
namespace Interpreter{
std::string fixDefinesDialog(std::string text, Context& context);
std::string fixDefinesMsgBox(std::string text, Context& context);
std::string fixDefinesBook(std::string text, Context& context);
std::string fixDefinesDialog(const std::string& text, Context& context);
std::string fixDefinesMsgBox(const std::string& text, Context& context);
std::string fixDefinesBook(const std::string& text, Context& context);
}
#endif

@ -154,7 +154,7 @@ void NiFloatData::read(NIFStream *nif)
void NiPixelData::read(NIFStream *nif)
{
nif->getInt(); // always 0 or 1
fmt = (Format)nif->getUInt();
rmask = nif->getInt(); // usually 0xff
gmask = nif->getInt(); // usually 0xff00
@ -169,19 +169,23 @@ void NiPixelData::read(NIFStream *nif)
mips = nif->getInt();
// Bytes per pixel, should be bpp * 8
/*int bytes =*/ nif->getInt();
/* int bytes = */ nif->getInt();
for(int i=0; i<mips; i++)
{
// Image size and offset in the following data field
/*int x =*/ nif->getInt();
/*int y =*/ nif->getInt();
/*int offset =*/ nif->getInt();
Mipmap m;
m.width = nif->getUInt();
m.height = nif->getUInt();
m.dataOffset = nif->getUInt();
mipmaps.push_back(m);
}
// Skip the data
// Read the data
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)

@ -105,9 +105,30 @@ public:
class NiPixelData : public Record
{
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;
int bpp, mips;
struct Mipmap
{
int width, height;
int dataOffset;
};
std::vector<Mipmap> mipmaps;
std::vector<unsigned char> data;
void read(NIFStream *nif);
};

@ -19,14 +19,20 @@ void NiTextureEffect::read(NIFStream *nif)
{
NiDynamicEffect::read(nif);
/*
3 x Vector4 = [1,0,0,0]
int = 2
int = 0 or 3
int = 2
int = 2
*/
nif->skip(16*4);
// Model Projection Matrix
nif->skip(3 * 3 * sizeof(float));
// Model Projection Transform
nif->skip(3 * sizeof(float));
// Texture Filtering
nif->skip(4);
clamp = nif->getUInt();
textureType = (TextureType)nif->getUInt();
coordGenType = (CoordGenType)nif->getUInt();
texture.read(nif);

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

Loading…
Cancel
Save