mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 20:26:43 +00:00 
			
		
		
		
	Merge branch 'master' into move
Conflicts: apps/opencs/view/render/object.cpp
This commit is contained in:
		
						commit
						6f619ea85f
					
				
					 138 changed files with 4601 additions and 1029 deletions
				
			
		|  | @ -5,4 +5,4 @@ mkdir build | |||
| cd build | ||||
| 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> | ||||
|  |  | |||
							
								
								
									
										79
									
								
								apps/opencs/model/tools/journalcheck.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								apps/opencs/model/tools/journalcheck.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | |||
| #include "journalcheck.hpp" | ||||
| 
 | ||||
| #include <set> | ||||
| #include <sstream> | ||||
| 
 | ||||
| CSMTools::JournalCheckStage::JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue> &journals, | ||||
|     const CSMWorld::InfoCollection& journalInfos) | ||||
|     : mJournals(journals), mJournalInfos(journalInfos) | ||||
| {} | ||||
| 
 | ||||
| int CSMTools::JournalCheckStage::setup() | ||||
| { | ||||
|     return mJournals.getSize(); | ||||
| } | ||||
| 
 | ||||
| void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages) | ||||
| { | ||||
|     const CSMWorld::Record<ESM::Dialogue> &journalRecord = mJournals.getRecord(stage); | ||||
| 
 | ||||
|     if (journalRecord.isDeleted()) | ||||
|         return; | ||||
| 
 | ||||
|     const ESM::Dialogue &journal = journalRecord.get(); | ||||
|     int statusNamedCount = 0; | ||||
|     int totalInfoCount = 0; | ||||
|     std::set<int> questIndices; | ||||
| 
 | ||||
|     CSMWorld::InfoCollection::Range range = mJournalInfos.getTopicRange(journal.mId); | ||||
| 
 | ||||
|     for (CSMWorld::InfoCollection::RecordConstIterator it = range.first; it != range.second; ++it) | ||||
|     { | ||||
|         const CSMWorld::Record<CSMWorld::Info> infoRecord = (*it); | ||||
| 
 | ||||
|         if (infoRecord.isDeleted()) | ||||
|             continue; | ||||
| 
 | ||||
|         const CSMWorld::Info& journalInfo = infoRecord.get(); | ||||
| 
 | ||||
|         totalInfoCount += 1; | ||||
| 
 | ||||
|         if (journalInfo.mQuestStatus == ESM::DialInfo::QS_Name) | ||||
|         { | ||||
|             statusNamedCount += 1; | ||||
|         } | ||||
| 
 | ||||
|         if (journalInfo.mResponse.empty()) | ||||
|         { | ||||
|             CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); | ||||
| 
 | ||||
|             messages.add(id, "Journal Info: missing description", "", CSMDoc::Message::Severity_Warning); | ||||
|         } | ||||
| 
 | ||||
|         std::pair<std::set<int>::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex); | ||||
| 
 | ||||
|         // Duplicate index
 | ||||
|         if (result.second == false) | ||||
|         { | ||||
|             CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId); | ||||
| 
 | ||||
|             std::ostringstream stream; | ||||
|             stream << "Journal: duplicated quest index " << journalInfo.mData.mJournalIndex; | ||||
| 
 | ||||
|             messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (totalInfoCount == 0) | ||||
|     { | ||||
|         CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); | ||||
| 
 | ||||
|         messages.add(id, "Journal: no defined Journal Infos", "", CSMDoc::Message::Severity_Warning); | ||||
|     } | ||||
|     else if (statusNamedCount > 1) | ||||
|     { | ||||
|         CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Journal, journal.mId); | ||||
| 
 | ||||
|         messages.add(id, "Journal: multiple infos with quest status \"Named\"", "", CSMDoc::Message::Severity_Error); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										35
									
								
								apps/opencs/model/tools/journalcheck.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								apps/opencs/model/tools/journalcheck.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| #ifndef CSM_TOOLS_JOURNALCHECK_H | ||||
| #define CSM_TOOLS_JOURNALCHECK_H | ||||
| 
 | ||||
| #include <components/esm/loaddial.hpp> | ||||
| 
 | ||||
| #include "../world/idcollection.hpp" | ||||
| #include "../world/infocollection.hpp" | ||||
| 
 | ||||
| #include "../doc/stage.hpp" | ||||
| 
 | ||||
| namespace CSMTools | ||||
| { | ||||
|     /// \brief VerifyStage: make sure that journal infos are good
 | ||||
|     class JournalCheckStage : public CSMDoc::Stage | ||||
|     { | ||||
|     public: | ||||
| 
 | ||||
|         JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue>& journals, | ||||
|             const CSMWorld::InfoCollection& journalInfos); | ||||
| 
 | ||||
|         virtual int setup(); | ||||
|         ///< \return number of steps
 | ||||
| 
 | ||||
|         virtual void perform(int stage, CSMDoc::Messages& messages); | ||||
|         ///< Messages resulting from this stage will be appended to \a messages
 | ||||
| 
 | ||||
|     private: | ||||
| 
 | ||||
|         const CSMWorld::IdCollection<ESM::Dialogue>& mJournals; | ||||
|         const CSMWorld::InfoCollection& mJournalInfos; | ||||
| 
 | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -30,6 +30,8 @@ | |||
| #include "magiceffectcheck.hpp" | ||||
| #include "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); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										441
									
								
								apps/opencs/model/tools/topicinfocheck.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										441
									
								
								apps/opencs/model/tools/topicinfocheck.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,441 @@ | |||
| #include "topicinfocheck.hpp" | ||||
| 
 | ||||
| #include <sstream> | ||||
| 
 | ||||
| #include "../world/infoselectwrapper.hpp" | ||||
| 
 | ||||
| CSMTools::TopicInfoCheckStage::TopicInfoCheckStage( | ||||
|     const CSMWorld::InfoCollection& topicInfos, | ||||
|     const CSMWorld::IdCollection<CSMWorld::Cell>& cells, | ||||
|     const CSMWorld::IdCollection<ESM::Class>& classes, | ||||
|     const CSMWorld::IdCollection<ESM::Faction>& factions, | ||||
|     const CSMWorld::IdCollection<ESM::GameSetting>& gmsts, | ||||
|     const CSMWorld::IdCollection<ESM::Global>& globals, | ||||
|     const CSMWorld::IdCollection<ESM::Dialogue>& journals, | ||||
|     const CSMWorld::IdCollection<ESM::Race>& races, | ||||
|     const CSMWorld::IdCollection<ESM::Region>& regions, | ||||
|     const CSMWorld::IdCollection<ESM::Dialogue> &topics, | ||||
|     const CSMWorld::RefIdData& referencables, | ||||
|     const CSMWorld::Resources& soundFiles) | ||||
|     : mTopicInfos(topicInfos), | ||||
|       mCells(cells), | ||||
|       mClasses(classes), | ||||
|       mFactions(factions), | ||||
|       mGameSettings(gmsts), | ||||
|       mGlobals(globals), | ||||
|       mJournals(journals), | ||||
|       mRaces(races), | ||||
|       mRegions(regions), | ||||
|       mTopics(topics), | ||||
|       mReferencables(referencables), | ||||
|       mSoundFiles(soundFiles) | ||||
| {} | ||||
| 
 | ||||
| int CSMTools::TopicInfoCheckStage::setup() | ||||
| { | ||||
|     // Generate list of cell names for reference checking
 | ||||
| 
 | ||||
|     mCellNames.clear(); | ||||
|     for (int i = 0; i < mCells.getSize(); ++i) | ||||
|     { | ||||
|         const CSMWorld::Record<CSMWorld::Cell>& cellRecord = mCells.getRecord(i); | ||||
| 
 | ||||
|         if (cellRecord.isDeleted()) | ||||
|             continue; | ||||
| 
 | ||||
|         mCellNames.insert(cellRecord.get().mName); | ||||
|     } | ||||
|     // Cell names can also include region names
 | ||||
|     for (int i = 0; i < mRegions.getSize(); ++i) | ||||
|     { | ||||
|         const CSMWorld::Record<ESM::Region>& regionRecord = mRegions.getRecord(i); | ||||
| 
 | ||||
|         if (regionRecord.isDeleted()) | ||||
|             continue; | ||||
| 
 | ||||
|         mCellNames.insert(regionRecord.get().mName); | ||||
|     } | ||||
|     // Default cell name
 | ||||
|     int index = mGameSettings.searchId("sDefaultCellname"); | ||||
|     if (index != -1) | ||||
|     { | ||||
|         const CSMWorld::Record<ESM::GameSetting>& gmstRecord = mGameSettings.getRecord(index); | ||||
| 
 | ||||
|         if (!gmstRecord.isDeleted() && gmstRecord.get().mValue.getType() == ESM::VT_String) | ||||
|         { | ||||
|             mCellNames.insert(gmstRecord.get().mValue.getString()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return mTopicInfos.getSize(); | ||||
| } | ||||
| 
 | ||||
| void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& messages) | ||||
| { | ||||
|     const CSMWorld::Record<CSMWorld::Info>& infoRecord = mTopicInfos.getRecord(stage); | ||||
| 
 | ||||
|     if (infoRecord.isDeleted()) | ||||
|         return; | ||||
| 
 | ||||
|     const CSMWorld::Info& topicInfo = infoRecord.get(); | ||||
| 
 | ||||
|     // There should always be a topic that matches
 | ||||
|     int topicIndex = mTopics.searchId(topicInfo.mTopicId); | ||||
| 
 | ||||
|     const CSMWorld::Record<ESM::Dialogue>& topicRecord = mTopics.getRecord(topicIndex); | ||||
| 
 | ||||
|     if (topicRecord.isDeleted()) | ||||
|         return; | ||||
| 
 | ||||
|     const ESM::Dialogue& topic = topicRecord.get(); | ||||
| 
 | ||||
|     CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_TopicInfo, topicInfo.mId); | ||||
| 
 | ||||
|     // Check fields
 | ||||
| 
 | ||||
|     if (!topicInfo.mActor.empty()) | ||||
|     { | ||||
|         verifyActor(topicInfo.mActor, id, messages); | ||||
|     } | ||||
| 
 | ||||
|     if (!topicInfo.mClass.empty()) | ||||
|     { | ||||
|         verifyId(topicInfo.mClass, mClasses, id, messages); | ||||
|     } | ||||
| 
 | ||||
|     if (!topicInfo.mCell.empty()) | ||||
|     { | ||||
|         verifyCell(topicInfo.mCell, id, messages); | ||||
|     } | ||||
| 
 | ||||
|     if (!topicInfo.mFaction.empty()) | ||||
|     { | ||||
|         if (verifyId(topicInfo.mFaction, mFactions, id, messages)) | ||||
|         { | ||||
|             verifyFactionRank(topicInfo.mFaction, topicInfo.mData.mRank, id, messages); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (!topicInfo.mPcFaction.empty()) | ||||
|     { | ||||
|         if (verifyId(topicInfo.mPcFaction, mFactions, id, messages)) | ||||
|         { | ||||
|             verifyFactionRank(topicInfo.mPcFaction, topicInfo.mData.mPCrank, id, messages); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (topicInfo.mData.mGender < -1 || topicInfo.mData.mGender > 1) | ||||
|     { | ||||
|         std::ostringstream stream; | ||||
|         messages.add(id, "Gender: Value is invalid", "", CSMDoc::Message::Severity_Error); | ||||
|     } | ||||
| 
 | ||||
|     if (!topicInfo.mRace.empty()) | ||||
|     { | ||||
|         verifyId(topicInfo.mRace, mRaces, id, messages); | ||||
|     } | ||||
| 
 | ||||
|     if (!topicInfo.mSound.empty()) | ||||
|     { | ||||
|         verifySound(topicInfo.mSound, id, messages); | ||||
|     } | ||||
| 
 | ||||
|     if (topicInfo.mResponse.empty() && topic.mType != ESM::Dialogue::Voice) | ||||
|     { | ||||
|         messages.add(id, "Response is empty", "", CSMDoc::Message::Severity_Warning); | ||||
|     } | ||||
| 
 | ||||
|     // Check info conditions
 | ||||
| 
 | ||||
|     for (std::vector<ESM::DialInfo::SelectStruct>::const_iterator it = topicInfo.mSelects.begin(); | ||||
|          it != topicInfo.mSelects.end(); ++it) | ||||
|     { | ||||
|         verifySelectStruct((*it), id, messages); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Verification functions
 | ||||
| 
 | ||||
| bool CSMTools::TopicInfoCheckStage::verifyActor(const std::string& actor, const CSMWorld::UniversalId& id, | ||||
|     CSMDoc::Messages& messages) | ||||
| { | ||||
|     const std::string specifier = "Actor"; | ||||
| 
 | ||||
|     CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(actor); | ||||
| 
 | ||||
|     if (index.first == -1) | ||||
|     { | ||||
|         writeMissingIdError(specifier, actor, id, messages); | ||||
|         return false; | ||||
|     } | ||||
|     else if (mReferencables.getRecord(index).isDeleted()) | ||||
|     { | ||||
|         writeDeletedRecordError(specifier, actor, id, messages); | ||||
|         return false; | ||||
|     } | ||||
|     else if (index.second != CSMWorld::UniversalId::Type_Npc && index.second != CSMWorld::UniversalId::Type_Creature) | ||||
|     { | ||||
|         writeInvalidTypeError(specifier, actor, index.second, "NPC or Creature", id, messages); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool CSMTools::TopicInfoCheckStage::verifyCell(const std::string& cell, const CSMWorld::UniversalId& id, | ||||
|     CSMDoc::Messages& messages) | ||||
| { | ||||
|     const std::string specifier = "Cell"; | ||||
| 
 | ||||
|     if (mCellNames.find(cell) == mCellNames.end()) | ||||
|     { | ||||
|         writeMissingIdError(specifier, cell, id, messages); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool CSMTools::TopicInfoCheckStage::verifyFactionRank(const std::string& factionName, int rank, const CSMWorld::UniversalId& id, | ||||
|     CSMDoc::Messages& messages) | ||||
| { | ||||
|     if (rank < -1) | ||||
|     { | ||||
|         std::ostringstream stream; | ||||
|         stream << "Rank or PC Rank is set to " << rank << ", but should be set to -1 if no rank is required"; | ||||
| 
 | ||||
|         messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     int index = mFactions.searchId(factionName); | ||||
| 
 | ||||
|     const ESM::Faction &faction = mFactions.getRecord(index).get(); | ||||
| 
 | ||||
|     int limit = 0; | ||||
|     for (; limit < 10; ++limit) | ||||
|     { | ||||
|         if (faction.mRanks[limit].empty()) | ||||
|             break; | ||||
|     } | ||||
| 
 | ||||
|     if (rank >= limit) | ||||
|     { | ||||
|         std::ostringstream stream; | ||||
|         stream << "Rank or PC Rank is set to " << rank << " which is more than the maximum of " << limit - 1 | ||||
|                << " for the " << factionName << " faction"; | ||||
| 
 | ||||
|         messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool CSMTools::TopicInfoCheckStage::verifyItem(const std::string& item, const CSMWorld::UniversalId& id, | ||||
|     CSMDoc::Messages& messages) | ||||
| { | ||||
|     const std::string specifier = "Item"; | ||||
| 
 | ||||
|     CSMWorld::RefIdData::LocalIndex index = mReferencables.searchId(item); | ||||
| 
 | ||||
|     if (index.first == -1) | ||||
|     { | ||||
|         writeMissingIdError(specifier, item, id, messages); | ||||
|         return false; | ||||
|     } | ||||
|     else if (mReferencables.getRecord(index).isDeleted()) | ||||
|     { | ||||
|         writeDeletedRecordError(specifier, item, id, messages); | ||||
|         return false; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         switch (index.second) | ||||
|         { | ||||
|             case CSMWorld::UniversalId::Type_Potion: | ||||
|             case CSMWorld::UniversalId::Type_Apparatus: | ||||
|             case CSMWorld::UniversalId::Type_Armor: | ||||
|             case CSMWorld::UniversalId::Type_Book: | ||||
|             case CSMWorld::UniversalId::Type_Clothing: | ||||
|             case CSMWorld::UniversalId::Type_Ingredient: | ||||
|             case CSMWorld::UniversalId::Type_Light: | ||||
|             case CSMWorld::UniversalId::Type_Lockpick: | ||||
|             case CSMWorld::UniversalId::Type_Miscellaneous: | ||||
|             case CSMWorld::UniversalId::Type_Probe: | ||||
|             case CSMWorld::UniversalId::Type_Repair: | ||||
|             case CSMWorld::UniversalId::Type_Weapon: | ||||
|             case CSMWorld::UniversalId::Type_ItemLevelledList: | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 writeInvalidTypeError(specifier, item, index.second, "Potion, Armor, Book, etc.", id, messages); | ||||
|                 return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool CSMTools::TopicInfoCheckStage::verifySelectStruct(const ESM::DialInfo::SelectStruct& select, | ||||
|     const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) | ||||
| { | ||||
|     CSMWorld::ConstInfoSelectWrapper infoCondition(select); | ||||
| 
 | ||||
|     if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_None) | ||||
|     { | ||||
|         messages.add(id, "Invalid Info Condition: " + infoCondition.toString(), "", CSMDoc::Message::Severity_Error); | ||||
|         return false; | ||||
|     } | ||||
|     else if (!infoCondition.variantTypeIsValid()) | ||||
|     { | ||||
|         std::ostringstream stream; | ||||
|         stream << "Info Condition: Value for \"" << infoCondition.toString() << "\" has a type of "; | ||||
| 
 | ||||
|         switch (select.mValue.getType()) | ||||
|         { | ||||
|             case ESM::VT_None:   stream << "None"; break; | ||||
|             case ESM::VT_Short:  stream << "Short"; break; | ||||
|             case ESM::VT_Int:    stream << "Int"; break; | ||||
|             case ESM::VT_Long:   stream << "Long"; break; | ||||
|             case ESM::VT_Float:  stream << "Float"; break; | ||||
|             case ESM::VT_String: stream << "String"; break; | ||||
|             default:             stream << "Unknown"; break; | ||||
|         } | ||||
| 
 | ||||
|         messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); | ||||
|         return false; | ||||
|     } | ||||
|     else if (infoCondition.conditionIsAlwaysTrue()) | ||||
|     { | ||||
|         std::ostringstream stream; | ||||
|         stream << "Info Condition: " << infoCondition.toString() << " is always true"; | ||||
| 
 | ||||
|         messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); | ||||
|         return false; | ||||
|     } | ||||
|     else if (infoCondition.conditionIsNeverTrue()) | ||||
|     { | ||||
|         std::ostringstream stream; | ||||
|         stream << "Info Condition: " << infoCondition.toString() << " is never true"; | ||||
| 
 | ||||
|         messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Warning); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     // Id checks
 | ||||
|     if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Global && | ||||
|         !verifyId(infoCondition.getVariableName(), mGlobals, id, messages)) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Journal && | ||||
|         !verifyId(infoCondition.getVariableName(), mJournals, id, messages)) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Item && | ||||
|         !verifyItem(infoCondition.getVariableName(), id, messages)) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_Dead && | ||||
|         !verifyActor(infoCondition.getVariableName(), id, messages)) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotId && | ||||
|         !verifyActor(infoCondition.getVariableName(), id, messages)) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotFaction && | ||||
|         !verifyId(infoCondition.getVariableName(), mFactions, id, messages)) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotClass && | ||||
|         !verifyId(infoCondition.getVariableName(), mClasses, id, messages)) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotRace && | ||||
|         !verifyId(infoCondition.getVariableName(), mRaces, id, messages)) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
|     else if (infoCondition.getFunctionName() == CSMWorld::ConstInfoSelectWrapper::Function_NotCell && | ||||
|         !verifyCell(infoCondition.getVariableName(), id, messages)) | ||||
|     { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool CSMTools::TopicInfoCheckStage::verifySound(const std::string& sound, const CSMWorld::UniversalId& id, | ||||
|     CSMDoc::Messages& messages) | ||||
| { | ||||
|     const std::string specifier = "Sound File"; | ||||
| 
 | ||||
|     if (mSoundFiles.searchId(sound) == -1) | ||||
|     { | ||||
|         writeMissingIdError(specifier, sound, id, messages); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| bool CSMTools::TopicInfoCheckStage::verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection, | ||||
|     const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) | ||||
| { | ||||
|     int index = collection.searchId(name); | ||||
| 
 | ||||
|     if (index == -1) | ||||
|     { | ||||
|         writeMissingIdError(T::getRecordType(), name, id, messages); | ||||
|         return false; | ||||
|     } | ||||
|     else if (collection.getRecord(index).isDeleted()) | ||||
|     { | ||||
|         writeDeletedRecordError(T::getRecordType(), name, id, messages); | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| // Error functions
 | ||||
| 
 | ||||
| void CSMTools::TopicInfoCheckStage::writeMissingIdError(const std::string& specifier, const std::string& missingId, | ||||
|     const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) | ||||
| { | ||||
|     std::ostringstream stream; | ||||
|     stream << specifier << ": ID or name \"" << missingId << "\" could not be found"; | ||||
| 
 | ||||
|     messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); | ||||
| } | ||||
| 
 | ||||
| void CSMTools::TopicInfoCheckStage::writeDeletedRecordError(const std::string& specifier, const std::string& recordId, | ||||
|     const CSMWorld::UniversalId& id, CSMDoc::Messages& messages) | ||||
| { | ||||
|     std::ostringstream stream; | ||||
|     stream << specifier << ": Deleted record with ID \"" << recordId << "\" is being referenced"; | ||||
| 
 | ||||
|     messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); | ||||
| } | ||||
| 
 | ||||
| void CSMTools::TopicInfoCheckStage::writeInvalidTypeError(const std::string& specifier, const std::string& invalidId, | ||||
|     CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, const CSMWorld::UniversalId& id, | ||||
|     CSMDoc::Messages& messages) | ||||
| { | ||||
|     CSMWorld::UniversalId tempId(invalidType, invalidId); | ||||
| 
 | ||||
|     std::ostringstream stream; | ||||
|     stream << specifier << ": invalid type of " << tempId.getTypeName() << " was found for referencable \"" | ||||
|            << invalidId << "\" (can be of type " << expectedType << ")"; | ||||
| 
 | ||||
|     messages.add(id, stream.str(), "", CSMDoc::Message::Severity_Error); | ||||
| } | ||||
							
								
								
									
										95
									
								
								apps/opencs/model/tools/topicinfocheck.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								apps/opencs/model/tools/topicinfocheck.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,95 @@ | |||
| #ifndef CSM_TOOLS_TOPICINFOCHECK_HPP | ||||
| #define CSM_TOOLS_TOPICINFOCHECK_HPP | ||||
| 
 | ||||
| #include <set> | ||||
| 
 | ||||
| #include <components/esm/loadclas.hpp> | ||||
| #include <components/esm/loaddial.hpp> | ||||
| #include <components/esm/loadfact.hpp> | ||||
| #include <components/esm/loadglob.hpp> | ||||
| #include <components/esm/loadgmst.hpp> | ||||
| #include <components/esm/loadrace.hpp> | ||||
| #include <components/esm/loadregn.hpp> | ||||
| 
 | ||||
| #include "../world/cell.hpp" | ||||
| #include "../world/idcollection.hpp" | ||||
| #include "../world/infocollection.hpp" | ||||
| #include "../world/refiddata.hpp" | ||||
| #include "../world/resources.hpp" | ||||
| 
 | ||||
| #include "../doc/stage.hpp" | ||||
| 
 | ||||
| namespace CSMTools | ||||
| { | ||||
|     /// \brief VerifyStage: check topics
 | ||||
|     class TopicInfoCheckStage : public CSMDoc::Stage | ||||
|     { | ||||
|     public: | ||||
| 
 | ||||
|         TopicInfoCheckStage( | ||||
|             const CSMWorld::InfoCollection& topicInfos, | ||||
|             const CSMWorld::IdCollection<CSMWorld::Cell>& cells, | ||||
|             const CSMWorld::IdCollection<ESM::Class>& classes, | ||||
|             const CSMWorld::IdCollection<ESM::Faction>& factions, | ||||
|             const CSMWorld::IdCollection<ESM::GameSetting>& gmsts, | ||||
|             const CSMWorld::IdCollection<ESM::Global>& globals, | ||||
|             const CSMWorld::IdCollection<ESM::Dialogue>& journals, | ||||
|             const CSMWorld::IdCollection<ESM::Race>& races, | ||||
|             const CSMWorld::IdCollection<ESM::Region>& regions, | ||||
|             const CSMWorld::IdCollection<ESM::Dialogue>& topics, | ||||
|             const CSMWorld::RefIdData& referencables, | ||||
|             const CSMWorld::Resources& soundFiles); | ||||
| 
 | ||||
|         virtual int setup(); | ||||
|         ///< \return number of steps
 | ||||
| 
 | ||||
|         virtual void perform(int step, CSMDoc::Messages& messages); | ||||
|         ///< Messages resulting from this stage will be appended to \a messages
 | ||||
| 
 | ||||
|     private: | ||||
| 
 | ||||
|         const CSMWorld::InfoCollection& mTopicInfos; | ||||
| 
 | ||||
|         const CSMWorld::IdCollection<CSMWorld::Cell>& mCells; | ||||
|         const CSMWorld::IdCollection<ESM::Class>& mClasses; | ||||
|         const CSMWorld::IdCollection<ESM::Faction>& mFactions; | ||||
|         const CSMWorld::IdCollection<ESM::GameSetting>& mGameSettings; | ||||
|         const CSMWorld::IdCollection<ESM::Global>& mGlobals; | ||||
|         const CSMWorld::IdCollection<ESM::Dialogue>& mJournals; | ||||
|         const CSMWorld::IdCollection<ESM::Race>& mRaces; | ||||
|         const CSMWorld::IdCollection<ESM::Region>& mRegions; | ||||
|         const CSMWorld::IdCollection<ESM::Dialogue>& mTopics; | ||||
| 
 | ||||
|         const CSMWorld::RefIdData& mReferencables; | ||||
|         const CSMWorld::Resources& mSoundFiles; | ||||
| 
 | ||||
|         std::set<std::string> mCellNames; | ||||
| 
 | ||||
|         // These return false when not successful and write an error
 | ||||
|         bool verifyActor(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); | ||||
|         bool verifyCell(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); | ||||
|         bool verifyFactionRank(const std::string& name, int rank, const CSMWorld::UniversalId& id, | ||||
|             CSMDoc::Messages& messages); | ||||
|         bool verifyItem(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); | ||||
|         bool verifySelectStruct(const ESM::DialInfo::SelectStruct& select, const CSMWorld::UniversalId& id, | ||||
|             CSMDoc::Messages& messages); | ||||
|         bool verifySound(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); | ||||
| 
 | ||||
|         template <typename T> | ||||
|         bool verifyId(const std::string& name, const CSMWorld::IdCollection<T>& collection, | ||||
|             const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); | ||||
| 
 | ||||
|         // Common error messages
 | ||||
|         void writeMissingIdError(const std::string& specifier, const std::string& missingId, | ||||
|             const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); | ||||
| 
 | ||||
|         void writeDeletedRecordError(const std::string& specifier, const std::string& recordId, | ||||
|             const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); | ||||
| 
 | ||||
|         void writeInvalidTypeError(const std::string& specifier, const std::string& invalidId, | ||||
|             CSMWorld::UniversalId::Type invalidType, const std::string& expectedType, | ||||
|             const CSMWorld::UniversalId& id, CSMDoc::Messages& messages); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -3,6 +3,7 @@ | |||
| #include <components/misc/stringops.hpp> | ||||
| 
 | ||||
| #include "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); | ||||
|  |  | |||
							
								
								
									
										893
									
								
								apps/opencs/model/world/infoselectwrapper.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										893
									
								
								apps/opencs/model/world/infoselectwrapper.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,893 @@ | |||
| #include "infoselectwrapper.hpp" | ||||
| 
 | ||||
| #include <limits> | ||||
| #include <sstream> | ||||
| #include <stdexcept> | ||||
| 
 | ||||
| const size_t CSMWorld::ConstInfoSelectWrapper::RuleMinSize = 5; | ||||
| 
 | ||||
| const size_t CSMWorld::ConstInfoSelectWrapper::FunctionPrefixOffset = 1; | ||||
| const size_t CSMWorld::ConstInfoSelectWrapper::FunctionIndexOffset = 2; | ||||
| const size_t CSMWorld::ConstInfoSelectWrapper::RelationIndexOffset = 4; | ||||
| const size_t CSMWorld::ConstInfoSelectWrapper::VarNameOffset = 5; | ||||
| 
 | ||||
| const char* CSMWorld::ConstInfoSelectWrapper::FunctionEnumStrings[] = | ||||
| { | ||||
|     "Rank Low", | ||||
|     "Rank High", | ||||
|     "Rank Requirement", | ||||
|     "Reputation", | ||||
|     "Health Percent", | ||||
|     "PC Reputation", | ||||
|     "PC Level", | ||||
|     "PC Health Percent", | ||||
|     "PC Magicka", | ||||
|     "PC Fatigue", | ||||
|     "PC Strength", | ||||
|     "PC Block", | ||||
|     "PC Armorer", | ||||
|     "PC Medium Armor", | ||||
|     "PC Heavy Armor", | ||||
|     "PC Blunt Weapon", | ||||
|     "PC Long Blade", | ||||
|     "PC Axe", | ||||
|     "PC Spear", | ||||
|     "PC Athletics", | ||||
|     "PC Enchant", | ||||
|     "PC Detruction", | ||||
|     "PC Alteration", | ||||
|     "PC Illusion", | ||||
|     "PC Conjuration", | ||||
|     "PC Mysticism", | ||||
|     "PC Restoration", | ||||
|     "PC Alchemy", | ||||
|     "PC Unarmored", | ||||
|     "PC Security", | ||||
|     "PC Sneak", | ||||
|     "PC Acrobatics", | ||||
|     "PC Light Armor", | ||||
|     "PC Short Blade", | ||||
|     "PC Marksman", | ||||
|     "PC Merchantile", | ||||
|     "PC Speechcraft", | ||||
|     "PC Hand to Hand", | ||||
|     "PC Sex", | ||||
|     "PC Expelled", | ||||
|     "PC Common Disease", | ||||
|     "PC Blight Disease", | ||||
|     "PC Clothing Modifier", | ||||
|     "PC Crime Level", | ||||
|     "Same Sex", | ||||
|     "Same Race", | ||||
|     "Same Faction", | ||||
|     "Faction Rank Difference", | ||||
|     "Detected", | ||||
|     "Alarmed", | ||||
|     "Choice", | ||||
|     "PC Intelligence", | ||||
|     "PC Willpower", | ||||
|     "PC Agility", | ||||
|     "PC Speed", | ||||
|     "PC Endurance", | ||||
|     "PC Personality", | ||||
|     "PC Luck", | ||||
|     "PC Corpus", | ||||
|     "Weather", | ||||
|     "PC Vampire", | ||||
|     "Level", | ||||
|     "Attacked", | ||||
|     "Talked to PC", | ||||
|     "PC Health", | ||||
|     "Creature Target", | ||||
|     "Friend Hit", | ||||
|     "Fight", | ||||
|     "Hello", | ||||
|     "Alarm", | ||||
|     "Flee", | ||||
|     "Should Attack", | ||||
|     "Werewolf", | ||||
|     "PC Werewolf Kills", | ||||
|     "Global", | ||||
|     "Local", | ||||
|     "Journal", | ||||
|     "Item", | ||||
|     "Dead", | ||||
|     "Not Id", | ||||
|     "Not Faction", | ||||
|     "Not Class", | ||||
|     "Not Race", | ||||
|     "Not Cell", | ||||
|     "Not Local", | ||||
|     0 | ||||
| }; | ||||
| 
 | ||||
| const char* CSMWorld::ConstInfoSelectWrapper::RelationEnumStrings[] = | ||||
| { | ||||
|     "=", | ||||
|     "!=", | ||||
|     ">", | ||||
|     ">=", | ||||
|     "<", | ||||
|     "<=", | ||||
|     0 | ||||
| }; | ||||
| 
 | ||||
| const char* CSMWorld::ConstInfoSelectWrapper::ComparisonEnumStrings[] = | ||||
| { | ||||
|     "Boolean", | ||||
|     "Integer", | ||||
|     "Numeric", | ||||
|     0 | ||||
| }; | ||||
| 
 | ||||
| // static functions
 | ||||
| 
 | ||||
| std::string CSMWorld::ConstInfoSelectWrapper::convertToString(FunctionName name) | ||||
| { | ||||
|     if (name < Function_None) | ||||
|         return FunctionEnumStrings[name]; | ||||
|     else | ||||
|         return "(Invalid Data: Function)"; | ||||
| } | ||||
| 
 | ||||
| std::string CSMWorld::ConstInfoSelectWrapper::convertToString(RelationType type) | ||||
| { | ||||
|     if (type < Relation_None) | ||||
|         return RelationEnumStrings[type]; | ||||
|     else | ||||
|         return "(Invalid Data: Relation)"; | ||||
| } | ||||
| 
 | ||||
| std::string CSMWorld::ConstInfoSelectWrapper::convertToString(ComparisonType type) | ||||
| { | ||||
|     if (type < Comparison_None) | ||||
|         return ComparisonEnumStrings[type]; | ||||
|     else | ||||
|         return "(Invalid Data: Comparison)"; | ||||
| } | ||||
| 
 | ||||
| // ConstInfoSelectWrapper
 | ||||
| 
 | ||||
| CSMWorld::ConstInfoSelectWrapper::ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select) | ||||
|     : mConstSelect(select) | ||||
| { | ||||
|     readRule(); | ||||
| } | ||||
| 
 | ||||
| CSMWorld::ConstInfoSelectWrapper::FunctionName CSMWorld::ConstInfoSelectWrapper::getFunctionName() const | ||||
| { | ||||
|     return mFunctionName; | ||||
| } | ||||
| 
 | ||||
| CSMWorld::ConstInfoSelectWrapper::RelationType CSMWorld::ConstInfoSelectWrapper::getRelationType() const | ||||
| { | ||||
|     return mRelationType; | ||||
| } | ||||
| 
 | ||||
| CSMWorld::ConstInfoSelectWrapper::ComparisonType CSMWorld::ConstInfoSelectWrapper::getComparisonType() const | ||||
| { | ||||
|     return mComparisonType; | ||||
| } | ||||
| 
 | ||||
| bool CSMWorld::ConstInfoSelectWrapper::hasVariable() const | ||||
| { | ||||
|     return mHasVariable; | ||||
| } | ||||
| 
 | ||||
| const std::string& CSMWorld::ConstInfoSelectWrapper::getVariableName() const | ||||
| { | ||||
|     return mVariableName; | ||||
| } | ||||
| 
 | ||||
| bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue() const | ||||
| { | ||||
|     if (!variantTypeIsValid()) | ||||
|         return false; | ||||
| 
 | ||||
|     if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer) | ||||
|     { | ||||
|         if (mConstSelect.mValue.getType() == ESM::VT_Float) | ||||
|             return conditionIsAlwaysTrue(getConditionFloatRange(), getValidIntRange()); | ||||
|         else | ||||
|             return conditionIsAlwaysTrue(getConditionIntRange(), getValidIntRange()); | ||||
|     } | ||||
|     else if (mComparisonType == Comparison_Numeric) | ||||
|     { | ||||
|         if (mConstSelect.mValue.getType() == ESM::VT_Float) | ||||
|             return conditionIsAlwaysTrue(getConditionFloatRange(), getValidFloatRange()); | ||||
|         else | ||||
|             return conditionIsAlwaysTrue(getConditionIntRange(), getValidFloatRange()); | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue() const | ||||
| { | ||||
|     if (!variantTypeIsValid()) | ||||
|         return false; | ||||
| 
 | ||||
|     if (mComparisonType == Comparison_Boolean || mComparisonType == Comparison_Integer) | ||||
|     { | ||||
|         if (mConstSelect.mValue.getType() == ESM::VT_Float) | ||||
|             return conditionIsNeverTrue(getConditionFloatRange(), getValidIntRange()); | ||||
|         else | ||||
|             return conditionIsNeverTrue(getConditionIntRange(), getValidIntRange()); | ||||
|     } | ||||
|     else if (mComparisonType == Comparison_Numeric) | ||||
|     { | ||||
|         if (mConstSelect.mValue.getType() == ESM::VT_Float) | ||||
|             return conditionIsNeverTrue(getConditionFloatRange(), getValidFloatRange()); | ||||
|         else | ||||
|             return conditionIsNeverTrue(getConditionIntRange(), getValidFloatRange()); | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool CSMWorld::ConstInfoSelectWrapper::variantTypeIsValid() const | ||||
| { | ||||
|     return (mConstSelect.mValue.getType() == ESM::VT_Int || mConstSelect.mValue.getType() == ESM::VT_Float); | ||||
| } | ||||
| 
 | ||||
| const ESM::Variant& CSMWorld::ConstInfoSelectWrapper::getVariant() const | ||||
| { | ||||
|     return mConstSelect.mValue; | ||||
| } | ||||
| 
 | ||||
| std::string CSMWorld::ConstInfoSelectWrapper::toString() const | ||||
| { | ||||
|     std::ostringstream stream; | ||||
|     stream << convertToString(mFunctionName) << " "; | ||||
| 
 | ||||
|     if (mHasVariable) | ||||
|         stream << mVariableName << " "; | ||||
| 
 | ||||
|     stream << convertToString(mRelationType) << " "; | ||||
| 
 | ||||
|     switch (mConstSelect.mValue.getType()) | ||||
|     { | ||||
|         case ESM::VT_Int: | ||||
|             stream << mConstSelect.mValue.getInteger(); | ||||
|             break; | ||||
| 
 | ||||
|         case ESM::VT_Float: | ||||
|             stream << mConstSelect.mValue.getFloat(); | ||||
|             break; | ||||
| 
 | ||||
|         default: | ||||
|             stream << "(Invalid value type)"; | ||||
|             break; | ||||
|     } | ||||
| 
 | ||||
|     return stream.str(); | ||||
| } | ||||
| 
 | ||||
| void CSMWorld::ConstInfoSelectWrapper::readRule() | ||||
| { | ||||
|     if (mConstSelect.mSelectRule.size() < RuleMinSize) | ||||
|         throw std::runtime_error("InfoSelectWrapper: rule is to small"); | ||||
| 
 | ||||
|     readFunctionName(); | ||||
|     readRelationType(); | ||||
|     readVariableName(); | ||||
|     updateHasVariable(); | ||||
|     updateComparisonType(); | ||||
| } | ||||
| 
 | ||||
| void CSMWorld::ConstInfoSelectWrapper::readFunctionName() | ||||
| { | ||||
|     char functionPrefix = mConstSelect.mSelectRule[FunctionPrefixOffset]; | ||||
|     std::string functionIndex = mConstSelect.mSelectRule.substr(FunctionIndexOffset, 2); | ||||
|     int convertedIndex = -1; | ||||
| 
 | ||||
|     // Read in function index, form ## from 00 .. 73, skip leading zero
 | ||||
|     if (functionIndex[0] == '0') | ||||
|         functionIndex = functionIndex[1]; | ||||
| 
 | ||||
|     std::stringstream stream; | ||||
|     stream << functionIndex; | ||||
|     stream >> convertedIndex; | ||||
| 
 | ||||
|     switch (functionPrefix) | ||||
|     { | ||||
|         case '1': | ||||
|             if (convertedIndex >= 0 && convertedIndex <= 73) | ||||
|                 mFunctionName = static_cast<FunctionName>(convertedIndex); | ||||
|             else | ||||
|                 mFunctionName = Function_None; | ||||
|             break; | ||||
| 
 | ||||
|         case '2': mFunctionName = Function_Global; break; | ||||
|         case '3': mFunctionName = Function_Local; break; | ||||
|         case '4': mFunctionName = Function_Journal; break; | ||||
|         case '5': mFunctionName = Function_Item; break; | ||||
|         case '6': mFunctionName = Function_Dead; break; | ||||
|         case '7': mFunctionName = Function_NotId; break; | ||||
|         case '8': mFunctionName = Function_NotFaction; break; | ||||
|         case '9': mFunctionName = Function_NotClass; break; | ||||
|         case 'A': mFunctionName = Function_NotRace; break; | ||||
|         case 'B': mFunctionName = Function_NotCell; break; | ||||
|         case 'C': mFunctionName = Function_NotLocal; break; | ||||
|         default:  mFunctionName = Function_None; break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void CSMWorld::ConstInfoSelectWrapper::readRelationType() | ||||
| { | ||||
|     char relationIndex = mConstSelect.mSelectRule[RelationIndexOffset]; | ||||
| 
 | ||||
|     switch (relationIndex) | ||||
|     { | ||||
|         case '0': mRelationType = Relation_Equal; break; | ||||
|         case '1': mRelationType = Relation_NotEqual; break; | ||||
|         case '2': mRelationType = Relation_Greater; break; | ||||
|         case '3': mRelationType = Relation_GreaterOrEqual; break; | ||||
|         case '4': mRelationType = Relation_Less; break; | ||||
|         case '5': mRelationType = Relation_LessOrEqual; break; | ||||
|         default:  mRelationType = Relation_None; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void CSMWorld::ConstInfoSelectWrapper::readVariableName() | ||||
| { | ||||
|     if (mConstSelect.mSelectRule.size() >= VarNameOffset) | ||||
|         mVariableName = mConstSelect.mSelectRule.substr(VarNameOffset); | ||||
|     else | ||||
|         mVariableName.clear(); | ||||
| } | ||||
| 
 | ||||
| void CSMWorld::ConstInfoSelectWrapper::updateHasVariable() | ||||
| { | ||||
|     switch (mFunctionName) | ||||
|     { | ||||
|         case Function_Global: | ||||
|         case Function_Local: | ||||
|         case Function_Journal: | ||||
|         case Function_Item: | ||||
|         case Function_Dead: | ||||
|         case Function_NotId: | ||||
|         case Function_NotFaction: | ||||
|         case Function_NotClass: | ||||
|         case Function_NotRace: | ||||
|         case Function_NotCell: | ||||
|         case Function_NotLocal: | ||||
|             mHasVariable = true; | ||||
|             break; | ||||
| 
 | ||||
|         default: | ||||
|             mHasVariable = false; | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void CSMWorld::ConstInfoSelectWrapper::updateComparisonType() | ||||
| { | ||||
|     switch (mFunctionName) | ||||
|     { | ||||
|         // Boolean
 | ||||
|         case Function_NotId: | ||||
|         case Function_NotFaction: | ||||
|         case Function_NotClass: | ||||
|         case Function_NotRace: | ||||
|         case Function_NotCell: | ||||
|         case Function_NotLocal: | ||||
|         case Function_PcExpelled: | ||||
|         case Function_PcCommonDisease: | ||||
|         case Function_PcBlightDisease: | ||||
|         case Function_SameSex: | ||||
|         case Function_SameRace: | ||||
|         case Function_SameFaction: | ||||
|         case Function_Detected: | ||||
|         case Function_Alarmed: | ||||
|         case Function_PcCorpus: | ||||
|         case Function_PcVampire: | ||||
|         case Function_Attacked: | ||||
|         case Function_TalkedToPc: | ||||
|         case Function_ShouldAttack: | ||||
|         case Function_Werewolf: | ||||
|             mComparisonType = Comparison_Boolean; | ||||
|             break; | ||||
| 
 | ||||
|         // Integer
 | ||||
|         case Function_Journal: | ||||
|         case Function_Item: | ||||
|         case Function_Dead: | ||||
|         case Function_RankLow: | ||||
|         case Function_RankHigh: | ||||
|         case Function_RankRequirement: | ||||
|         case Function_Reputation: | ||||
|         case Function_PcReputation: | ||||
|         case Function_PcLevel: | ||||
|         case Function_PcStrength: | ||||
|         case Function_PcBlock: | ||||
|         case Function_PcArmorer: | ||||
|         case Function_PcMediumArmor: | ||||
|         case Function_PcHeavyArmor: | ||||
|         case Function_PcBluntWeapon: | ||||
|         case Function_PcLongBlade: | ||||
|         case Function_PcAxe: | ||||
|         case Function_PcSpear: | ||||
|         case Function_PcAthletics: | ||||
|         case Function_PcEnchant: | ||||
|         case Function_PcDestruction: | ||||
|         case Function_PcAlteration: | ||||
|         case Function_PcIllusion: | ||||
|         case Function_PcConjuration: | ||||
|         case Function_PcMysticism: | ||||
|         case Function_PcRestoration: | ||||
|         case Function_PcAlchemy: | ||||
|         case Function_PcUnarmored: | ||||
|         case Function_PcSecurity: | ||||
|         case Function_PcSneak: | ||||
|         case Function_PcAcrobatics: | ||||
|         case Function_PcLightArmor: | ||||
|         case Function_PcShortBlade: | ||||
|         case Function_PcMarksman: | ||||
|         case Function_PcMerchantile: | ||||
|         case Function_PcSpeechcraft: | ||||
|         case Function_PcHandToHand: | ||||
|         case Function_PcGender: | ||||
|         case Function_PcClothingModifier: | ||||
|         case Function_PcCrimeLevel: | ||||
|         case Function_FactionRankDifference: | ||||
|         case Function_Choice: | ||||
|         case Function_PcIntelligence: | ||||
|         case Function_PcWillpower: | ||||
|         case Function_PcAgility: | ||||
|         case Function_PcSpeed: | ||||
|         case Function_PcEndurance: | ||||
|         case Function_PcPersonality: | ||||
|         case Function_PcLuck: | ||||
|         case Function_Weather: | ||||
|         case Function_Level: | ||||
|         case Function_CreatureTarget: | ||||
|         case Function_FriendHit: | ||||
|         case Function_Fight: | ||||
|         case Function_Hello: | ||||
|         case Function_Alarm: | ||||
|         case Function_Flee: | ||||
|         case Function_PcWerewolfKills: | ||||
|             mComparisonType = Comparison_Integer; | ||||
|             break; | ||||
| 
 | ||||
|         // Numeric
 | ||||
|         case Function_Global: | ||||
|         case Function_Local: | ||||
| 
 | ||||
|         case Function_Health_Percent: | ||||
|         case Function_PcHealthPercent: | ||||
|         case Function_PcMagicka: | ||||
|         case Function_PcFatigue: | ||||
|         case Function_PcHealth: | ||||
|             mComparisonType = Comparison_Numeric; | ||||
|             break; | ||||
| 
 | ||||
|         default: | ||||
|             mComparisonType = Comparison_None; | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::pair<int, int> CSMWorld::ConstInfoSelectWrapper::getConditionIntRange() const | ||||
| { | ||||
|     const int IntMax = std::numeric_limits<int>::max(); | ||||
|     const int IntMin = std::numeric_limits<int>::min(); | ||||
|     const std::pair<int, int> InvalidRange(IntMax, IntMin); | ||||
| 
 | ||||
|     int value = mConstSelect.mValue.getInteger(); | ||||
| 
 | ||||
|     switch (mRelationType) | ||||
|     { | ||||
|         case Relation_Equal: | ||||
|         case Relation_NotEqual: | ||||
|             return std::pair<int, int>(value, value); | ||||
| 
 | ||||
|         case Relation_Greater: | ||||
|             if (value == IntMax) | ||||
|             { | ||||
|                 return InvalidRange; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return std::pair<int, int>(value + 1, IntMax); | ||||
|             } | ||||
|             break; | ||||
| 
 | ||||
|         case Relation_GreaterOrEqual: | ||||
|             return std::pair<int, int>(value, IntMax); | ||||
| 
 | ||||
|         case Relation_Less: | ||||
|             if (value == IntMin) | ||||
|             { | ||||
|                 return InvalidRange; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 return std::pair<int, int>(IntMin, value - 1); | ||||
|             } | ||||
| 
 | ||||
|         case Relation_LessOrEqual: | ||||
|             return std::pair<int, int>(IntMin, value); | ||||
| 
 | ||||
|         default: | ||||
|             throw std::logic_error("InfoSelectWrapper: relation does not have a range"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::pair<float, float> CSMWorld::ConstInfoSelectWrapper::getConditionFloatRange() const | ||||
| { | ||||
|     const float FloatMax = std::numeric_limits<float>::infinity(); | ||||
|     const float FloatMin = -std::numeric_limits<float>::infinity(); | ||||
|     const float Epsilon = std::numeric_limits<float>::epsilon(); | ||||
|     const std::pair<float, float> InvalidRange(FloatMax, FloatMin); | ||||
| 
 | ||||
|     float value = mConstSelect.mValue.getFloat(); | ||||
| 
 | ||||
|     switch (mRelationType) | ||||
|     { | ||||
|         case Relation_Equal: | ||||
|         case Relation_NotEqual: | ||||
|             return std::pair<float, float>(value, value); | ||||
| 
 | ||||
|         case Relation_Greater: | ||||
|             return std::pair<float, float>(value + Epsilon, FloatMax); | ||||
| 
 | ||||
|         case Relation_GreaterOrEqual: | ||||
|             return std::pair<float, float>(value, FloatMax); | ||||
| 
 | ||||
|         case Relation_Less: | ||||
|             return std::pair<float, float>(FloatMin, value - Epsilon); | ||||
| 
 | ||||
|         case Relation_LessOrEqual: | ||||
|             return std::pair<float, float>(FloatMin, value); | ||||
| 
 | ||||
|         default: | ||||
|             throw std::logic_error("InfoSelectWrapper: given relation does not have a range"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::pair<int, int> CSMWorld::ConstInfoSelectWrapper::getValidIntRange() const | ||||
| { | ||||
|     const int IntMax = std::numeric_limits<int>::max(); | ||||
|     const int IntMin = std::numeric_limits<int>::min(); | ||||
| 
 | ||||
|     switch (mFunctionName) | ||||
|     { | ||||
|         // Boolean
 | ||||
|         case Function_NotId: | ||||
|         case Function_NotFaction: | ||||
|         case Function_NotClass: | ||||
|         case Function_NotRace: | ||||
|         case Function_NotCell: | ||||
|         case Function_NotLocal: | ||||
|         case Function_PcExpelled: | ||||
|         case Function_PcCommonDisease: | ||||
|         case Function_PcBlightDisease: | ||||
|         case Function_SameSex: | ||||
|         case Function_SameRace: | ||||
|         case Function_SameFaction: | ||||
|         case Function_Detected: | ||||
|         case Function_Alarmed: | ||||
|         case Function_PcCorpus: | ||||
|         case Function_PcVampire: | ||||
|         case Function_Attacked: | ||||
|         case Function_TalkedToPc: | ||||
|         case Function_ShouldAttack: | ||||
|         case Function_Werewolf: | ||||
|             return std::pair<int, int>(0, 1); | ||||
| 
 | ||||
|         // Integer
 | ||||
|         case Function_RankLow: | ||||
|         case Function_RankHigh: | ||||
|         case Function_Reputation: | ||||
|         case Function_PcReputation: | ||||
|         case Function_Journal: | ||||
|             return std::pair<int, int>(IntMin, IntMax); | ||||
| 
 | ||||
|         case Function_Item: | ||||
|         case Function_Dead: | ||||
|         case Function_PcLevel: | ||||
|         case Function_PcStrength: | ||||
|         case Function_PcBlock: | ||||
|         case Function_PcArmorer: | ||||
|         case Function_PcMediumArmor: | ||||
|         case Function_PcHeavyArmor: | ||||
|         case Function_PcBluntWeapon: | ||||
|         case Function_PcLongBlade: | ||||
|         case Function_PcAxe: | ||||
|         case Function_PcSpear: | ||||
|         case Function_PcAthletics: | ||||
|         case Function_PcEnchant: | ||||
|         case Function_PcDestruction: | ||||
|         case Function_PcAlteration: | ||||
|         case Function_PcIllusion: | ||||
|         case Function_PcConjuration: | ||||
|         case Function_PcMysticism: | ||||
|         case Function_PcRestoration: | ||||
|         case Function_PcAlchemy: | ||||
|         case Function_PcUnarmored: | ||||
|         case Function_PcSecurity: | ||||
|         case Function_PcSneak: | ||||
|         case Function_PcAcrobatics: | ||||
|         case Function_PcLightArmor: | ||||
|         case Function_PcShortBlade: | ||||
|         case Function_PcMarksman: | ||||
|         case Function_PcMerchantile: | ||||
|         case Function_PcSpeechcraft: | ||||
|         case Function_PcHandToHand: | ||||
|         case Function_PcClothingModifier: | ||||
|         case Function_PcCrimeLevel: | ||||
|         case Function_Choice: | ||||
|         case Function_PcIntelligence: | ||||
|         case Function_PcWillpower: | ||||
|         case Function_PcAgility: | ||||
|         case Function_PcSpeed: | ||||
|         case Function_PcEndurance: | ||||
|         case Function_PcPersonality: | ||||
|         case Function_PcLuck: | ||||
|         case Function_Level: | ||||
|         case Function_PcWerewolfKills: | ||||
|             return std::pair<int, int>(0, IntMax); | ||||
| 
 | ||||
|         case Function_Fight: | ||||
|         case Function_Hello: | ||||
|         case Function_Alarm: | ||||
|         case Function_Flee: | ||||
|             return std::pair<int, int>(0, 100); | ||||
| 
 | ||||
|         case Function_Weather: | ||||
|             return std::pair<int, int>(0, 9); | ||||
| 
 | ||||
|         case Function_FriendHit: | ||||
|             return std::pair<int, int>(0, 4); | ||||
| 
 | ||||
|         case Function_RankRequirement: | ||||
|             return std::pair<int, int>(0, 3); | ||||
| 
 | ||||
|         case Function_CreatureTarget: | ||||
|             return std::pair<int, int>(0, 2); | ||||
| 
 | ||||
|         case Function_PcGender: | ||||
|             return std::pair<int, int>(0, 1); | ||||
| 
 | ||||
|         case Function_FactionRankDifference: | ||||
|             return std::pair<int, int>(-9, 9); | ||||
| 
 | ||||
|         // Numeric
 | ||||
|         case Function_Global: | ||||
|         case Function_Local: | ||||
|             return std::pair<int, int>(IntMin, IntMax); | ||||
| 
 | ||||
|         case Function_PcMagicka: | ||||
|         case Function_PcFatigue: | ||||
|         case Function_PcHealth: | ||||
|             return std::pair<int, int>(0, IntMax); | ||||
| 
 | ||||
|         case Function_Health_Percent: | ||||
|         case Function_PcHealthPercent: | ||||
|             return std::pair<int, int>(0, 100); | ||||
| 
 | ||||
|         default: | ||||
|             throw std::runtime_error("InfoSelectWrapper: function does not exist"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::pair<float, float> CSMWorld::ConstInfoSelectWrapper::getValidFloatRange() const | ||||
| { | ||||
|     const float FloatMax = std::numeric_limits<float>::infinity(); | ||||
|     const float FloatMin = -std::numeric_limits<float>::infinity(); | ||||
| 
 | ||||
|     switch (mFunctionName) | ||||
|     { | ||||
|         // Numeric
 | ||||
|         case Function_Global: | ||||
|         case Function_Local: | ||||
|         case Function_NotLocal: | ||||
|             return std::pair<float, float>(FloatMin, FloatMax); | ||||
| 
 | ||||
|         case Function_PcMagicka: | ||||
|         case Function_PcFatigue: | ||||
|         case Function_PcHealth: | ||||
|             return std::pair<float, float>(0, FloatMax); | ||||
| 
 | ||||
|         case Function_Health_Percent: | ||||
|         case Function_PcHealthPercent: | ||||
|             return std::pair<float, float>(0, 100); | ||||
| 
 | ||||
|         default: | ||||
|             throw std::runtime_error("InfoSelectWrapper: function does not exist or is not numeric"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T1, typename T2> | ||||
| bool CSMWorld::ConstInfoSelectWrapper::rangeContains(T1 value, std::pair<T2,T2> range) const | ||||
| { | ||||
|     return (value >= range.first && value <= range.second); | ||||
| } | ||||
| 
 | ||||
| template <typename T1, typename T2> | ||||
| bool CSMWorld::ConstInfoSelectWrapper::rangeFullyContains(std::pair<T1,T1> containingRange, | ||||
|     std::pair<T2,T2> testRange) const | ||||
| { | ||||
|     return (containingRange.first <= testRange.first) && (testRange.second <= containingRange.second); | ||||
| } | ||||
| 
 | ||||
| template <typename T1, typename T2> | ||||
| bool CSMWorld::ConstInfoSelectWrapper::rangesOverlap(std::pair<T1,T1> range1, std::pair<T2,T2> range2) const | ||||
| { | ||||
|     // One of the bounds of either range should fall within the other range
 | ||||
|     return | ||||
|         (range1.first <= range2.first  && range2.first  <= range1.second) || | ||||
|         (range1.first <= range2.second && range2.second <= range1.second) || | ||||
|         (range2.first <= range1.first  && range1.first  <= range2.second) || | ||||
|         (range2.first <= range1.second && range1.second <= range2.second); | ||||
| } | ||||
| 
 | ||||
| template <typename T1, typename T2> | ||||
| bool CSMWorld::ConstInfoSelectWrapper::rangesMatch(std::pair<T1,T1> range1, std::pair<T2,T2> range2) const | ||||
| { | ||||
|     return (range1.first == range2.first  && range1.second == range2.second); | ||||
| } | ||||
| 
 | ||||
| template <typename T1, typename T2> | ||||
| bool CSMWorld::ConstInfoSelectWrapper::conditionIsAlwaysTrue(std::pair<T1,T1> conditionRange, | ||||
|     std::pair<T2,T2> validRange) const | ||||
| { | ||||
|     switch (mRelationType) | ||||
|     { | ||||
|         case Relation_Equal: | ||||
|             return false; | ||||
| 
 | ||||
|         case Relation_NotEqual: | ||||
|             // If value is not within range, it will always be true
 | ||||
|             return !rangeContains(conditionRange.first, validRange); | ||||
| 
 | ||||
|         case Relation_Greater: | ||||
|         case Relation_GreaterOrEqual: | ||||
|         case Relation_Less: | ||||
|         case Relation_LessOrEqual: | ||||
|             // If the valid range is completely within the condition range, it will always be true
 | ||||
|             return rangeFullyContains(conditionRange, validRange); | ||||
| 
 | ||||
|         default: | ||||
|             throw std::logic_error("InfoCondition: operator can not be used to compare"); | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| template <typename T1, typename T2> | ||||
| bool CSMWorld::ConstInfoSelectWrapper::conditionIsNeverTrue(std::pair<T1,T1> conditionRange, | ||||
|     std::pair<T2,T2> validRange) const | ||||
| { | ||||
|     switch (mRelationType) | ||||
|     { | ||||
|         case Relation_Equal: | ||||
|             return !rangeContains(conditionRange.first, validRange); | ||||
| 
 | ||||
|         case Relation_NotEqual: | ||||
|             return false; | ||||
| 
 | ||||
|         case Relation_Greater: | ||||
|         case Relation_GreaterOrEqual: | ||||
|         case Relation_Less: | ||||
|         case Relation_LessOrEqual: | ||||
|             // If ranges do not overlap, it will never be true
 | ||||
|             return !rangesOverlap(conditionRange, validRange); | ||||
| 
 | ||||
|         default: | ||||
|             throw std::logic_error("InfoCondition: operator can not be used to compare"); | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| // InfoSelectWrapper
 | ||||
| 
 | ||||
| CSMWorld::InfoSelectWrapper::InfoSelectWrapper(ESM::DialInfo::SelectStruct& select) | ||||
|     : CSMWorld::ConstInfoSelectWrapper(select), mSelect(select) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void CSMWorld::InfoSelectWrapper::setFunctionName(FunctionName name) | ||||
| { | ||||
|     mFunctionName = name; | ||||
|     updateHasVariable(); | ||||
|     updateComparisonType(); | ||||
| } | ||||
| 
 | ||||
| void CSMWorld::InfoSelectWrapper::setRelationType(RelationType type) | ||||
| { | ||||
|     mRelationType = type; | ||||
| } | ||||
| 
 | ||||
| void CSMWorld::InfoSelectWrapper::setVariableName(const std::string& name) | ||||
| { | ||||
|     mVariableName = name; | ||||
| } | ||||
| 
 | ||||
| void CSMWorld::InfoSelectWrapper::setDefaults() | ||||
| { | ||||
|     if (!variantTypeIsValid()) | ||||
|         mSelect.mValue.setType(ESM::VT_Int); | ||||
| 
 | ||||
|     switch (mComparisonType) | ||||
|     { | ||||
|         case Comparison_Boolean: | ||||
|             setRelationType(Relation_Equal); | ||||
|             mSelect.mValue.setInteger(1); | ||||
|             break; | ||||
| 
 | ||||
|         case Comparison_Integer: | ||||
|         case Comparison_Numeric: | ||||
|             setRelationType(Relation_Greater); | ||||
|             mSelect.mValue.setInteger(0); | ||||
|             break; | ||||
| 
 | ||||
|         default: | ||||
|             // Do nothing
 | ||||
|             break; | ||||
|     } | ||||
| 
 | ||||
|     update(); | ||||
| } | ||||
| 
 | ||||
| void CSMWorld::InfoSelectWrapper::update() | ||||
| { | ||||
|     std::ostringstream stream; | ||||
| 
 | ||||
|     // Leading 0
 | ||||
|     stream << '0'; | ||||
| 
 | ||||
|     // Write Function
 | ||||
| 
 | ||||
|     bool writeIndex = false; | ||||
|     size_t functionIndex = static_cast<size_t>(mFunctionName); | ||||
| 
 | ||||
|     switch (mFunctionName) | ||||
|     { | ||||
|         case Function_None:         stream << '0'; break; | ||||
|         case Function_Global:       stream << '2'; break; | ||||
|         case Function_Local:        stream << '3'; break; | ||||
|         case Function_Journal:      stream << '4'; break; | ||||
|         case Function_Item:         stream << '5'; break; | ||||
|         case Function_Dead:         stream << '6'; break; | ||||
|         case Function_NotId:        stream << '7'; break; | ||||
|         case Function_NotFaction:   stream << '8'; break; | ||||
|         case Function_NotClass:     stream << '9'; break; | ||||
|         case Function_NotRace:      stream << 'A'; break; | ||||
|         case Function_NotCell:      stream << 'B'; break; | ||||
|         case Function_NotLocal:     stream << 'C'; break; | ||||
|         default:                    stream << '1'; writeIndex = true; break; | ||||
|     } | ||||
| 
 | ||||
|     if (writeIndex && functionIndex < 10) // leading 0
 | ||||
|         stream << '0' << functionIndex; | ||||
|     else if (writeIndex) | ||||
|         stream << functionIndex; | ||||
|     else | ||||
|         stream << "00"; | ||||
| 
 | ||||
|     // Write Relation
 | ||||
|     switch (mRelationType) | ||||
|     { | ||||
|         case Relation_Equal:            stream << '0'; break; | ||||
|         case Relation_NotEqual:         stream << '1'; break; | ||||
|         case Relation_Greater:          stream << '2'; break; | ||||
|         case Relation_GreaterOrEqual:   stream << '3'; break; | ||||
|         case Relation_Less:             stream << '4'; break; | ||||
|         case Relation_LessOrEqual:      stream << '5'; break; | ||||
|         default:                        stream << '0'; break; | ||||
|     } | ||||
| 
 | ||||
|     if (mHasVariable) | ||||
|         stream << mVariableName; | ||||
| 
 | ||||
|     mSelect.mSelectRule = stream.str(); | ||||
| } | ||||
| 
 | ||||
| ESM::Variant& CSMWorld::InfoSelectWrapper::getVariant() | ||||
| { | ||||
|     return mSelect.mValue; | ||||
| } | ||||
							
								
								
									
										243
									
								
								apps/opencs/model/world/infoselectwrapper.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								apps/opencs/model/world/infoselectwrapper.hpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,243 @@ | |||
| #ifndef CSM_WORLD_INFOSELECTWRAPPER_H | ||||
| #define CSM_WORLD_INFOSELECTWRAPPER_H | ||||
| 
 | ||||
| #include <components/esm/loadinfo.hpp> | ||||
| 
 | ||||
| namespace CSMWorld | ||||
| { | ||||
|     // ESM::DialInfo::SelectStruct.mSelectRule
 | ||||
|     // 012345...
 | ||||
|     // ^^^ ^^
 | ||||
|     // ||| ||
 | ||||
|     // ||| |+------------- condition variable string
 | ||||
|     // ||| +-------------- comparison type, ['0'..'5']; e.g. !=, <, >=, etc
 | ||||
|     // ||+---------------- function index (encoded, where function == '1')
 | ||||
|     // |+----------------- function, ['1'..'C']; e.g. Global, Local, Not ID, etc
 | ||||
|     // +------------------ unknown
 | ||||
|     //
 | ||||
| 
 | ||||
|     // Wrapper for DialInfo::SelectStruct
 | ||||
|     class ConstInfoSelectWrapper | ||||
|     { | ||||
|     public: | ||||
| 
 | ||||
|         // Order matters
 | ||||
|         enum FunctionName | ||||
|         { | ||||
|             Function_RankLow=0, | ||||
|             Function_RankHigh, | ||||
|             Function_RankRequirement, | ||||
|             Function_Reputation, | ||||
|             Function_Health_Percent, | ||||
|             Function_PcReputation, | ||||
|             Function_PcLevel, | ||||
|             Function_PcHealthPercent, | ||||
|             Function_PcMagicka, | ||||
|             Function_PcFatigue, | ||||
|             Function_PcStrength, | ||||
|             Function_PcBlock, | ||||
|             Function_PcArmorer, | ||||
|             Function_PcMediumArmor, | ||||
|             Function_PcHeavyArmor, | ||||
|             Function_PcBluntWeapon, | ||||
|             Function_PcLongBlade, | ||||
|             Function_PcAxe, | ||||
|             Function_PcSpear, | ||||
|             Function_PcAthletics, | ||||
|             Function_PcEnchant, | ||||
|             Function_PcDestruction, | ||||
|             Function_PcAlteration, | ||||
|             Function_PcIllusion, | ||||
|             Function_PcConjuration, | ||||
|             Function_PcMysticism, | ||||
|             Function_PcRestoration, | ||||
|             Function_PcAlchemy, | ||||
|             Function_PcUnarmored, | ||||
|             Function_PcSecurity, | ||||
|             Function_PcSneak, | ||||
|             Function_PcAcrobatics, | ||||
|             Function_PcLightArmor, | ||||
|             Function_PcShortBlade, | ||||
|             Function_PcMarksman, | ||||
|             Function_PcMerchantile, | ||||
|             Function_PcSpeechcraft, | ||||
|             Function_PcHandToHand, | ||||
|             Function_PcGender, | ||||
|             Function_PcExpelled, | ||||
|             Function_PcCommonDisease, | ||||
|             Function_PcBlightDisease, | ||||
|             Function_PcClothingModifier, | ||||
|             Function_PcCrimeLevel, | ||||
|             Function_SameSex, | ||||
|             Function_SameRace, | ||||
|             Function_SameFaction, | ||||
|             Function_FactionRankDifference, | ||||
|             Function_Detected, | ||||
|             Function_Alarmed, | ||||
|             Function_Choice, | ||||
|             Function_PcIntelligence, | ||||
|             Function_PcWillpower, | ||||
|             Function_PcAgility, | ||||
|             Function_PcSpeed, | ||||
|             Function_PcEndurance, | ||||
|             Function_PcPersonality, | ||||
|             Function_PcLuck, | ||||
|             Function_PcCorpus, | ||||
|             Function_Weather, | ||||
|             Function_PcVampire, | ||||
|             Function_Level, | ||||
|             Function_Attacked, | ||||
|             Function_TalkedToPc, | ||||
|             Function_PcHealth, | ||||
|             Function_CreatureTarget, | ||||
|             Function_FriendHit, | ||||
|             Function_Fight, | ||||
|             Function_Hello, | ||||
|             Function_Alarm, | ||||
|             Function_Flee, | ||||
|             Function_ShouldAttack, | ||||
|             Function_Werewolf, | ||||
|             Function_PcWerewolfKills=73, | ||||
| 
 | ||||
|             Function_Global, | ||||
|             Function_Local, | ||||
|             Function_Journal, | ||||
|             Function_Item, | ||||
|             Function_Dead, | ||||
|             Function_NotId, | ||||
|             Function_NotFaction, | ||||
|             Function_NotClass, | ||||
|             Function_NotRace, | ||||
|             Function_NotCell, | ||||
|             Function_NotLocal, | ||||
| 
 | ||||
|             Function_None | ||||
|         }; | ||||
| 
 | ||||
|         enum RelationType | ||||
|         { | ||||
|             Relation_Equal, | ||||
|             Relation_NotEqual, | ||||
|             Relation_Greater, | ||||
|             Relation_GreaterOrEqual, | ||||
|             Relation_Less, | ||||
|             Relation_LessOrEqual, | ||||
| 
 | ||||
|             Relation_None | ||||
|         }; | ||||
| 
 | ||||
|         enum ComparisonType | ||||
|         { | ||||
|             Comparison_Boolean, | ||||
|             Comparison_Integer, | ||||
|             Comparison_Numeric, | ||||
| 
 | ||||
|             Comparison_None | ||||
|         }; | ||||
| 
 | ||||
|         static const size_t RuleMinSize; | ||||
| 
 | ||||
|         static const size_t FunctionPrefixOffset; | ||||
|         static const size_t FunctionIndexOffset; | ||||
|         static const size_t RelationIndexOffset; | ||||
|         static const size_t VarNameOffset; | ||||
| 
 | ||||
|         static const char* FunctionEnumStrings[]; | ||||
|         static const char* RelationEnumStrings[]; | ||||
|         static const char* ComparisonEnumStrings[]; | ||||
| 
 | ||||
|         static std::string convertToString(FunctionName name); | ||||
|         static std::string convertToString(RelationType type); | ||||
|         static std::string convertToString(ComparisonType type); | ||||
| 
 | ||||
|         ConstInfoSelectWrapper(const ESM::DialInfo::SelectStruct& select); | ||||
| 
 | ||||
|         FunctionName getFunctionName() const; | ||||
|         RelationType getRelationType() const; | ||||
|         ComparisonType getComparisonType() const; | ||||
| 
 | ||||
|         bool hasVariable() const; | ||||
|         const std::string& getVariableName() const; | ||||
| 
 | ||||
|         bool conditionIsAlwaysTrue() const; | ||||
|         bool conditionIsNeverTrue() const; | ||||
|         bool variantTypeIsValid() const; | ||||
| 
 | ||||
|         const ESM::Variant& getVariant() const; | ||||
| 
 | ||||
|         std::string toString() const; | ||||
| 
 | ||||
|     protected: | ||||
| 
 | ||||
|         void readRule(); | ||||
|         void readFunctionName(); | ||||
|         void readRelationType(); | ||||
|         void readVariableName(); | ||||
|         void updateHasVariable(); | ||||
|         void updateComparisonType(); | ||||
| 
 | ||||
|         std::pair<int, int> getConditionIntRange() const; | ||||
|         std::pair<float, float> getConditionFloatRange() const; | ||||
| 
 | ||||
|         std::pair<int, int> getValidIntRange() const; | ||||
|         std::pair<float, float> getValidFloatRange() const; | ||||
| 
 | ||||
|         template <typename Type1, typename Type2> | ||||
|         bool rangeContains(Type1 value, std::pair<Type2,Type2> range) const; | ||||
| 
 | ||||
|         template <typename Type1, typename Type2> | ||||
|         bool rangesOverlap(std::pair<Type1,Type1> range1, std::pair<Type2,Type2> range2) const; | ||||
| 
 | ||||
|         template <typename Type1, typename Type2> | ||||
|         bool rangeFullyContains(std::pair<Type1,Type1> containing, std::pair<Type2,Type2> test) const; | ||||
| 
 | ||||
|         template <typename Type1, typename Type2> | ||||
|         bool rangesMatch(std::pair<Type1,Type1> range1, std::pair<Type2,Type2> range2) const; | ||||
| 
 | ||||
|         template <typename Type1, typename Type2> | ||||
|         bool conditionIsAlwaysTrue(std::pair<Type1,Type1> conditionRange, std::pair<Type2,Type2> validRange) const; | ||||
| 
 | ||||
|         template <typename Type1, typename Type2> | ||||
|         bool conditionIsNeverTrue(std::pair<Type1,Type1> conditionRange, std::pair<Type2,Type2> validRange) const; | ||||
| 
 | ||||
|         FunctionName mFunctionName; | ||||
|         RelationType mRelationType; | ||||
|         ComparisonType mComparisonType; | ||||
| 
 | ||||
|         bool mHasVariable; | ||||
|         std::string mVariableName; | ||||
| 
 | ||||
|     private: | ||||
| 
 | ||||
|         const ESM::DialInfo::SelectStruct& mConstSelect; | ||||
|     }; | ||||
| 
 | ||||
|     // Wrapper for DialInfo::SelectStruct that can modify the wrapped select struct
 | ||||
|     class InfoSelectWrapper : public ConstInfoSelectWrapper | ||||
|     { | ||||
|     public: | ||||
| 
 | ||||
|         InfoSelectWrapper(ESM::DialInfo::SelectStruct& select); | ||||
| 
 | ||||
|         // Wrapped SelectStruct will not be modified until update() is called
 | ||||
|         void setFunctionName(FunctionName name); | ||||
|         void setRelationType(RelationType type); | ||||
|         void setVariableName(const std::string& name); | ||||
| 
 | ||||
|         // Modified wrapped SelectStruct
 | ||||
|         void update(); | ||||
| 
 | ||||
|         // This sets properties based on the function name to its defaults and updates the wrapped object
 | ||||
|         void setDefaults(); | ||||
| 
 | ||||
|         ESM::Variant& getVariant(); | ||||
| 
 | ||||
|     private: | ||||
| 
 | ||||
|         ESM::DialInfo::SelectStruct& mSelect; | ||||
| 
 | ||||
|         void writeRule(); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -6,6 +6,7 @@ | |||
| #include "idcollection.hpp" | ||||
| #include "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()) | ||||
|                 { | ||||
|                     // FIXME: when these change the values of the other columns need to change
 | ||||
|                     // correspondingly (and automatically)
 | ||||
|                     case 1: | ||||
|                     { | ||||
|                         conditions[subRowIndex].mSelectRule[1] = '1';             // Function
 | ||||
|                         // default to "Rank Low"
 | ||||
|                         conditions[subRowIndex].mSelectRule[2] = '0'; | ||||
|                         conditions[subRowIndex].mSelectRule[3] = '0'; | ||||
|                         break; | ||||
|                     } | ||||
|                     case 2:  conditions[subRowIndex].mSelectRule[1] = '2'; break; // Global
 | ||||
|                     case 3:  conditions[subRowIndex].mSelectRule[1] = '3'; break; // Local
 | ||||
|                     case 4:  conditions[subRowIndex].mSelectRule[1] = '4'; break; // Journal
 | ||||
|                     case 5:  conditions[subRowIndex].mSelectRule[1] = '5'; break; // Item
 | ||||
|                     case 6:  conditions[subRowIndex].mSelectRule[1] = '6'; break; // Dead
 | ||||
|                     case 7:  conditions[subRowIndex].mSelectRule[1] = '7'; break; // Not ID
 | ||||
|                     case 8:  conditions[subRowIndex].mSelectRule[1] = '8'; break; // Not Faction
 | ||||
|                     case 9:  conditions[subRowIndex].mSelectRule[1] = '9'; break; // Not Class
 | ||||
|                     case 10: conditions[subRowIndex].mSelectRule[1] = 'A'; break; // Not Race
 | ||||
|                     case 11: conditions[subRowIndex].mSelectRule[1] = 'B'; break; // Not Cell
 | ||||
|                     case 12: conditions[subRowIndex].mSelectRule[1] = 'C'; break; // Not Local
 | ||||
|                     default: return; // return without saving
 | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|             case 1: | ||||
|             { | ||||
|                 if (conditions[subRowIndex].mSelectRule[1] == '1') | ||||
|                 { | ||||
|                     std::map<const std::string, std::string>::const_iterator it = sEncToInfoFunc.begin(); | ||||
|                     for (;it != sEncToInfoFunc.end(); ++it) | ||||
|                     { | ||||
|                         if (it->second == value.toString().toUtf8().constData()) | ||||
|                         { | ||||
|                             std::string rule = conditions[subRowIndex].mSelectRule.substr(0, 2); | ||||
|                             rule.append(it->first); | ||||
|                             // leave old values for undo (NOTE: may not be vanilla's behaviour)
 | ||||
|                             rule.append(conditions[subRowIndex].mSelectRule.substr(4)); | ||||
|                             conditions[subRowIndex].mSelectRule = rule; | ||||
|                             break; | ||||
|                         } | ||||
|                     } | ||||
|                 infoSelectWrapper.setFunctionName(static_cast<ConstInfoSelectWrapper::FunctionName>(value.toInt())); | ||||
| 
 | ||||
|                     if (it == sEncToInfoFunc.end()) | ||||
|                         return; // return without saving; TODO: maybe log an error here
 | ||||
|                 } | ||||
|                 else | ||||
|                 if (infoSelectWrapper.getComparisonType() != ConstInfoSelectWrapper::Comparison_Numeric && | ||||
|                     infoSelectWrapper.getVariant().getType() != ESM::VT_Int) | ||||
|                 { | ||||
|                     // 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.getVariant().setType(ESM::VT_Int); | ||||
|                 } | ||||
| 
 | ||||
|                 infoSelectWrapper.update(); | ||||
|                 break; | ||||
|             } | ||||
|             case 2: | ||||
|             case 1: // Variable
 | ||||
|             { | ||||
|                 // 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.setVariableName(value.toString().toUtf8().constData()); | ||||
|                 infoSelectWrapper.update(); | ||||
|                 break; | ||||
|             } | ||||
|             case 3: | ||||
|             case 2: // Relation
 | ||||
|             { | ||||
|                 switch (conditions[subRowIndex].mValue.getType()) | ||||
|                 infoSelectWrapper.setRelationType(static_cast<ConstInfoSelectWrapper::RelationType>(value.toInt())); | ||||
|                 infoSelectWrapper.update(); | ||||
|                 break; | ||||
|             } | ||||
|             case 3: // Value
 | ||||
|             { | ||||
|                 switch (infoSelectWrapper.getComparisonType()) | ||||
|                 { | ||||
|                     case ESM::VT_String: | ||||
|                     case ConstInfoSelectWrapper::Comparison_Numeric: | ||||
|                     { | ||||
|                         conditions[subRowIndex].mValue.setString (value.toString().toUtf8().constData()); | ||||
|                         // QVariant seems to have issues converting 0
 | ||||
|                         if ((value.toInt(&conversionResult) && conversionResult) || value.toString().compare("0") == 0) | ||||
|                         { | ||||
|                             infoSelectWrapper.getVariant().setType(ESM::VT_Int); | ||||
|                             infoSelectWrapper.getVariant().setInteger(value.toInt()); | ||||
|                         } | ||||
|                         else if (value.toFloat(&conversionResult) && conversionResult) | ||||
|                         { | ||||
|                             infoSelectWrapper.getVariant().setType(ESM::VT_Float); | ||||
|                             infoSelectWrapper.getVariant().setFloat(value.toFloat()); | ||||
|                         } | ||||
|                         break; | ||||
|                     } | ||||
|                     case ESM::VT_Int: | ||||
|                     case ESM::VT_Short: | ||||
|                     case ESM::VT_Long: | ||||
|                     case ConstInfoSelectWrapper::Comparison_Boolean: | ||||
|                     case ConstInfoSelectWrapper::Comparison_Integer: | ||||
|                     { | ||||
|                         conditions[subRowIndex].mValue.setInteger (value.toInt()); | ||||
|                         break; | ||||
|                     } | ||||
|                     case ESM::VT_Float: | ||||
|                     { | ||||
|                         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,29 +29,29 @@ 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) | ||||
| { | ||||
|     std::string enchantmentName = !object.isEmpty() ? object.getClass().getEnchantment(object) : ""; | ||||
|     if (!enchantmentName.empty()) | ||||
|     { | ||||
|         const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find( | ||||
|                     enchantmentName); | ||||
|         if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) | ||||
|         { | ||||
|             MWMechanics::CastSpell cast(attacker, victim); | ||||
|             cast.mHitPosition = hitPosition; | ||||
|             cast.cast(object); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| namespace MWMechanics | ||||
| { | ||||
| 
 | ||||
|     bool applyOnStrikeEnchantment (const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim, const MWWorld::Ptr& object, const osg::Vec3f& hitPosition) | ||||
|     { | ||||
|         std::string enchantmentName = !object.isEmpty() ? object.getClass().getEnchantment(object) : ""; | ||||
|         if (!enchantmentName.empty()) | ||||
|         { | ||||
|             const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find( | ||||
|                         enchantmentName); | ||||
|             if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) | ||||
|             { | ||||
|                 MWMechanics::CastSpell cast(attacker, victim); | ||||
|                 cast.mHitPosition = hitPosition; | ||||
|                 cast.cast(object, false); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     bool blockMeleeAttack(const MWWorld::Ptr &attacker, const MWWorld::Ptr &blocker, const MWWorld::Ptr &weapon, float damage, float attackStrength) | ||||
|     { | ||||
|         if (!blocker.getClass().hasInventoryStore(blocker)) | ||||
|  | @ -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…
	
		Reference in a new issue