diff --git a/.travis.yml b/.travis.yml index 04d019c0d8..5d0326a070 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,17 +4,16 @@ compiler: branches: only: - master - - next + - /openmw-.*$/ before_install: - pwd - - git submodule update --init --recursive - echo "yes" | sudo add-apt-repository "deb http://archive.ubuntu.com/ubuntu `lsb_release -sc` main universe restricted multiverse" - echo "yes" | sudo apt-add-repository ppa:openmw/openmw - sudo apt-get update -qq - - sudo apt-get install -qq libboost-all-dev libgtest-dev google-mock libzzip-dev uuid-dev - - sudo apt-get install -qq libqt4-dev libxaw7-dev libxrandr-dev libfreeimage-dev libpng-dev - - sudo apt-get install -qq libopenal-dev libmpg123-dev libsndfile1-dev - - sudo apt-get install -qq libavcodec-dev libavformat-dev libavdevice-dev libavutil-dev libswscale-dev libpostproc-dev + - sudo apt-get install -qq libboost-all-dev libgtest-dev google-mock + - sudo apt-get install -qq libqt4-dev + - sudo apt-get install -qq libopenal-dev + - sudo apt-get install -qq libavcodec-dev libavformat-dev libavutil-dev libswscale-dev - sudo apt-get install -qq libbullet-dev libogre-1.9-dev libmygui-dev libsdl2-dev libunshield-dev - sudo mkdir /usr/src/gtest/build - cd /usr/src/gtest/build @@ -37,3 +36,9 @@ notifications: email: on_success: change on_failure: always + irc: + channels: + - "chat.freenode.net#openmw" + on_success: change + on_failure: always + diff --git a/CMakeLists.txt b/CMakeLists.txt index 01ac9b23f3..ae9ec8ac02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,40 +4,65 @@ if (APPLE) set(APP_BUNDLE_NAME "${CMAKE_PROJECT_NAME}.app") set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/${APP_BUNDLE_NAME}") - - set(CMAKE_EXE_LINKER_FLAGS "-F /Library/Frameworks") - set(CMAKE_SHARED_LINKER_FLAGS "-F /Library/Frameworks") - set(CMAKE_MODULE_LINKER_FLAGS "-F /Library/Frameworks") endif (APPLE) -# Macros - set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) -include(OpenMWMacros) - # Version +message(STATUS "Configuring OpenMW...") -include(GetGitRevisionDescription) +set(OPENMW_VERSION_MAJOR 0) +set(OPENMW_VERSION_MINOR 28) +set(OPENMW_VERSION_RELEASE 0) -get_git_tag_revision(TAGHASH --tags --max-count=1) -get_git_head_revision(REFSPEC COMMITHASH) -git_describe(VERSION --tags ${TAGHASH}) +set(OPENMW_VERSION_COMMITHASH "") +set(OPENMW_VERSION_TAGHASH "") -string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}") -if (MATCH) - string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${VERSION}") - string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${VERSION}") - string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${VERSION}") +set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") - set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") - set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") - set(OPENMW_VERSION_TAGHASH "${TAGHASH}") +if(EXISTS ${PROJECT_SOURCE_DIR}/.git) + if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) + find_package(Git) - message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...") -else (MATCH) - message(FATAL_ERROR "Failed to get valid version information from Git") -endif (MATCH) + if(GIT_FOUND) + include(GetGitRevisionDescription) + get_git_tag_revision(TAGHASH --tags --max-count=1) + get_git_head_revision(REFSPEC COMMITHASH) + git_describe(VERSION --tags ${TAGHASH}) + + string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}") + if(MATCH) + string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" GIT_VERSION_MAJOR "${VERSION}") + string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_MINOR "${VERSION}") + string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_RELEASE "${VERSION}") + + set(GIT_VERSION "${GIT_VERSION_MAJOR}.${GIT_VERSION_MINOR}.${GIT_VERSION_RELEASE}") + + if(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) + message(FATAL_ERROR "Silly Zini forgot to update the version again...") + else(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) + set(OPENMW_VERSION_MAJOR ${GIT_VERSION_MAJOR}) + set(OPENMW_VERSION_MINOR ${GIT_VERSION_MINOR}) + set(OPENMW_VERSION_RELEASE ${GIT_VERSION_RELEASE}) + + set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}") + set(OPENMW_VERSION_TAGHASH "${TAGHASH}") + endif(NOT ${OPENMW_VERSION} STREQUAL ${GIT_VERSION}) + + message(STATUS "OpenMW version ${OPENMW_VERSION}") + else(MATCH) + message(WARNING "Failed to get valid version information from Git") + endif(MATCH) + else(GIT_FOUND) + message(WARNING "Git executable not found") + endif(GIT_FOUND) + else(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) + message(STATUS "Shallow Git clone detected, not attempting to retrieve version info") + endif(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) +endif(EXISTS ${PROJECT_SOURCE_DIR}/.git) + +# Macros +include(OpenMWMacros) # doxygen main page @@ -206,10 +231,6 @@ if (MSVC10) set(PLATFORM_INCLUDE_DIR "") endif() -if (APPLE) - set(Boost_USE_STATIC_LIBS ON) -endif (APPLE) - # Dependencies # Fix for not visible pthreads functions for linker with glibc 2.15 @@ -260,10 +281,15 @@ link_directories(${SDL2_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS} ${OGRE_LIB_DIR} ${MY if (APPLE) # List used Ogre plugins SET(USED_OGRE_PLUGINS ${OGRE_RenderSystem_GL_LIBRARY_REL} - ${OGRE_Plugin_OctreeSceneManager_LIBRARY_REL} - ${OGRE_Plugin_CgProgramManager_LIBRARY_REL} ${OGRE_Plugin_ParticleFX_LIBRARY_REL}) + # Actually we must use OGRE_Plugin_CgProgramManager_FOUND but it's + # not reliable and equals TRUE even if there's no Ogre Cg plugin + if (Cg_FOUND) + set(USED_OGRE_PLUGINS ${USED_OGRE_PLUGINS} + ${OGRE_Plugin_CgProgramManager_LIBRARY_REL}) + endif () + if (${OGRE_PLUGIN_DIR_REL}}) set(OGRE_PLUGINS_REL_FOUND TRUE) endif () @@ -278,8 +304,6 @@ if (APPLE) set(OGRE_PLUGIN_DIR ${OGRE_PLUGIN_DIR_DBG}) endif () - #set(OGRE_PLUGIN_DIR "${OGRE_PLUGIN_DIR}/") - configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist "${APP_BUNDLE_DIR}/Contents/Info.plist") @@ -301,7 +325,8 @@ endif() add_definitions(-DOGRE_PLUGIN_DIR_REL="${OGRE_PLUGIN_DIR_REL}") add_definitions(-DOGRE_PLUGIN_DIR_DBG="${OGRE_PLUGIN_DIR_DBG}") if (APPLE AND OPENMW_OSX_DEPLOYMENT) - add_definitions(-DOGRE_PLUGIN_DIR="${APP_BUNDLE_NAME}/Contents/Plugins") + # make it empty so plugin loading code can check this and try to find plugins inside app bundle + add_definitions(-DOGRE_PLUGIN_DIR="") else() add_definitions(-DOGRE_PLUGIN_DIR="${OGRE_PLUGIN_DIR}") endif() @@ -657,14 +682,14 @@ if (APPLE) set(CPACK_GENERATOR "DragNDrop") set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) set(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) - set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO}) + set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) set(OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}") - set(OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/OpenCS.app") + set(OPENCS_BUNDLE_NAME "OpenCS.app") + set(OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}") - set(PLUGINS "") set(ABSOLUTE_PLUGINS "") foreach (PLUGIN ${USED_OGRE_PLUGINS}) @@ -672,12 +697,36 @@ if (APPLE) set(ABSOLUTE_PLUGINS ${PLUGIN_ABS} ${ABSOLUTE_PLUGINS}) endforeach () - set(PLUGIN_INSTALL_BASE "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}/Contents/Plugins") - install(FILES ${ABSOLUTE_PLUGINS} DESTINATION "${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}/Contents/Plugins" COMPONENT Runtime) - foreach (PLUGIN ${ABSOLUTE_PLUGINS}) - get_filename_component(PLUGIN_RELATIVE ${PLUGIN} NAME) - set(PLUGINS ${PLUGINS} "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}") - endforeach () + install(CODE " + set(BU_CHMOD_BUNDLE_ITEMS ON) + include(BundleUtilities) + " COMPONENT Runtime) + + # installs used plugins in bundle at given path (bundle_path must be relative to ${CMAKE_INSTALL_PREFIX}) + # and returns list of install paths for all installed plugins + function (install_plugins_for_bundle bundle_path plugins_var) + set(RELATIVE_PLUGIN_INSTALL_BASE "${bundle_path}/Contents/Frameworks") + + set(PLUGINS "") + set(PLUGIN_INSTALL_BASE "\${CMAKE_INSTALL_PREFIX}/${RELATIVE_PLUGIN_INSTALL_BASE}") + + foreach (PLUGIN ${ABSOLUTE_PLUGINS}) + get_filename_component(PLUGIN_RELATIVE ${PLUGIN} NAME) + get_filename_component(PLUGIN_RELATIVE_WE ${PLUGIN} NAME_WE) + + set(PLUGIN_DYLIB_IN_BUNDLE "${PLUGIN_INSTALL_BASE}/${PLUGIN_RELATIVE}/${PLUGIN_RELATIVE_WE}") + set(PLUGINS ${PLUGINS} "${PLUGIN_DYLIB_IN_BUNDLE}") + + install(CODE " + copy_resolved_framework_into_bundle(\"${PLUGIN}/${PLUGIN_RELATIVE_WE}\" \"${PLUGIN_DYLIB_IN_BUNDLE}\") + " COMPONENT Runtime) + endforeach () + + set(${plugins_var} ${PLUGINS} PARENT_SCOPE) + endfunction (install_plugins_for_bundle) + + install_plugins_for_bundle("${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}" PLUGINS) + install_plugins_for_bundle("${INSTALL_SUBDIR}/${OPENCS_BUNDLE_NAME}" OPENCS_PLUGINS) #For now, search unresolved dependencies only in default system paths, so if you put unresolveable (i.e. with @executable_path in id name) lib or framework somewhere else, it would fail set(DIRS "") @@ -690,6 +739,7 @@ if (APPLE) # Current limitations: # 1. Handles only frameworks, not simple libs INSTALL(CODE " + cmake_policy(SET CMP0009 OLD) set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES}) set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) set(CMAKE_SYSTEM_FRAMEWORK_PATH ${CMAKE_SYSTEM_FRAMEWORK_PATH}) @@ -721,11 +771,8 @@ if (APPLE) endif() endfunction(gp_resolve_item_override) - cmake_policy(SET CMP0009 OLD) - set(BU_CHMOD_BUNDLE_ITEMS ON) - include(BundleUtilities) fixup_bundle(\"${OPENMW_APP}\" \"${PLUGINS}\" \"${DIRS}\") - fixup_bundle(\"${OPENCS_APP}\" \"\" \"${DIRS}\") + fixup_bundle(\"${OPENCS_APP}\" \"${OPENCS_PLUGINS}\" \"${DIRS}\") " COMPONENT Runtime) include(CPack) endif (APPLE) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 56b3186ff3..5cf8f8a892 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -76,17 +76,20 @@ Launcher::MainDialog::MainDialog(QWidget *parent) QString revision(OPENMW_VERSION_COMMITHASH); QString tag(OPENMW_VERSION_TAGHASH); - if (revision == tag) { - versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION)); - } else { - versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10))); - } + if (!revision.isEmpty() && !tag.isEmpty()) + { + if (revision == tag) { + versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION)); + } else { + versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10))); + } - // Add the compile date and time - versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(), - QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate), - QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), - QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate))); + // Add the compile date and time + versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(), + QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate), + QLocale(QLocale::C).toTime(QString(__TIME__).simplified(), + QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate))); + } createIcons(); } diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 5c3cd0dcc1..6bcad1d08f 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -24,7 +24,7 @@ opencs_units (model/world opencs_units_noqt (model/world universalid record commands columnbase scriptcontext cell refidcollection - refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection + refidadapter refiddata refidadapterimp ref collectionbase refcollection columns infocollection tablemimedata ) opencs_hdrs_noqt (model/world @@ -38,7 +38,7 @@ opencs_units (model/tools opencs_units_noqt (model/tools mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck - birthsigncheck spellcheck referenceablecheck + birthsigncheck spellcheck referenceablecheck scriptcheck ) @@ -60,7 +60,7 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool - scenetoolmode infocreator + scenetoolmode infocreator scriptedit ) opencs_units (view/render diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 44926610b3..5d5ac4c551 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -210,6 +210,8 @@ int CS::Editor::run() if (mLocal.empty()) return 1; +// temporarily disable OGRE-integration (need to fix path problem first) +#if 0 // TODO: setting Ogre::Root::getSingleton().setRenderSystem(Ogre::Root::getSingleton().getRenderSystemByName("OpenGL Rendering Subsystem")); @@ -221,8 +223,12 @@ int CS::Editor::run() params.insert(std::make_pair("FSAA", "0")); params.insert(std::make_pair("vsync", "false")); params.insert(std::make_pair("hidden", "true")); +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE + params.insert(std::make_pair("macAPI", "cocoa")); +#endif Ogre::RenderWindow* hiddenWindow = Ogre::Root::getSingleton().createRenderWindow("InactiveHidden", 1, 1, false, ¶ms); hiddenWindow->setActive(false); +#endif mStartup.show(); diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index 57eaf2d253..931d633127 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -44,8 +44,11 @@ int main(int argc, char *argv[]) // SceneWidget destructor will delete the created render window, which would be called _after_ Root has shut down :( Application mApplication (argc, argv); +// temporarily disable OGRE-integration (need to fix path problem first) +#if 0 OgreInit::OgreInit ogreInit; ogreInit.init("./opencsOgre.log"); // TODO log path? +#endif #ifdef Q_OS_MAC QDir dir(QCoreApplication::applicationDirPath()); diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 437b0c5131..201fb4342a 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -71,7 +71,10 @@ namespace CSMDoc public: - Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_); + Document (const Files::ConfigurationManager& configuration, + const std::vector< boost::filesystem::path >& files, + const boost::filesystem::path& savePath, + const boost::filesystem::path& resDir, bool new_); ~Document(); diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index b80a186429..b969862e99 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -29,7 +29,9 @@ namespace CSMDoc ~DocumentManager(); - Document *addDocument (const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_); + Document *addDocument (const std::vector< boost::filesystem::path >& files, + const boost::filesystem::path& savePath, + bool new_); ///< The ownership of the returned document is not transferred to the caller. /// /// \param new_ Do not load the last content file in \a files and instead create in an diff --git a/apps/opencs/model/tools/scriptcheck.cpp b/apps/opencs/model/tools/scriptcheck.cpp new file mode 100644 index 0000000000..a5154d2926 --- /dev/null +++ b/apps/opencs/model/tools/scriptcheck.cpp @@ -0,0 +1,103 @@ + +#include "scriptcheck.hpp" + +#include +#include +#include +#include +#include + +#include "../world/data.hpp" + +void CSMTools::ScriptCheckStage::report (const std::string& message, const Compiler::TokenLoc& loc, + Type type) +{ + std::ostringstream stream; + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); + + stream << id.toString() << "|"; + + if (type==ErrorMessage) + stream << "error "; + else + stream << "warning "; + + stream + << "script " << mFile + << ", line " << loc.mLine << ", column " << loc.mColumn + << " (" << loc.mLiteral << "): " << message; + + mMessages->push_back (stream.str()); +} + +void CSMTools::ScriptCheckStage::report (const std::string& message, Type type) +{ + std::ostringstream stream; + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); + + stream << id.toString() << "|"; + + if (type==ErrorMessage) + stream << "error: "; + else + stream << "warning: "; + + stream << message; + + mMessages->push_back (stream.str()); +} + +CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMWorld::Data& data) +: mData (data), mContext (data), mMessages (0) +{ + /// \todo add an option to configure warning mode + setWarningsMode (0); + + Compiler::registerExtensions (mExtensions); + mContext.setExtensions (&mExtensions); +} + +int CSMTools::ScriptCheckStage::setup() +{ + mContext.clear(); + mMessages = 0; + mId.clear(); + + return mData.getScripts().getSize(); +} + +void CSMTools::ScriptCheckStage::perform (int stage, std::vector& messages) +{ + mMessages = &messages; + mId = mData.getScripts().getId (stage); + + try + { + mFile = mData.getScripts().getRecord (stage).get().mId; + std::istringstream input (mData.getScripts().getRecord (stage).get().mScriptText); + + Compiler::Scanner scanner (*this, input, mContext.getExtensions()); + + Compiler::FileParser parser (*this, mContext); + + scanner.scan (parser); + } + catch (const Compiler::SourceException&) + { + // error has already been reported via error handler + } + catch (const std::exception& error) + { + std::ostringstream stream; + + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Script, mId); + + stream << id.toString() << "|Critical compile error: " << error.what(); + + messages.push_back (stream.str()); + } + + mMessages = 0; +} \ No newline at end of file diff --git a/apps/opencs/model/tools/scriptcheck.hpp b/apps/opencs/model/tools/scriptcheck.hpp new file mode 100644 index 0000000000..8de8e1a667 --- /dev/null +++ b/apps/opencs/model/tools/scriptcheck.hpp @@ -0,0 +1,41 @@ +#ifndef CSM_TOOLS_SCRIPTCHECK_H +#define CSM_TOOLS_SCRIPTCHECK_H + +#include +#include + +#include "../doc/stage.hpp" + +#include "../world/scriptcontext.hpp" + +namespace CSMTools +{ + /// \brief VerifyStage: make sure that scripts compile + class ScriptCheckStage : public CSMDoc::Stage, private Compiler::ErrorHandler + { + const CSMWorld::Data& mData; + Compiler::Extensions mExtensions; + CSMWorld::ScriptContext mContext; + std::string mId; + std::string mFile; + std::vector *mMessages; + + virtual void report (const std::string& message, const Compiler::TokenLoc& loc, Type type); + ///< Report error to the user. + + virtual void report (const std::string& message, Type type); + ///< Report a file related error + + public: + + ScriptCheckStage (const CSMWorld::Data& data); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this tage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 64e39ad2f6..79a09dcb48 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -20,6 +20,7 @@ #include "birthsigncheck.hpp" #include "spellcheck.hpp" #include "referenceablecheck.hpp" +#include "scriptcheck.hpp" CSMDoc::Operation *CSMTools::Tools::get (int type) { @@ -77,6 +78,8 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier() mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); + + mVerifier->appendStage (new ScriptCheckStage (mData)); } return mVerifier; diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 70f38c5341..e043336080 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -26,7 +26,49 @@ namespace CSMWorld enum Display { + Display_None, //Do not use Display_String, + + //CONCRETE TYPES STARTS HERE + Display_Skill, + Display_Class, + Display_Faction, + Display_Race, + Display_Sound, + Display_Region, + Display_Birthsign, + Display_Spell, + Display_Cell, + Display_Referenceable, + Display_Activator, + Display_Potion, + Display_Apparatus, + Display_Armor, + Display_Book, + Display_Clothing, + Display_Container, + Display_Creature, + Display_Door, + Display_Ingredient, + Display_CreatureLevelledList, + Display_ItemLevelledList, + Display_Light, + Display_Lockpick, + Display_Miscellaneous, + Display_Npc, + Display_Probe, + Display_Repair, + Display_Static, + Display_Weapon, + Display_Reference, + Display_Filter, + Display_Topic, + Display_Journal, + Display_TopicInfo, + Display_JournalInfo, + Display_Scene, + //CONCRETE TYPES ENDS HERE + Display_Integer, Display_Float, Display_Var, diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 18aac9e0be..def225018f 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -373,7 +373,7 @@ namespace CSMWorld SkillsColumn (int index, bool typePrefix = false, bool major = false) : Column ((typePrefix ? ( major ? Columns::ColumnId_MajorSkill1 : Columns::ColumnId_MinorSkill1) : - Columns::ColumnId_Skill1) + index, ColumnBase::Display_String), + Columns::ColumnId_Skill1) + index, ColumnBase::Display_Skill), mIndex (index), mMajor (major) {} @@ -598,7 +598,7 @@ namespace CSMWorld struct SoundFileColumn : public Column { SoundFileColumn() - : Column (Columns::ColumnId_SoundFile, ColumnBase::Display_String) + : Column (Columns::ColumnId_SoundFile, ColumnBase::Display_Sound) {} virtual QVariant get (const Record& record) const @@ -811,7 +811,7 @@ namespace CSMWorld template struct CellColumn : public Column { - CellColumn() : Column (Columns::ColumnId_Cell, ColumnBase::Display_String) {} + CellColumn() : Column (Columns::ColumnId_Cell, ColumnBase::Display_Cell) {} virtual QVariant get (const Record& record) const { @@ -890,7 +890,7 @@ namespace CSMWorld template struct OwnerColumn : public Column { - OwnerColumn() : Column (Columns::ColumnId_Owner, ColumnBase::Display_String) {} + OwnerColumn() : Column (Columns::ColumnId_Owner, ColumnBase::Display_Npc) {} virtual QVariant get (const Record& record) const { @@ -915,7 +915,7 @@ namespace CSMWorld template struct SoulColumn : public Column { - SoulColumn() : Column (Columns::ColumnId_Soul, ColumnBase::Display_String) {} + SoulColumn() : Column (Columns::ColumnId_Soul, ColumnBase::Display_Creature) {} virtual QVariant get (const Record& record) const { @@ -940,7 +940,7 @@ namespace CSMWorld template struct FactionColumn : public Column { - FactionColumn() : Column (Columns::ColumnId_Faction, ColumnBase::Display_String) {} + FactionColumn() : Column (Columns::ColumnId_Faction, ColumnBase::Display_Faction) {} virtual QVariant get (const Record& record) const { @@ -1090,7 +1090,7 @@ namespace CSMWorld struct TeleportCellColumn : public Column { TeleportCellColumn() - : Column (Columns::ColumnId_TeleportCell, ColumnBase::Display_String) + : Column (Columns::ColumnId_TeleportCell, ColumnBase::Display_Cell) {} virtual QVariant get (const Record& record) const @@ -1146,7 +1146,7 @@ namespace CSMWorld template struct KeyColumn : public Column { - KeyColumn() : Column (Columns::ColumnId_Key, ColumnBase::Display_String) {} + KeyColumn() : Column (Columns::ColumnId_Key, ColumnBase::Display_Miscellaneous) {} virtual QVariant get (const Record& record) const { @@ -1485,7 +1485,7 @@ namespace CSMWorld template struct RaceColumn : public Column { - RaceColumn() : Column (Columns::ColumnId_Race, ColumnBase::Display_String) {} + RaceColumn() : Column (Columns::ColumnId_Race, ColumnBase::Display_Race) {} virtual QVariant get (const Record& record) const { @@ -1510,7 +1510,7 @@ namespace CSMWorld template struct ClassColumn : public Column { - ClassColumn() : Column (Columns::ColumnId_Class, ColumnBase::Display_String) {} + ClassColumn() : Column (Columns::ColumnId_Class, ColumnBase::Display_Class) {} virtual QVariant get (const Record& record) const { @@ -1535,7 +1535,7 @@ namespace CSMWorld template struct PcFactionColumn : public Column { - PcFactionColumn() : Column (Columns::ColumnId_PcFaction, ColumnBase::Display_String) {} + PcFactionColumn() : Column (Columns::ColumnId_PcFaction, ColumnBase::Display_Faction) {} virtual QVariant get (const Record& record) const { diff --git a/apps/opencs/model/world/idtableproxymodel.cpp b/apps/opencs/model/world/idtableproxymodel.cpp index 2b757adfe5..f51b7f818f 100644 --- a/apps/opencs/model/world/idtableproxymodel.cpp +++ b/apps/opencs/model/world/idtableproxymodel.cpp @@ -33,7 +33,9 @@ bool CSMWorld::IdTableProxyModel::filterAcceptsRow (int sourceRow, const QModelI CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent) : QSortFilterProxyModel (parent) -{} +{ + setSortCaseSensitivity (Qt::CaseInsensitive); +} QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const { diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 8a1af35092..f515e34d8e 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -59,7 +59,7 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.push_back (RefIdColumn (Columns::ColumnId_Name, ColumnBase::Display_String)); nameColumns.mName = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Script, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Script, ColumnBase::Display_Script)); nameColumns.mScript = &mColumns.back(); InventoryColumns inventoryColumns (nameColumns); @@ -214,10 +214,10 @@ CSMWorld::RefIdCollection::RefIdCollection() creatureColumns.mFlags.insert (std::make_pair (respawn, ESM::Creature::Respawn)); - mColumns.push_back (RefIdColumn (Columns::ColumnId_OpenSound, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_OpenSound, ColumnBase::Display_Sound)); const RefIdColumn *openSound = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_CloseSound, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_CloseSound, ColumnBase::Display_Sound)); const RefIdColumn *closeSound = &mColumns.back(); LightColumns lightColumns (inventoryColumns); @@ -231,7 +231,7 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.push_back (RefIdColumn (Columns::ColumnId_Colour, ColumnBase::Display_Integer)); lightColumns.mColor = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Sound, ColumnBase::Display_Sound)); lightColumns.mSound = &mColumns.back(); static const struct @@ -263,13 +263,13 @@ CSMWorld::RefIdCollection::RefIdCollection() NpcColumns npcColumns (actorsColumns); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Race, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Race, ColumnBase::Display_Race)); npcColumns.mRace = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Class, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Class, ColumnBase::Display_Class)); npcColumns.mClass = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Faction, ColumnBase::Display_String)); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Faction, ColumnBase::Display_Faction)); npcColumns.mFaction = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::Columnid_Hair, ColumnBase::Display_String)); @@ -432,7 +432,7 @@ void CSMWorld::RefIdCollection::removeRows (int index, int count) void CSMWorld::RefIdCollection::appendBlankRecord (const std::string& id, UniversalId::Type type) { - mData.appendRecord (type, id); + mData.appendRecord (type, id, false); } int CSMWorld::RefIdCollection::searchId (const std::string& id) const @@ -450,7 +450,7 @@ void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record) mData.getRecord (mData.globalToLocalIndex (index)).assign (record); } -void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, +void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, const std::string& destination, const CSMWorld::UniversalId::Type type) { @@ -467,7 +467,7 @@ void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, int index = mData.getAppendIndex (type); - mData.appendRecord (type, id); + mData.appendRecord (type, id, false); mData.getRecord (mData.globalToLocalIndex (index)).assign (record); } @@ -515,7 +515,7 @@ void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, Univers { // new record int index = mData.getAppendIndex (type); - mData.appendRecord (type, id); + mData.appendRecord (type, id, base); RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 9d980a99cd..f67ab21526 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -131,7 +131,7 @@ CSMWorld::RecordBase& CSMWorld::RefIdData::getRecord (const LocalIndex& index) return iter->second->getRecord (index.first); } -void CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::string& id) +void CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::string& id, bool base) { std::map::iterator iter = mRecordContainers.find (type); @@ -139,7 +139,7 @@ void CSMWorld::RefIdData::appendRecord (UniversalId::Type type, const std::strin if (iter==mRecordContainers.end()) throw std::logic_error ("invalid local index type"); - iter->second->appendRecord (id); + iter->second->appendRecord (id, base); mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), LocalIndex (iter->second->getSize()-1, type))); diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index cf98fee213..1b600364c7 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -45,8 +45,8 @@ namespace CSMWorld virtual RecordBase& getRecord (int index)= 0; - virtual void appendRecord (const std::string& id) = 0; - + virtual void appendRecord (const std::string& id, bool base) = 0; + virtual void insertRecord (RecordBase& record) = 0; virtual void load (int index, ESM::ESMReader& reader, bool base) = 0; @@ -69,8 +69,8 @@ namespace CSMWorld virtual RecordBase& getRecord (int index); - virtual void appendRecord (const std::string& id); - + virtual void appendRecord (const std::string& id, bool base); + virtual void insertRecord (RecordBase& record); virtual void load (int index, ESM::ESMReader& reader, bool base); @@ -88,7 +88,7 @@ namespace CSMWorld Record& newRecord = dynamic_cast& >(record); mContainer.push_back(newRecord); } - + template int RefIdDataContainer::getSize() const { @@ -108,12 +108,15 @@ namespace CSMWorld } template - void RefIdDataContainer::appendRecord (const std::string& id) + void RefIdDataContainer::appendRecord (const std::string& id, bool base) { Record record; + + record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + + record.mBase.mId = id; record.mModified.mId = id; - record.mModified.blank(); - record.mState = RecordBase::State_ModifiedOnly; + (base ? record.mBase : record.mModified).blank(); mContainer.push_back (record); } @@ -206,14 +209,14 @@ namespace CSMWorld LocalIndex searchId (const std::string& id) const; void erase (int index, int count); - + void insertRecord(CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id); const RecordBase& getRecord (const LocalIndex& index) const; RecordBase& getRecord (const LocalIndex& index); - void appendRecord (UniversalId::Type type, const std::string& id); + void appendRecord (UniversalId::Type type, const std::string& id, bool base); int getAppendIndex (UniversalId::Type type) const; diff --git a/apps/opencs/model/world/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp index 86689d823b..9da49defe7 100644 --- a/apps/opencs/model/world/scriptcontext.cpp +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -5,23 +5,92 @@ #include +#include +#include +#include + #include "data.hpp" CSMWorld::ScriptContext::ScriptContext (const Data& data) : mData (data), mIdsUpdated (false) {} bool CSMWorld::ScriptContext::canDeclareLocals() const { - return false; + return true; } char CSMWorld::ScriptContext::getGlobalType (const std::string& name) const { + int index = mData.getGlobals().searchId (name); + + if (index!=-1) + { + switch (mData.getGlobals().getRecord (index).get().mValue.getType()) + { + case ESM::VT_Short: return 's'; + case ESM::VT_Long: return 'l'; + case ESM::VT_Float: return 'f'; + + default: return ' '; + } + } + return ' '; } -char CSMWorld::ScriptContext::getMemberType (const std::string& name, const std::string& id) const +std::pair CSMWorld::ScriptContext::getMemberType (const std::string& name, + const std::string& id) const { - return ' '; + /// \todo invalidate locals cache on change to scripts + + std::string id2 = Misc::StringUtils::lowerCase (id); + + int index = mData.getScripts().searchId (id2); + bool reference = false; + + if (index!=-1) + { + // ID is not a script ID. Search for a matching referenceable instead. + index = mData.getReferenceables().searchId (id2); + + if (index!=-1) + { + // Referenceable found. + int columnIndex = mData.getReferenceables().searchColumnIndex (Columns::ColumnId_Script); + + if (columnIndex!=-1) + { + id2 = Misc::StringUtils::lowerCase (mData.getReferenceables(). + getData (index, columnIndex).toString().toUtf8().constData()); + + if (!id2.empty()) + { + // Referenceable has a script -> use it. + index = mData.getScripts().searchId (id2); + reference = true; + } + } + } + } + + if (index==-1) + return std::make_pair (' ', false); + + std::map::iterator iter = mLocals.find (id2); + + if (iter==mLocals.end()) + { + Compiler::Locals locals; + + Compiler::NullErrorHandler errorHandler; + std::istringstream stream (mData.getScripts().getRecord (index).get().mScriptText); + Compiler::QuickFileParser parser (errorHandler, *this, locals); + Compiler::Scanner scanner (errorHandler, stream, getExtensions()); + scanner.scan (parser); + + iter = mLocals.insert (std::make_pair (id2, locals)).first; + } + + return std::make_pair (iter->second.getType (Misc::StringUtils::lowerCase (name)), reference); } bool CSMWorld::ScriptContext::isId (const std::string& name) const @@ -31,6 +100,7 @@ bool CSMWorld::ScriptContext::isId (const std::string& name) const mIds = mData.getIds(); std::for_each (mIds.begin(), mIds.end(), &Misc::StringUtils::lowerCase); + std::sort (mIds.begin(), mIds.end()); mIdsUpdated = true; } @@ -38,7 +108,19 @@ bool CSMWorld::ScriptContext::isId (const std::string& name) const return std::binary_search (mIds.begin(), mIds.end(), Misc::StringUtils::lowerCase (name)); } +bool CSMWorld::ScriptContext::isJournalId (const std::string& name) const +{ + return mData.getJournals().searchId (name)!=-1; +} + void CSMWorld::ScriptContext::invalidateIds() { mIdsUpdated = false; +} + +void CSMWorld::ScriptContext::clear() +{ + mIds.clear(); + mIdsUpdated = false; + mLocals.clear(); } \ No newline at end of file diff --git a/apps/opencs/model/world/scriptcontext.hpp b/apps/opencs/model/world/scriptcontext.hpp index b839b5a432..29ee42645a 100644 --- a/apps/opencs/model/world/scriptcontext.hpp +++ b/apps/opencs/model/world/scriptcontext.hpp @@ -3,8 +3,10 @@ #include #include +#include #include +#include namespace CSMWorld { @@ -15,6 +17,7 @@ namespace CSMWorld const Data& mData; mutable std::vector mIds; mutable bool mIdsUpdated; + mutable std::map mLocals; public: @@ -26,13 +29,23 @@ namespace CSMWorld virtual char getGlobalType (const std::string& name) const; ///< 'l: long, 's': short, 'f': float, ' ': does not exist. - virtual char getMemberType (const std::string& name, const std::string& id) const; - ///< 'l: long, 's': short, 'f': float, ' ': does not exist. + virtual std::pair getMemberType (const std::string& name, + const std::string& id) const; + ///< Return type of member variable \a name in script \a id or in script of reference of + /// \a id + /// \return first: 'l: long, 's': short, 'f': float, ' ': does not exist. + /// second: true: script of reference virtual bool isId (const std::string& name) const; ///< Does \a name match an ID, that can be referenced? + virtual bool isJournalId (const std::string& name) const; + ///< Does \a name match a journal ID? + void invalidateIds(); + + void clear(); + ///< Remove all cached data. }; } diff --git a/apps/opencs/model/world/tablemimedata.cpp b/apps/opencs/model/world/tablemimedata.cpp new file mode 100644 index 0000000000..b56c9c8c25 --- /dev/null +++ b/apps/opencs/model/world/tablemimedata.cpp @@ -0,0 +1,446 @@ +#include "tablemimedata.hpp" +#include + +#include "universalid.hpp" +#include "columnbase.hpp" + +CSMWorld::TableMimeData::TableMimeData (UniversalId id, const CSMDoc::Document& document) : +mDocument(document) +{ + mUniversalId.push_back (id); + mObjectsFormats << QString::fromStdString ("tabledata/" + id.getTypeName()); +} + +CSMWorld::TableMimeData::TableMimeData (std::vector< CSMWorld::UniversalId >& id, const CSMDoc::Document& document) : + mUniversalId (id), mDocument(document) +{ + for (std::vector::iterator it (mUniversalId.begin()); it != mUniversalId.end(); ++it) + { + mObjectsFormats << QString::fromStdString ("tabledata/" + it->getTypeName()); + } +} + +QStringList CSMWorld::TableMimeData::formats() const +{ + return mObjectsFormats; +} + +CSMWorld::TableMimeData::~TableMimeData() +{ +} + +std::string CSMWorld::TableMimeData::getIcon() const +{ + if (mUniversalId.empty()) + { + throw ("TableMimeData holds no UniversalId"); + } + + std::string tmpIcon; + bool firstIteration = true; + + for (unsigned i = 0; i < mUniversalId.size(); ++i) + { + if (firstIteration) + { + firstIteration = false; + tmpIcon = mUniversalId[i].getIcon(); + continue; + } + + if (tmpIcon != mUniversalId[i].getIcon()) + { + return ":/multitype.png"; //icon stolen from gnome + } + + tmpIcon = mUniversalId[i].getIcon(); + } + + return mUniversalId.begin()->getIcon(); //All objects are of the same type; +} + +std::vector< CSMWorld::UniversalId > CSMWorld::TableMimeData::getData() const +{ + return mUniversalId; +} + + +bool CSMWorld::TableMimeData::holdsType (CSMWorld::UniversalId::Type type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == type) + { + return true; + } + } + + return false; +} + +bool CSMWorld::TableMimeData::holdsType (CSMWorld::ColumnBase::Display type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == convertEnums (type)) + { + return true; + } + } + + return false; +} + +CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::UniversalId::Type type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == type) + { + return *it; + } + } + + throw ("TableMimeData object does not hold object of the seeked type"); +} + +CSMWorld::UniversalId CSMWorld::TableMimeData::returnMatching (CSMWorld::ColumnBase::Display type) const +{ + for (std::vector::const_iterator it = mUniversalId.begin(); it != mUniversalId.end(); ++it) + { + if (it->getType() == convertEnums (type)) + { + return *it; + } + } + + throw ("TableMimeData object does not hold object of the seeked type"); +} + +bool CSMWorld::TableMimeData::fromDocument (const CSMDoc::Document& document) const +{ + return &document == &mDocument; +} + +CSMWorld::UniversalId::Type CSMWorld::TableMimeData::convertEnums (CSMWorld::ColumnBase::Display type) +{ + switch (type) + { + case CSMWorld::ColumnBase::Display_Race: + return CSMWorld::UniversalId::Type_Race; + + + case CSMWorld::ColumnBase::Display_Skill: + return CSMWorld::UniversalId::Type_Skill; + + + case CSMWorld::ColumnBase::Display_Class: + return CSMWorld::UniversalId::Type_Class; + + + case CSMWorld::ColumnBase::Display_Faction: + return CSMWorld::UniversalId::Type_Faction; + + + case CSMWorld::ColumnBase::Display_Sound: + return CSMWorld::UniversalId::Type_Sound; + + + case CSMWorld::ColumnBase::Display_Region: + return CSMWorld::UniversalId::Type_Region; + + + case CSMWorld::ColumnBase::Display_Birthsign: + return CSMWorld::UniversalId::Type_Birthsign; + + + case CSMWorld::ColumnBase::Display_Spell: + return CSMWorld::UniversalId::Type_Spell; + + + case CSMWorld::ColumnBase::Display_Cell: + return CSMWorld::UniversalId::Type_Cell; + + + case CSMWorld::ColumnBase::Display_Referenceable: + return CSMWorld::UniversalId::Type_Referenceable; + + + case CSMWorld::ColumnBase::Display_Activator: + return CSMWorld::UniversalId::Type_Activator; + + + case CSMWorld::ColumnBase::Display_Potion: + return CSMWorld::UniversalId::Type_Potion; + + + case CSMWorld::ColumnBase::Display_Apparatus: + return CSMWorld::UniversalId::Type_Apparatus; + + + case CSMWorld::ColumnBase::Display_Armor: + return CSMWorld::UniversalId::Type_Armor; + + + case CSMWorld::ColumnBase::Display_Book: + return CSMWorld::UniversalId::Type_Book; + + + case CSMWorld::ColumnBase::Display_Clothing: + return CSMWorld::UniversalId::Type_Clothing; + + + case CSMWorld::ColumnBase::Display_Container: + return CSMWorld::UniversalId::Type_Container; + + + case CSMWorld::ColumnBase::Display_Creature: + return CSMWorld::UniversalId::Type_Creature; + + + case CSMWorld::ColumnBase::Display_Door: + return CSMWorld::UniversalId::Type_Door; + + + case CSMWorld::ColumnBase::Display_Ingredient: + return CSMWorld::UniversalId::Type_Ingredient; + + + case CSMWorld::ColumnBase::Display_CreatureLevelledList: + return CSMWorld::UniversalId::Type_CreatureLevelledList; + + + case CSMWorld::ColumnBase::Display_ItemLevelledList: + return CSMWorld::UniversalId::Type_ItemLevelledList; + + + case CSMWorld::ColumnBase::Display_Light: + return CSMWorld::UniversalId::Type_Light; + + + case CSMWorld::ColumnBase::Display_Lockpick: + return CSMWorld::UniversalId::Type_Lockpick; + + + case CSMWorld::ColumnBase::Display_Miscellaneous: + return CSMWorld::UniversalId::Type_Miscellaneous; + + + case CSMWorld::ColumnBase::Display_Npc: + return CSMWorld::UniversalId::Type_Npc; + + + case CSMWorld::ColumnBase::Display_Probe: + return CSMWorld::UniversalId::Type_Probe; + + + case CSMWorld::ColumnBase::Display_Repair: + return CSMWorld::UniversalId::Type_Repair; + + + case CSMWorld::ColumnBase::Display_Static: + return CSMWorld::UniversalId::Type_Static; + + + case CSMWorld::ColumnBase::Display_Weapon: + return CSMWorld::UniversalId::Type_Weapon; + + + case CSMWorld::ColumnBase::Display_Reference: + return CSMWorld::UniversalId::Type_Reference; + + + case CSMWorld::ColumnBase::Display_Filter: + return CSMWorld::UniversalId::Type_Filter; + + + case CSMWorld::ColumnBase::Display_Topic: + return CSMWorld::UniversalId::Type_Topic; + + + case CSMWorld::ColumnBase::Display_Journal: + return CSMWorld::UniversalId::Type_Journal; + + + case CSMWorld::ColumnBase::Display_TopicInfo: + return CSMWorld::UniversalId::Type_TopicInfo; + + + case CSMWorld::ColumnBase::Display_JournalInfo: + return CSMWorld::UniversalId::Type_JournalInfo; + + + case CSMWorld::ColumnBase::Display_Scene: + return CSMWorld::UniversalId::Type_Scene; + + + case CSMWorld::ColumnBase::Display_Script: + return CSMWorld::UniversalId::Type_Script; + + + default: + return CSMWorld::UniversalId::Type_None; + + } +} + +CSMWorld::ColumnBase::Display CSMWorld::TableMimeData::convertEnums (CSMWorld::UniversalId::Type type) +{ + switch (type) + { + case CSMWorld::UniversalId::Type_Race: + return CSMWorld::ColumnBase::Display_Race; + + + case CSMWorld::UniversalId::Type_Skill: + return CSMWorld::ColumnBase::Display_Skill; + + + case CSMWorld::UniversalId::Type_Class: + return CSMWorld::ColumnBase::Display_Class; + + + case CSMWorld::UniversalId::Type_Faction: + return CSMWorld::ColumnBase::Display_Faction; + + + case CSMWorld::UniversalId::Type_Sound: + return CSMWorld::ColumnBase::Display_Sound; + + + case CSMWorld::UniversalId::Type_Region: + return CSMWorld::ColumnBase::Display_Region; + + + case CSMWorld::UniversalId::Type_Birthsign: + return CSMWorld::ColumnBase::Display_Birthsign; + + + case CSMWorld::UniversalId::Type_Spell: + return CSMWorld::ColumnBase::Display_Spell; + + + case CSMWorld::UniversalId::Type_Cell: + return CSMWorld::ColumnBase::Display_Cell; + + + case CSMWorld::UniversalId::Type_Referenceable: + return CSMWorld::ColumnBase::Display_Referenceable; + + + case CSMWorld::UniversalId::Type_Activator: + return CSMWorld::ColumnBase::Display_Activator; + + + case CSMWorld::UniversalId::Type_Potion: + return CSMWorld::ColumnBase::Display_Potion; + + + case CSMWorld::UniversalId::Type_Apparatus: + return CSMWorld::ColumnBase::Display_Apparatus; + + + case CSMWorld::UniversalId::Type_Armor: + return CSMWorld::ColumnBase::Display_Armor; + + + case CSMWorld::UniversalId::Type_Book: + return CSMWorld::ColumnBase::Display_Book; + + + case CSMWorld::UniversalId::Type_Clothing: + return CSMWorld::ColumnBase::Display_Clothing; + + + case CSMWorld::UniversalId::Type_Container: + return CSMWorld::ColumnBase::Display_Container; + + + case CSMWorld::UniversalId::Type_Creature: + return CSMWorld::ColumnBase::Display_Creature; + + + case CSMWorld::UniversalId::Type_Door: + return CSMWorld::ColumnBase::Display_Door; + + + case CSMWorld::UniversalId::Type_Ingredient: + return CSMWorld::ColumnBase::Display_Ingredient; + + + case CSMWorld::UniversalId::Type_CreatureLevelledList: + return CSMWorld::ColumnBase::Display_CreatureLevelledList; + + + case CSMWorld::UniversalId::Type_ItemLevelledList: + return CSMWorld::ColumnBase::Display_ItemLevelledList; + + + case CSMWorld::UniversalId::Type_Light: + return CSMWorld::ColumnBase::Display_Light; + + + case CSMWorld::UniversalId::Type_Lockpick: + return CSMWorld::ColumnBase::Display_Lockpick; + + + case CSMWorld::UniversalId::Type_Miscellaneous: + return CSMWorld::ColumnBase::Display_Miscellaneous; + + + case CSMWorld::UniversalId::Type_Npc: + return CSMWorld::ColumnBase::Display_Npc; + + + case CSMWorld::UniversalId::Type_Probe: + return CSMWorld::ColumnBase::Display_Probe; + + + case CSMWorld::UniversalId::Type_Repair: + return CSMWorld::ColumnBase::Display_Repair; + + + case CSMWorld::UniversalId::Type_Static: + return CSMWorld::ColumnBase::Display_Static; + + + case CSMWorld::UniversalId::Type_Weapon: + return CSMWorld::ColumnBase::Display_Weapon; + + + case CSMWorld::UniversalId::Type_Reference: + return CSMWorld::ColumnBase::Display_Reference; + + + case CSMWorld::UniversalId::Type_Filter: + return CSMWorld::ColumnBase::Display_Filter; + + + case CSMWorld::UniversalId::Type_Topic: + return CSMWorld::ColumnBase::Display_Topic; + + + case CSMWorld::UniversalId::Type_Journal: + return CSMWorld::ColumnBase::Display_Journal; + + + case CSMWorld::UniversalId::Type_TopicInfo: + return CSMWorld::ColumnBase::Display_TopicInfo; + + + case CSMWorld::UniversalId::Type_JournalInfo: + return CSMWorld::ColumnBase::Display_JournalInfo; + + + case CSMWorld::UniversalId::Type_Scene: + return CSMWorld::ColumnBase::Display_Scene; + + + case CSMWorld::UniversalId::Type_Script: + return CSMWorld::ColumnBase::Display_Script; + + + default: + return CSMWorld::ColumnBase::Display_None; + } +} \ No newline at end of file diff --git a/apps/opencs/model/world/tablemimedata.hpp b/apps/opencs/model/world/tablemimedata.hpp new file mode 100644 index 0000000000..7687f3555f --- /dev/null +++ b/apps/opencs/model/world/tablemimedata.hpp @@ -0,0 +1,63 @@ + +#ifndef TABLEMIMEDATA_H +#define TABLEMIMEDATA_H + +#include + +#include +#include + +#include "universalid.hpp" +#include "columnbase.hpp" + +namespace CSMDoc +{ + class Document; +} + +namespace CSMWorld +{ + +/// \brief Subclass of QmimeData, augmented to contain and transport UniversalIds. +/// +/// This class provides way to construct mimedata object holding the universalid copy +/// Universalid is used in the majority of the tables to store type, id, argument types. +/// This way universalid grants a way to retrive record from the concrete table. +/// Please note, that tablemimedata object can hold multiple universalIds in the vector. + + class TableMimeData : public QMimeData + { + public: + TableMimeData(UniversalId id, const CSMDoc::Document& document); + + TableMimeData(std::vector& id, const CSMDoc::Document& document); + + ~TableMimeData(); + + virtual QStringList formats() const; + + std::string getIcon() const; + + std::vector getData() const; + + bool holdsType(UniversalId::Type type) const; + + bool holdsType(CSMWorld::ColumnBase::Display type) const; + + bool fromDocument(const CSMDoc::Document& document) const; + + UniversalId returnMatching(UniversalId::Type type) const; + + UniversalId returnMatching(CSMWorld::ColumnBase::Display type) const; + + static CSMWorld::UniversalId::Type convertEnums(CSMWorld::ColumnBase::Display type); + static CSMWorld::ColumnBase::Display convertEnums(CSMWorld::UniversalId::Type type); + + private: + std::vector mUniversalId; + QStringList mObjectsFormats; + const CSMDoc::Document& mDocument; + + }; +} +#endif // TABLEMIMEDATA_H \ No newline at end of file diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index e633f4f696..8301ebfd36 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace { @@ -186,7 +187,6 @@ CSMWorld::UniversalId::UniversalId (Type type, const std::string& id) mClass = sIdArg[i].mClass; return; } - throw std::logic_error ("invalid ID argument UniversalId type"); } diff --git a/apps/opencs/view/filter/editwidget.cpp b/apps/opencs/view/filter/editwidget.cpp index 708d450325..cc1578bdd9 100644 --- a/apps/opencs/view/filter/editwidget.cpp +++ b/apps/opencs/view/filter/editwidget.cpp @@ -2,6 +2,8 @@ #include "editwidget.hpp" #include +#include +#include #include "../../model/world/data.hpp" @@ -56,3 +58,146 @@ void CSVFilter::EditWidget::filterRowsInserted (const QModelIndex& parent, int s { textChanged (text()); } + +void CSVFilter::EditWidget::createFilterRequest (std::vector< std::pair< std::string, std::vector< std::string > > >& filterSource, + Qt::DropAction action) +{ + const unsigned count = filterSource.size(); + bool multipleElements = false; + + switch (count) //setting multipleElements; + { + case 0: //empty + return; //nothing to do here + + case 1: //only single + multipleElements = false; + break; + + default: + multipleElements = true; + break; + } + + Qt::KeyboardModifiers key = QApplication::keyboardModifiers(); + QString oldContent (text()); + + bool replaceMode = false; + std::string orAnd; + + switch (key) //setting replaceMode and string used to glue expressions + { + case Qt::ShiftModifier: + orAnd = "!or("; + replaceMode = false; + break; + + case Qt::ControlModifier: + orAnd = "!and("; + replaceMode = false; + break; + + default: + replaceMode = true; + break; + } + + if (oldContent.isEmpty() || !oldContent.contains (QRegExp ("^!.*$", Qt::CaseInsensitive))) //if line edit is empty or it does not contain one shot filter go into replace mode + { + replaceMode = true; + } + + if (!replaceMode) + { + oldContent.remove ('!'); + } + + std::stringstream ss; + + if (multipleElements) + { + if (replaceMode) + { + ss<<"!or("; + } else { + ss << orAnd << oldContent.toStdString() << ','; + } + + for (unsigned i = 0; i < count; ++i) + { + ss<4) + { + clear(); + insert (QString::fromUtf8(ss.str().c_str())); + } +} + +std::string CSVFilter::EditWidget::generateFilter (std::pair< std::string, std::vector< std::string > >& seekedString) const +{ + const unsigned columns = seekedString.second.size(); + + bool multipleColumns = false; + switch (columns) + { + case 0: //empty + return ""; //no column to filter + + case 1: //one column to look for + multipleColumns = false; + break; + + default: + multipleColumns = true; + break; + } + + std::stringstream ss; + if (multipleColumns) + { + ss<<"or("; + for (unsigned i = 0; i < columns; ++i) + { + ss<<"string("<<'"'< #include +#include #include "../../model/filter/parser.hpp" #include "../../model/filter/node.hpp" @@ -33,6 +34,9 @@ namespace CSVFilter void filterChanged (boost::shared_ptr filter); + private: + std::string generateFilter(std::pair >& seekedString) const; + private slots: void textChanged (const QString& text); @@ -42,6 +46,11 @@ namespace CSVFilter void filterRowsRemoved (const QModelIndex& parent, int start, int end); void filterRowsInserted (const QModelIndex& parent, int start, int end); + + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + + void useFilterRequest(const std::string& idOfFilter); }; } diff --git a/apps/opencs/view/filter/filterbox.cpp b/apps/opencs/view/filter/filterbox.cpp index 2731708841..a332880252 100644 --- a/apps/opencs/view/filter/filterbox.cpp +++ b/apps/opencs/view/filter/filterbox.cpp @@ -2,9 +2,12 @@ #include "filterbox.hpp" #include +#include #include "recordfilterbox.hpp" +#include + CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) : QWidget (parent) { @@ -21,4 +24,27 @@ CSVFilter::FilterBox::FilterBox (CSMWorld::Data& data, QWidget *parent) connect (recordFilterBox, SIGNAL (filterChanged (boost::shared_ptr)), this, SIGNAL (recordFilterChanged (boost::shared_ptr))); -} \ No newline at end of file + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + recordFilterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), recordFilterBox, SIGNAL(useFilterRequest(const std::string&))); + setAcceptDrops(true); +} + +void CSVFilter::FilterBox::dropEvent (QDropEvent* event) +{ + std::vector data = dynamic_cast (event->mimeData())->getData(); + + emit recordDropped(data, event->proposedAction()); +} + +void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event) +{ + event->acceptProposedAction(); +} + +void CSVFilter::FilterBox::dragMoveEvent (QDragMoveEvent* event) +{ + event->accept(); +} diff --git a/apps/opencs/view/filter/filterbox.hpp b/apps/opencs/view/filter/filterbox.hpp index 2524fa0a38..3817d5e709 100644 --- a/apps/opencs/view/filter/filterbox.hpp +++ b/apps/opencs/view/filter/filterbox.hpp @@ -1,9 +1,13 @@ #ifndef CSV_FILTER_FILTERBOX_H #define CSV_FILTER_FILTERBOX_H +#include + #include +#include #include "../../model/filter/node.hpp" +#include "../../model/world/universalid.hpp" namespace CSMWorld { @@ -16,6 +20,12 @@ namespace CSVFilter { Q_OBJECT + void dragEnterEvent (QDragEnterEvent* event); + + void dropEvent (QDropEvent* event); + + void dragMoveEvent(QDragMoveEvent *event); + public: FilterBox (CSMWorld::Data& data, QWidget *parent = 0); @@ -23,8 +33,13 @@ namespace CSVFilter signals: void recordFilterChanged (boost::shared_ptr filter); + void recordDropped (std::vector& types, Qt::DropAction action); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); }; } #endif + diff --git a/apps/opencs/view/filter/recordfilterbox.cpp b/apps/opencs/view/filter/recordfilterbox.cpp index c405177b01..2a1a1407fa 100644 --- a/apps/opencs/view/filter/recordfilterbox.cpp +++ b/apps/opencs/view/filter/recordfilterbox.cpp @@ -24,4 +24,9 @@ CSVFilter::RecordFilterBox::RecordFilterBox (CSMWorld::Data& data, QWidget *pare connect ( editWidget, SIGNAL (filterChanged (boost::shared_ptr)), this, SIGNAL (filterChanged (boost::shared_ptr))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + editWidget, SLOT(createFilterRequest(std::vector > >&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), editWidget, SLOT(useFilterRequest(const std::string&))); } diff --git a/apps/opencs/view/filter/recordfilterbox.hpp b/apps/opencs/view/filter/recordfilterbox.hpp index 057d69518d..3638dc6c34 100644 --- a/apps/opencs/view/filter/recordfilterbox.hpp +++ b/apps/opencs/view/filter/recordfilterbox.hpp @@ -4,6 +4,7 @@ #include #include +#include #include @@ -27,6 +28,9 @@ namespace CSVFilter signals: void filterChanged (boost::shared_ptr filter); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); }; } diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 620586bd2d..6e327bb6e0 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -67,6 +67,10 @@ namespace CSVRender params.insert(std::make_pair("title", windowTitle.str())); params.insert(std::make_pair("FSAA", "0")); // TODO setting params.insert(std::make_pair("vsync", "false")); // TODO setting +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE + params.insert(std::make_pair("macAPI", "cocoa")); + params.insert(std::make_pair("macAPICocoaUseNSView", "true")); +#endif mWindow = Ogre::Root::getSingleton().createRenderWindow(windowTitle.str(), this->width(), this->height(), false, ¶ms); mWindow->addViewport(mCamera)->setBackgroundColour(Ogre::ColourValue(0.3,0.3,0.3,1)); diff --git a/apps/opencs/view/world/genericcreator.cpp b/apps/opencs/view/world/genericcreator.cpp index cd7a5fa189..31c216e2cb 100644 --- a/apps/opencs/view/world/genericcreator.cpp +++ b/apps/opencs/view/world/genericcreator.cpp @@ -133,9 +133,9 @@ void CSVWorld::GenericCreator::create() std::string id = getId(); std::auto_ptr command (new CSMWorld::CloneCommand ( dynamic_cast (*mData.getTableModel(mListId)), mClonedId, id, mClonedType)); - + mUndoStack.push(command.release()); - + emit done(); emit requestFocus(id); } else { diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index 83b30be133..33ae327a0d 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -43,12 +43,26 @@ toolbar->addTool (new SceneToolMode (toolbar)); toolbar->addTool (new SceneToolMode (toolbar)); layout2->addWidget (toolbar, 0); - +// temporarily disable OGRE-integration (need to fix path problem first) +#if 0 CSVRender::SceneWidget* sceneWidget = new CSVRender::SceneWidget(this); layout2->addWidget (sceneWidget, 1); layout->insertLayout (0, layout2, 1); +#endif + /// \todo replace with rendering widget + QPalette palette2 (palette()); + palette2.setColor (QPalette::Background, Qt::white); + QLabel *placeholder = new QLabel ("Here goes the 3D scene", this); + placeholder->setAutoFillBackground (true); + placeholder->setPalette (palette2); + placeholder->setAlignment (Qt::AlignHCenter); + + layout2->addWidget (placeholder, 1); + + layout->insertLayout (0, layout2, 1); + CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); diff --git a/apps/opencs/view/world/scriptedit.cpp b/apps/opencs/view/world/scriptedit.cpp new file mode 100644 index 0000000000..fccac75b41 --- /dev/null +++ b/apps/opencs/view/world/scriptedit.cpp @@ -0,0 +1,88 @@ +#include "scriptedit.hpp" + +#include + +#include +#include +#include + +#include "../../model/world/universalid.hpp" +#include "../../model/world/tablemimedata.hpp" + +CSVWorld::ScriptEdit::ScriptEdit (QWidget* parent, const CSMDoc::Document& document) : + QTextEdit (parent), + mDocument (document), + mWhiteListQoutes("^[a-z|_]{1}[a-z|0-9|_]{0,}$", Qt::CaseInsensitive) +{ + mAllowedTypes <pos())); + event->acceptProposedAction(); +} + +void CSVWorld::ScriptEdit::dragMoveEvent (QDragMoveEvent* event) +{ + setTextCursor (cursorForPosition (event->pos())); + event->accept(); +} + +void CSVWorld::ScriptEdit::dropEvent (QDropEvent* event) +{ + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + + setTextCursor (cursorForPosition (event->pos())); + + if (mime->fromDocument (mDocument)) + { + std::vector records (mime->getData()); + + for (std::vector::iterator it = records.begin(); it != records.end(); ++it) + { + if (mAllowedTypes.contains (it->getType())) + { + if (stringNeedsQuote(it->getId())) + { + insertPlainText(QString::fromStdString ('"' + it->getId() + '"')); + } else { + insertPlainText(QString::fromStdString (it->getId())); + } + } + } + } +} + +bool CSVWorld::ScriptEdit::stringNeedsQuote (const std::string& id) const +{ + const QString string(QString::fromStdString(id)); // is only for c++11, so let's use qregexp for now. + //I'm not quite sure when do we need to put quotes. To be safe we will use quotes for anything other than… + return !(string.contains(mWhiteListQoutes)); +} diff --git a/apps/opencs/view/world/scriptedit.hpp b/apps/opencs/view/world/scriptedit.hpp new file mode 100644 index 0000000000..b4627c2fee --- /dev/null +++ b/apps/opencs/view/world/scriptedit.hpp @@ -0,0 +1,39 @@ +#ifndef SCRIPTEDIT_H +#define SCRIPTEDIT_H + +#include +#include + +#include "../../model/world/universalid.hpp" + +class QWidget; +class QRegExp; + +namespace CSMDoc +{ + class Document; +} + +namespace CSVWorld +{ + class ScriptEdit : public QTextEdit + { + Q_OBJECT + public: + ScriptEdit (QWidget* parent, const CSMDoc::Document& document); + + private: + QVector mAllowedTypes; + const CSMDoc::Document& mDocument; + const QRegExp mWhiteListQoutes; + + void dragEnterEvent (QDragEnterEvent* event); + + void dropEvent (QDropEvent* event); + + void dragMoveEvent (QDragMoveEvent* event); + + bool stringNeedsQuote(const std::string& id) const; + }; +} +#endif // SCRIPTEDIT_H \ No newline at end of file diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp index 446c34e5f0..fa41151ca3 100644 --- a/apps/opencs/view/world/scriptsubview.cpp +++ b/apps/opencs/view/world/scriptsubview.cpp @@ -13,6 +13,7 @@ #include "../../model/world/idtable.hpp" #include "scripthighlighter.hpp" +#include "scriptedit.hpp" CSVWorld::ScriptSubView::ChangeLock::ChangeLock (ScriptSubView& view) : mView (view) { @@ -27,7 +28,7 @@ CSVWorld::ScriptSubView::ChangeLock::~ChangeLock() CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : SubView (id), mDocument (document), mColumn (-1), mChangeLocked (0) { - setWidget (mEditor = new QTextEdit (this)); + setWidget (mEditor = new ScriptEdit (this, mDocument)); mEditor->setAcceptRichText (false); mEditor->setLineWrapMode (QTextEdit::NoWrap); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 8f6fc46a88..edf3bc6dea 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -4,8 +4,11 @@ #include #include +#include #include #include +#include +#include #include "../../model/world/data.hpp" #include "../../model/world/commands.hpp" @@ -13,6 +16,8 @@ #include "../../model/world/idtable.hpp" #include "../../model/world/record.hpp" #include "../../model/world/columns.hpp" +#include "../../model/world/tablemimedata.hpp" +#include "../../model/world/tablemimedata.hpp" #include "recordstatusdelegate.hpp" #include "util.hpp" @@ -88,7 +93,7 @@ std::vector CSVWorld::Table::listRevertableSelectedIds() const QModelIndexList selectedRows = selectionModel()->selectedRows(); for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); - ++iter) + ++iter) { QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); @@ -158,8 +163,8 @@ std::vector CSVWorld::Table::listDeletableSelectedIds() const } CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, - bool createAndDelete, bool sorting) - : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0) + bool createAndDelete, bool sorting, const CSMDoc::Document& document) + : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0), mDocument(document) { mModel = &dynamic_cast (*data.getTableModel (id)); @@ -235,6 +240,8 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)), this, SLOT (selectionSizeUpdate ())); + + setAcceptDrops(true); } void CSVWorld::Table::setEditLock (bool locked) @@ -432,3 +439,108 @@ void CSVWorld::Table::recordFilterChanged (boost::shared_ptr fi { mProxyModel->setFilter (filter); } + +void CSVWorld::Table::mouseMoveEvent (QMouseEvent* event) +{ + if (event->buttons() & Qt::LeftButton) + { + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size() == 0) + { + return; + } + + QDrag* drag = new QDrag (this); + CSMWorld::TableMimeData* mime = NULL; + + if (selectedRows.size() == 1) + { + mime = new CSMWorld::TableMimeData (getUniversalId (selectedRows.begin()->row()), mDocument); + } + else + { + std::vector idToDrag; + + foreach (QModelIndex it, selectedRows) //I had a dream. Dream where you could use C++11 in OpenMW. + { + idToDrag.push_back (getUniversalId (it.row())); + } + + mime = new CSMWorld::TableMimeData (idToDrag, mDocument); + } + + drag->setMimeData (mime); + drag->setPixmap (QString::fromStdString (mime->getIcon())); + + Qt::DropActions action = Qt::IgnoreAction; + switch (QApplication::keyboardModifiers()) + { + case Qt::ControlModifier: + action = Qt::CopyAction; + break; + + case Qt::ShiftModifier: + action = Qt::MoveAction; + break; + } + + drag->exec(action); + } + +} + +void CSVWorld::Table::dragEnterEvent(QDragEnterEvent *event) +{ + event->acceptProposedAction(); +} + +void CSVWorld::Table::dropEvent(QDropEvent *event) +{ + QModelIndex index = indexAt (event->pos()); + + if (!index.isValid()) + { + return; + } + + const CSMWorld::TableMimeData* mime = dynamic_cast (event->mimeData()); + if (mime->fromDocument (mDocument)) + { + CSMWorld::ColumnBase::Display display = static_cast + (mModel->headerData (index.column(), Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + if (mime->holdsType (display)) + { + CSMWorld::UniversalId record (mime->returnMatching (display)); + + std::auto_ptr command (new CSMWorld::ModifyCommand + (*mProxyModel, index, QVariant (QString::fromUtf8 (record.getId().c_str())))); + + mUndoStack.push (command.release()); + } + } //TODO handle drops from different document +} + +void CSVWorld::Table::dragMoveEvent(QDragMoveEvent *event) +{ + event->accept(); +} + +std::vector CSVWorld::Table::getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const +{ + const int count = mModel->columnCount(); + + std::vector titles; + for (int i = 0; i < count; ++i) + { + CSMWorld::ColumnBase::Display columndisplay = static_cast + (mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); + + if (display == columndisplay) + { + titles.push_back(mModel->headerData (i, Qt::Horizontal).toString().toStdString()); + } + } + return titles; +} \ No newline at end of file diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index d30083333f..615a31b4d7 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -5,8 +5,14 @@ #include #include +#include #include "../../model/filter/node.hpp" +#include "../../model/world/columnbase.hpp" + +namespace CSMDoc { + class Document; +} class QUndoStack; class QAction; @@ -42,6 +48,10 @@ namespace CSVWorld bool mEditLock; int mRecordStatusDisplay; + /// \brief This variable is used exclusivly for checking if dropEvents came from the same document. Most likely you + /// should NOT use it for anything else. + const CSMDoc::Document& mDocument; + private: void contextMenuEvent (QContextMenuEvent *event); @@ -50,9 +60,19 @@ namespace CSVWorld std::vector listDeletableSelectedIds() const; + void mouseMoveEvent(QMouseEvent *event); + + void dragEnterEvent(QDragEnterEvent *event); + + void dragMoveEvent(QDragMoveEvent *event); + + void dropEvent(QDropEvent *event); + public: - Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, bool sorting); + Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, bool createAndDelete, + bool sorting, const CSMDoc::Document& document); + ///< \param createAndDelete Allow creation and deletion of records. /// \param sorting Allow changing order of rows in the view via column headers. @@ -62,6 +82,8 @@ namespace CSVWorld void updateEditorSetting (const QString &settingName, const QString &settingValue); + std::vector getColumnsWithDisplay(CSMWorld::ColumnBase::Display display) const; + signals: void editRequest (int row); diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 981faaf591..e330d47758 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -2,8 +2,10 @@ #include "tablesubview.hpp" #include +#include #include "../../model/doc/document.hpp" +#include "../../model/world/tablemimedata.hpp" #include "../filter/filterbox.hpp" #include "table.hpp" @@ -22,7 +24,7 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0); layout->insertWidget (0, mTable = - new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting), 2); + new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete(), sorting, document), 2); CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); @@ -43,6 +45,9 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D mTable->tableSizeUpdate(); mTable->selectionSizeUpdate(); + mTable->viewport()->installEventFilter(this); + mBottom->installEventFilter(this); + filterBox->installEventFilter(this); if (mBottom->canCreateAndDelete()) { @@ -60,6 +65,14 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D connect (filterBox, SIGNAL (recordFilterChanged (boost::shared_ptr)), mTable, SLOT (recordFilterChanged (boost::shared_ptr))); + + connect(filterBox, SIGNAL(recordDropped(std::vector&, Qt::DropAction)), + this, SLOT(createFilterRequest(std::vector&, Qt::DropAction))); + + connect(this, SIGNAL(useFilterRequest(const std::string&)), filterBox, SIGNAL(useFilterRequest(const std::string&))); + + connect(this, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction)), + filterBox, SIGNAL(createFilterRequest(std::vector > >&, Qt::DropAction))); } void CSVWorld::TableSubView::setEditLock (bool locked) @@ -87,3 +100,33 @@ void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone) { emit cloneRequest(toClone.getId(), toClone.getType()); } + +void CSVWorld::TableSubView::createFilterRequest (std::vector< CSMWorld::UniversalId>& types, Qt::DropAction action) +{ + std::vector > > filterSource; + + for (std::vector::iterator it = types.begin(); it != types.end(); ++it) + { + std::pair > pair( //splited long line + std::make_pair(it->getId(), mTable->getColumnsWithDisplay(CSMWorld::TableMimeData::convertEnums(it->getType())))); + + filterSource.push_back(pair); + } + emit createFilterRequest(filterSource, action); +} + +bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) +{ + if (event->type() == QEvent::Drop) + { + QDropEvent* drop = dynamic_cast(event); + const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); + bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); + if (handled) + { + emit useFilterRequest(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); + } + return handled; + } + return false; +} \ No newline at end of file diff --git a/apps/opencs/view/world/tablesubview.hpp b/apps/opencs/view/world/tablesubview.hpp index d728dc2f38..1f67e0262a 100644 --- a/apps/opencs/view/world/tablesubview.hpp +++ b/apps/opencs/view/world/tablesubview.hpp @@ -3,6 +3,8 @@ #include "../doc/subview.hpp" +#include + class QModelIndex; namespace CSMWorld @@ -39,14 +41,22 @@ namespace CSVWorld virtual void setStatusBar (bool show); + protected: + bool eventFilter(QObject* object, QEvent *event); + signals: void cloneRequest(const std::string&, const CSMWorld::UniversalId::Type); + void createFilterRequest(std::vector > >& filterSource, + Qt::DropAction action); + void useFilterRequest(const std::string& idOfFilter); private slots: void editRequest (int row); void cloneRequest (const CSMWorld::UniversalId& toClone); + void createFilterRequest(std::vector< CSMWorld::UniversalId >& types, + Qt::DropAction action); }; } diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index bf1cca5e0f..e80bd954e3 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -155,6 +155,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager) , mSkipMenu (false) , mUseSound (true) , mCompileAll (false) + , mWarningsMode (1) , mScriptContext (0) , mFSStrict (false) , mScriptConsoleMode (false) @@ -424,7 +425,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mScriptContext->setExtensions (&mExtensions); mEnvironment.setScriptManager (new MWScript::ScriptManager (MWBase::Environment::get().getWorld()->getStore(), - mVerboseScripts, *mScriptContext)); + mVerboseScripts, *mScriptContext, mWarningsMode)); // Create game mechanics system MWMechanics::MechanicsManager* mechanics = new MWMechanics::MechanicsManager; @@ -612,8 +613,12 @@ void OMW::Engine::setStartupScript (const std::string& path) mStartupScript = path; } - void OMW::Engine::setActivationDistanceOverride (int distance) { mActivationDistanceOverride = distance; } + +void OMW::Engine::setWarningsMode (int mode) +{ + mWarningsMode = mode; +} \ No newline at end of file diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 8b2a65b7ed..5c15ddf6fc 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -74,6 +74,7 @@ namespace OMW bool mSkipMenu; bool mUseSound; bool mCompileAll; + int mWarningsMode; std::string mFocusName; std::map mFallbackMap; bool mScriptConsoleMode; @@ -181,6 +182,8 @@ namespace OMW /// Override the game setting specified activation distance. void setActivationDistanceOverride (int distance); + void setWarningsMode (int mode); + private: Files::ConfigurationManager& mCfgMgr; }; diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 67578a214a..3098d953ec 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -136,6 +136,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("script-run", bpo::value()->default_value(""), "select a file containing a list of console commands that is executed on startup") + ("script-warn", bpo::value()->implicit_value (1) + ->default_value (1), + "handling of warnings when compiling scripts\n" + "\t0 - ignore warning\n" + "\t1 - show warning but consider script as correctly compiled anyway\n" + "\t2 - treat warnings as errors") + ("skip-menu", bpo::value()->implicit_value(true) ->default_value(false), "skip main menu on game startup") @@ -241,6 +248,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setScriptConsoleMode (variables["script-console"].as()); engine.setStartupScript (variables["script-run"].as()); engine.setActivationDistanceOverride (variables["activate-dist"].as()); + engine.setWarningsMode (variables["script-warn"].as()); return true; } diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index 971bc3b4e9..33bba07e15 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -3,6 +3,14 @@ #include +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; +} + namespace MWWorld { class Ptr; @@ -52,6 +60,12 @@ namespace MWBase virtual void persuade (int type) = 0; virtual int getTemporaryDispositionChange () const = 0; virtual void applyDispositionChange (int delta) = 0; + + virtual int countSavedGameRecords() const = 0; + + virtual void write (ESM::ESMWriter& writer) const = 0; + + virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 6af8373c55..81ca0ce2bf 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -766,8 +766,11 @@ namespace MWClass ensureCustomData (ptr); - dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore-> - readState (state2.mInventory); + CustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + + customData.mContainerStore->readState (state2.mInventory); + customData.mCreatureStats.readState (state2.mCreatureStats); + } void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) @@ -777,8 +780,10 @@ namespace MWClass ensureCustomData (ptr); - dynamic_cast (*ptr.getRefData().getCustomData()).mContainerStore-> - writeState (state2.mInventory); + CustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + + customData.mContainerStore->writeState (state2.mInventory); + customData.mCreatureStats.writeState (state2.mCreatureStats); } const ESM::GameSetting* Creature::fMinWalkSpeedCreature; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 34ea515bab..3a95f3c294 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1278,8 +1278,11 @@ namespace MWClass ensureCustomData (ptr); - dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore. - readState (state2.mInventory); + CustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + + customData.mInventoryStore.readState (state2.mInventory); + customData.mNpcStats.readState (state2.mNpcStats); + static_cast (customData.mNpcStats).readState (state2.mCreatureStats); } void Npc::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) @@ -1289,8 +1292,11 @@ namespace MWClass ensureCustomData (ptr); - dynamic_cast (*ptr.getRefData().getCustomData()).mInventoryStore. - writeState (state2.mInventory); + CustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + + customData.mInventoryStore.writeState (state2.mInventory); + customData.mNpcStats.writeState (state2.mNpcStats); + static_cast (customData.mNpcStats).writeState (state2.mCreatureStats); } const ESM::GameSetting *Npc::fMinWalkSpeed; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 845c3c07be..c9e8ad9551 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -591,6 +592,41 @@ namespace MWDialogue } } + int DialogueManager::countSavedGameRecords() const + { + return 1; // known topics + } + + void DialogueManager::write (ESM::ESMWriter& writer) const + { + ESM::DialogueState state; + + for (std::map::const_iterator iter (mKnownTopics.begin()); + iter!=mKnownTopics.end(); ++iter) + if (iter->second) + state.mKnownTopics.push_back (iter->first); + + writer.startRecord (ESM::REC_DIAS); + state.save (writer); + writer.endRecord (ESM::REC_DIAS); + } + + void DialogueManager::readRecord (ESM::ESMReader& reader, int32_t type) + { + if (type==ESM::REC_DIAS) + { + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + + ESM::DialogueState state; + state.load (reader); + + for (std::vector::const_iterator iter (state.mKnownTopics.begin()); + iter!=state.mKnownTopics.end(); ++iter) + if (store.get().search (*iter)) + mKnownTopics.insert (std::make_pair (*iter, true)); + } + } + std::vector ParseHyperText(const std::string& text) { diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index c32a5dbd89..cf8ea11764 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -78,6 +78,12 @@ namespace MWDialogue virtual void persuade (int type); virtual int getTemporaryDispositionChange () const; virtual void applyDispositionChange (int delta); + + virtual int countSavedGameRecords() const; + + virtual void write (ESM::ESMWriter& writer) const; + + virtual void readRecord (ESM::ESMReader& reader, int32_t type); }; diff --git a/apps/openmw/mwgui/journalviewmodel.cpp b/apps/openmw/mwgui/journalviewmodel.cpp index a0d67b0256..c6bd6d15d2 100644 --- a/apps/openmw/mwgui/journalviewmodel.cpp +++ b/apps/openmw/mwgui/journalviewmodel.cpp @@ -206,9 +206,9 @@ struct JournalViewModelImpl : JournalViewModel const MWDialogue::Quest& quest = i->second; // Unfortunately Morrowind.esm has no quest names, since the quest book was added with tribunal. - if (quest.getName().empty()) - visitor (reinterpret_cast (&i->second), toUtf8Span (i->first)); - else + // Note that even with Tribunal, some quests still don't have quest names. I'm assuming those are not supposed + // to appear in the quest book. + if (!quest.getName().empty()) visitor (reinterpret_cast (&i->second), toUtf8Span (quest.getName())); } } diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 7ed3007ffd..8bc6facea0 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -17,6 +17,7 @@ #include "../mwworld/player.hpp" #include "../mwworld/class.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/soundmanager.hpp" @@ -451,7 +452,7 @@ namespace MWInput mInputBinder->adjustMouseRegion(width, height); } - bool InputManager::keyPressed( const SDL_KeyboardEvent &arg ) + void InputManager::keyPressed( const SDL_KeyboardEvent &arg ) { // Cut, copy & paste MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); @@ -497,7 +498,6 @@ namespace MWInput if (kc != OIS::KC_UNASSIGNED) MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0); - return true; } void InputManager::textInput(const SDL_TextInputEvent &arg) @@ -508,23 +508,21 @@ namespace MWInput MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::None, *it); } - bool InputManager::keyReleased(const SDL_KeyboardEvent &arg ) + void InputManager::keyReleased(const SDL_KeyboardEvent &arg ) { mInputBinder->keyReleased (arg); OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc)); - - return true; } - bool InputManager::mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) + void InputManager::mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) { mInputBinder->mousePressed (arg, id); if (id != SDL_BUTTON_LEFT && id != SDL_BUTTON_RIGHT) - return true; // MyGUI has no use for these events + return; // MyGUI has no use for these events MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, sdlButtonToMyGUI(id)); if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) @@ -535,20 +533,16 @@ namespace MWInput MWBase::Environment::get().getSoundManager ()->playSound ("Menu Click", 1.f, 1.f); } } - - return true; } - bool InputManager::mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) + void InputManager::mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) { mInputBinder->mouseReleased (arg, id); MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, sdlButtonToMyGUI(id)); - - return true; } - bool InputManager::mouseMoved(const SFO::MouseMotionEvent &arg ) + void InputManager::mouseMoved(const SFO::MouseMotionEvent &arg ) { mInputBinder->mouseMoved (arg); @@ -596,8 +590,6 @@ namespace MWInput MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true); } } - - return true; } void InputManager::windowFocusChange(bool have_focus) @@ -647,7 +639,9 @@ namespace MWInput return; // Not allowed if no spell selected - if (MWBase::Environment::get().getWindowManager()->getSelectedSpell().empty()) + MWWorld::InventoryStore& inventory = MWWorld::Class::get(mPlayer->getPlayer()).getInventoryStore(mPlayer->getPlayer()); + if (MWBase::Environment::get().getWindowManager()->getSelectedSpell().empty() && + inventory.getSelectedEnchantItem() == inventory.end()) return; MWMechanics::DrawState_ state = mPlayer->getDrawState(); diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index d41b4c3f3b..bd3f4954b1 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -86,13 +86,13 @@ namespace MWInput virtual void resetToDefaultBindings(); public: - virtual bool keyPressed(const SDL_KeyboardEvent &arg ); - virtual bool keyReleased( const SDL_KeyboardEvent &arg ); + virtual void keyPressed(const SDL_KeyboardEvent &arg ); + virtual void keyReleased( const SDL_KeyboardEvent &arg ); virtual void textInput (const SDL_TextInputEvent &arg); - virtual bool mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ); - virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ); - virtual bool mouseMoved( const SFO::MouseMotionEvent &arg ); + virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ); + virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ); + virtual void mouseMoved( const SFO::MouseMotionEvent &arg ); virtual void windowVisibilityChange( bool visible ); virtual void windowFocusChange( bool have_focus ); diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 3653587f88..91c77702e2 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -13,7 +13,7 @@ #include "../mwbase/dialoguemanager.hpp" -#include "npcstats.hpp" +#include "creaturestats.hpp" #include "steering.hpp" #include "movement.hpp" #include "character.hpp" // fixme: for getActiveWeapon @@ -138,11 +138,11 @@ namespace MWMechanics { MWMechanics::DrawState_ state = actor.getClass().getCreatureStats(actor).getDrawState(); if (state == MWMechanics::DrawState_Spell || state == MWMechanics::DrawState_Nothing) - actor.getClass().getNpcStats(actor).setDrawState(MWMechanics::DrawState_Weapon); + actor.getClass().getCreatureStats(actor).setDrawState(MWMechanics::DrawState_Weapon); //Get weapon speed and range MWWorld::ContainerStoreIterator weaponSlot = - MWMechanics::getActiveWeapon(cls.getNpcStats(actor), cls.getInventoryStore(actor), &weaptype); + MWMechanics::getActiveWeapon(cls.getCreatureStats(actor), cls.getInventoryStore(actor), &weaptype); if (weaptype == WeapType_HandToHand) { const MWWorld::Store &gmst = @@ -240,16 +240,20 @@ namespace MWMechanics //target is at far distance: build path to target OR follow target (if previously actor had reached it once) mFollowTarget = false; - buildNewPath(actor); + buildNewPath(actor); //not guaranteed, check before use //delete visited path node mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]); - //try shortcut - if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) - mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); - else - mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + //if buildNewPath() failed leave mTargetAngle unchanged + if(!mPathFinder.getPath().empty()) + { + //try shortcut + if(vDir.length() < mPathFinder.getDistToNext(pos.pos[0],pos.pos[1],pos.pos[2]) && MWBase::Environment::get().getWorld()->getLOS(actor, mTarget)) + mTargetAngle = Ogre::Radian( Ogre::Math::ACos(vDir.y / vDir.length()) * sgn(Ogre::Math::ASin(vDir.x / vDir.length())) ).valueDegrees(); + else + mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]); + } mRotate = true; mMovement.mPosition[1] = 1; @@ -300,9 +304,13 @@ namespace MWMechanics dest.mZ = mTarget.getRefData().getPosition().pos[2]; Ogre::Vector3 newPathTarget = Ogre::Vector3(dest.mX, dest.mY, dest.mZ); - ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); - Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); - float dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); + float dist = -1; //hack to indicate first time, to construct a new path + if(!mPathFinder.getPath().empty()) + { + ESM::Pathgrid::Point lastPt = mPathFinder.getPath().back(); + Ogre::Vector3 currPathTarget(lastPt.mX, lastPt.mY, lastPt.mZ); + dist = Ogre::Math::Abs((newPathTarget - currPathTarget).length()); + } float targetPosThreshold; bool isOutside = actor.getCell()->mCell->isExterior(); @@ -311,7 +319,7 @@ namespace MWMechanics else targetPosThreshold = 100; - if(dist > targetPosThreshold) + if((dist < 0) || (dist > targetPosThreshold)) { //construct new path only if target has moved away more than on ESM::Position pos = actor.getRefData().getPosition(); @@ -332,8 +340,11 @@ namespace MWMechanics //maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path, //not the actual path length. Here we should know if the new path is actually more effective. //if(pathFinder2.getPathSize() < mPathFinder.getPathSize()) - newPathFinder.syncStart(mPathFinder.getPath()); - mPathFinder = newPathFinder; + if(!mPathFinder.getPath().empty()) + { + newPathFinder.syncStart(mPathFinder.getPath()); + mPathFinder = newPathFinder; + } } } } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 8f890befba..d61b967390 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -2,6 +2,8 @@ #include +#include + #include "../mwworld/esmstore.hpp" #include "../mwbase/environment.hpp" @@ -457,4 +459,21 @@ namespace MWMechanics mAttackStrength = value; } + void CreatureStats::writeState (ESM::CreatureStats& state) const + { + for (int i=0; i<8; ++i) + mAttributes[i].writeState (state.mAttributes[i]); + + for (int i=0; i<3; ++i) + mDynamic[i].writeState (state.mDynamic[i]); + } + + void CreatureStats::readState (const ESM::CreatureStats& state) + { + for (int i=0; i<8; ++i) + mAttributes[i].readState (state.mAttributes[i]); + + for (int i=0; i<3; ++i) + mDynamic[i].readState (state.mDynamic[i]); + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index bb95833015..94e506fc4f 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -12,6 +12,11 @@ #include "aisequence.hpp" #include "drawstate.hpp" +namespace ESM +{ + struct CreatureStats; +} + namespace MWMechanics { /// \brief Common creature stats @@ -212,6 +217,10 @@ namespace MWMechanics std::set mBoundItems; // Same as above std::map mSummonedCreatures; + + void writeState (ESM::CreatureStats& state) const; + + void readState (const ESM::CreatureStats& state); }; } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 63b4467f6d..8918bfbe70 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "../mwworld/class.hpp" #include "../mwworld/esmstore.hpp" @@ -423,3 +424,89 @@ void MWMechanics::NpcStats::setTimeToStartDrowning(float time) assert(time>=0 && time<=20); mTimeToStartDrowning=time; } + +void MWMechanics::NpcStats::writeState (ESM::NpcStats& state) const +{ + for (std::map::const_iterator iter (mFactionRank.begin()); + iter!=mFactionRank.end(); ++iter) + state.mFactions[iter->first].mRank = iter->second; + + state.mDisposition = mDisposition; + + for (int i=0; i<27; ++i) + { + mSkill[i].writeState (state.mSkills[i].mRegular); + mWerewolfSkill[i].writeState (state.mSkills[i].mWerewolf); + } + + state.mBounty = mBounty; + + for (std::set::const_iterator iter (mExpelled.begin()); + iter!=mExpelled.end(); ++iter) + state.mFactions[*iter].mExpelled = true; + + for (std::map::const_iterator iter (mFactionReputation.begin()); + iter!=mFactionReputation.end(); ++iter) + state.mFactions[iter->first].mReputation = iter->second; + + state.mReputation = mReputation; + state.mWerewolfKills = mWerewolfKills; + state.mProfit = mProfit; + state.mAttackStrength = mAttackStrength; + state.mLevelProgress = mLevelProgress; + + for (int i=0; i<8; ++i) + state.mSkillIncrease[i] = mSkillIncreases[i]; + + std::copy (mUsedIds.begin(), mUsedIds.end(), std::back_inserter (state.mUsedIds)); + + state.mTimeToStartDrowning = mTimeToStartDrowning; + state.mLastDrowningHit = mLastDrowningHit; + state.mLevelHealthBonus = mLevelHealthBonus; +} + +void MWMechanics::NpcStats::readState (const ESM::NpcStats& state) +{ + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + + for (std::map::const_iterator iter (state.mFactions.begin()); + iter!=state.mFactions.end(); ++iter) + if (store.get().search (iter->first)) + { + if (iter->second.mExpelled) + mExpelled.insert (iter->first); + + if (iter->second.mRank) + mFactionRank.insert (std::make_pair (iter->first, iter->second.mRank)); + + if (iter->second.mReputation) + mFactionReputation.insert (std::make_pair (iter->first, iter->second.mReputation)); + } + + mDisposition = state.mDisposition; + + for (int i=0; i<27; ++i) + { + mSkill[i].readState (state.mSkills[i].mRegular); + mWerewolfSkill[i].readState (state.mSkills[i].mWerewolf); + } + + mBounty = state.mBounty; + mReputation = state.mReputation; + mWerewolfKills = state.mWerewolfKills; + mProfit = state.mProfit; + mAttackStrength = state.mAttackStrength; + mLevelProgress = state.mLevelProgress; + + for (int i=0; i<8; ++i) + mSkillIncreases[i] = state.mSkillIncrease[i]; + + for (std::vector::const_iterator iter (state.mUsedIds.begin()); + iter!=state.mUsedIds.end(); ++iter) + if (store.find (*iter)) + mUsedIds.insert (*iter); + + mTimeToStartDrowning = state.mTimeToStartDrowning; + mLastDrowningHit = state.mLastDrowningHit; + mLevelHealthBonus = state.mLevelHealthBonus; +} \ No newline at end of file diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index d7db999e45..ad493be3c1 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -13,6 +13,7 @@ namespace ESM { struct Class; + struct NpcStats; } namespace MWMechanics @@ -128,6 +129,10 @@ namespace MWMechanics /// Sets time left for the creature to drown if it stays underwater. /// @param time value from [0,20] void setTimeToStartDrowning(float time); + + void writeState (ESM::NpcStats& state) const; + + void readState (const ESM::NpcStats& state); }; } diff --git a/apps/openmw/mwmechanics/pathfinding.cpp b/apps/openmw/mwmechanics/pathfinding.cpp index 4407363a61..5996eb0793 100644 --- a/apps/openmw/mwmechanics/pathfinding.cpp +++ b/apps/openmw/mwmechanics/pathfinding.cpp @@ -391,6 +391,8 @@ namespace MWMechanics void PathFinder::syncStart(const std::list &path) { + if (path.size() < 2) + return; //nothing to pop std::list::const_iterator oldStart = path.begin(); std::list::iterator iter = ++mPath.begin(); diff --git a/apps/openmw/mwmechanics/stat.cpp b/apps/openmw/mwmechanics/stat.cpp new file mode 100644 index 0000000000..61b6d60ad4 --- /dev/null +++ b/apps/openmw/mwmechanics/stat.cpp @@ -0,0 +1,29 @@ + +#include "stat.hpp" + +void MWMechanics::AttributeValue::writeState (ESM::StatState& state) const +{ + state.mBase = mBase; + state.mMod = mModifier; + state.mDamage = mDamage; +} + +void MWMechanics::AttributeValue::readState (const ESM::StatState& state) +{ + mBase = state.mBase; + mModifier = state.mMod; + mDamage = state.mDamage; +} + + +void MWMechanics::SkillValue::writeState (ESM::StatState& state) const +{ + AttributeValue::writeState (state); + state.mProgress = mProgress; +} + +void MWMechanics::SkillValue::readState (const ESM::StatState& state) +{ + AttributeValue::readState (state); + mProgress = state.mProgress; +} \ No newline at end of file diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 75ac6939aa..0fb4c57328 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -6,6 +6,8 @@ #include +#include + namespace MWMechanics { template @@ -86,6 +88,18 @@ namespace MWMechanics { mModified = mBase + modifier; } + + void writeState (ESM::StatState& state) const + { + state.mBase = mBase; + state.mMod = mModified; + } + + void readState (const ESM::StatState& state) + { + mBase = state.mBase; + mModified = state.mMod; + } }; template @@ -190,6 +204,18 @@ namespace MWMechanics mStatic.setModifier (modifier); setCurrent (getCurrent()+diff); } + + void writeState (ESM::StatState& state) const + { + mStatic.writeState (state); + state.mCurrent = mCurrent; + } + + void readState (const ESM::StatState& state) + { + mStatic.readState (state); + mCurrent = state.mCurrent; + } }; template @@ -225,6 +251,10 @@ namespace MWMechanics void damage(int damage) { mDamage += damage; } void restore(int amount) { mDamage -= std::min(mDamage, amount); } int getDamage() const { return mDamage; } + + void writeState (ESM::StatState& state) const; + + void readState (const ESM::StatState& state); }; class SkillValue : public AttributeValue @@ -234,6 +264,10 @@ namespace MWMechanics SkillValue() : mProgress(0) {} float getProgress() const { return mProgress; } void setProgress(float progress) { mProgress = progress; } + + void writeState (ESM::StatState& state) const; + + void readState (const ESM::StatState& state); }; inline bool operator== (const AttributeValue& left, const AttributeValue& right) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 44bba90d07..b3aa0cd858 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -285,6 +285,17 @@ void Animation::addAnimSource(const std::string &model) } } + if (grp == 0 && dstval->getNode()->getName() == "Bip01") + { + mNonAccumRoot = dstval->getNode(); + mAccumRoot = mNonAccumRoot->getParent(); + if(!mAccumRoot) + { + std::cerr<< "Non-Accum root for "<getStore(); @@ -39,8 +39,6 @@ namespace MWRender // since grid coords are at cell origin, we need to add 1 cell maxX += 1; maxY += 1; - - return Ogre::AxisAlignedBox(minX, minY, 0, maxX, maxY, 0); } ESM::Land* TerrainStorage::getLand(int cellX, int cellY) diff --git a/apps/openmw/mwrender/terrainstorage.hpp b/apps/openmw/mwrender/terrainstorage.hpp index 5c20359527..2ef014aaf1 100644 --- a/apps/openmw/mwrender/terrainstorage.hpp +++ b/apps/openmw/mwrender/terrainstorage.hpp @@ -1,6 +1,9 @@ #ifndef MWRENDER_TERRAINSTORAGE_H #define MWRENDER_TERRAINSTORAGE_H +#include +#include + #include namespace MWRender @@ -14,7 +17,7 @@ namespace MWRender public: /// Get bounds of the whole terrain in cell units - virtual Ogre::AxisAlignedBox getBounds(); + virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY); /// Get the minimum and maximum heights of a terrain chunk. /// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree. diff --git a/apps/openmw/mwscript/compilercontext.cpp b/apps/openmw/mwscript/compilercontext.cpp index 7e63a33b25..1b3e769bf1 100644 --- a/apps/openmw/mwscript/compilercontext.cpp +++ b/apps/openmw/mwscript/compilercontext.cpp @@ -3,6 +3,8 @@ #include "../mwworld/esmstore.hpp" +#include + #include #include "../mwbase/environment.hpp" @@ -28,16 +30,32 @@ namespace MWScript return MWBase::Environment::get().getWorld()->getGlobalVariableType (name); } - char CompilerContext::getMemberType (const std::string& name, const std::string& id) const + std::pair CompilerContext::getMemberType (const std::string& name, + const std::string& id) const { - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr (id, false); + std::string script; + bool reference = false; - std::string script = MWWorld::Class::get (ptr).getScript (ptr); + if (const ESM::Script *scriptRecord = + MWBase::Environment::get().getWorld()->getStore().get().search (id)) + { + script = scriptRecord->mId; + } + else + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPtr (id, false); - if (script.empty()) - return ' '; + script = MWWorld::Class::get (ptr).getScript (ptr); + reference = true; + } - return MWBase::Environment::get().getScriptManager()->getLocals (script).getType (name); + char type = ' '; + + if (!script.empty()) + type = MWBase::Environment::get().getScriptManager()->getLocals (script).getType ( + Misc::StringUtils::lowerCase (name)); + + return std::make_pair (type, reference); } bool CompilerContext::isId (const std::string& name) const @@ -67,4 +85,14 @@ namespace MWScript store.get().search (name) || store.get().search (name); } + + bool CompilerContext::isJournalId (const std::string& name) const + { + const MWWorld::ESMStore &store = + MWBase::Environment::get().getWorld()->getStore(); + + const ESM::Dialogue *topic = store.get().search (name); + + return topic && topic->mType==ESM::Dialogue::Journal; + } } diff --git a/apps/openmw/mwscript/compilercontext.hpp b/apps/openmw/mwscript/compilercontext.hpp index 5ec98e09a7..95719ab692 100644 --- a/apps/openmw/mwscript/compilercontext.hpp +++ b/apps/openmw/mwscript/compilercontext.hpp @@ -30,11 +30,18 @@ namespace MWScript /// 'l: long, 's': short, 'f': float, ' ': does not exist. virtual char getGlobalType (const std::string& name) const; - virtual char getMemberType (const std::string& name, const std::string& id) const; - ///< 'l: long, 's': short, 'f': float, ' ': does not exist. + virtual std::pair getMemberType (const std::string& name, + const std::string& id) const; + ///< Return type of member variable \a name in script \a id or in script of reference of + /// \a id + /// \return first: 'l: long, 's': short, 'f': float, ' ': does not exist. + /// second: true: script of reference virtual bool isId (const std::string& name) const; ///< Does \a name match an ID, that can be referenced? + + virtual bool isJournalId (const std::string& name) const; + ///< Does \a name match a journal ID? }; } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 25f3c38aa7..70186a089b 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -52,7 +52,9 @@ op 0x20023: AiFollow, explicit reference op 0x20024: AiFollowCell op 0x20025: AiFollowCell, explicit reference op 0x20026: ModRegion -opcodes 0x20027-0x3ffff unused +op 0x20027: RemoveSoulGem +op 0x20028: RemoveSoulGem, explicit reference +opcodes 0x20029-0x3ffff unused Segment 4: (not implemented yet) @@ -308,8 +310,8 @@ op 0x20001f1: GetDetected op 0x20001f2: GetDetected, explicit reference op 0x20001f3: AddSoulGem op 0x20001f4: AddSoulGem, explicit reference -op 0x20001f5: RemoveSoulGem -op 0x20001f6: RemoveSoulGem, explicit reference +op 0x20001f5: unused +op 0x20001f6: unused op 0x20001f7: PlayBink op 0x20001f8: Drop op 0x20001f9: Drop, explicit reference @@ -381,5 +383,7 @@ op 0x200023a: StartCombat op 0x200023b: StartCombatExplicit op 0x200023c: StopCombat op 0x200023d: StopCombatExplicit +op 0x200023e: GetPcInJail +op 0x200023f: GetPcTraveling -opcodes 0x200023e-0x3ffffff unused +opcodes 0x2000240-0x3ffffff unused diff --git a/apps/openmw/mwscript/globalscripts.cpp b/apps/openmw/mwscript/globalscripts.cpp index 8f269a0153..179e2bb0bb 100644 --- a/apps/openmw/mwscript/globalscripts.cpp +++ b/apps/openmw/mwscript/globalscripts.cpp @@ -148,4 +148,25 @@ namespace MWScript return false; } + + Locals& GlobalScripts::getLocals (const std::string& name) + { + std::string name2 = Misc::StringUtils::lowerCase (name); + std::map >::iterator iter = + mScripts.find (name2); + + if (iter==mScripts.end()) + { + if (const ESM::Script *script = mStore.get().find (name)) + { + Locals locals; + + locals.configure (*script); + + iter = mScripts.insert (std::make_pair (name, std::make_pair (false, locals))).first; + } + } + + return iter->second.second; + } } diff --git a/apps/openmw/mwscript/globalscripts.hpp b/apps/openmw/mwscript/globalscripts.hpp index cf716c8e42..a4a7662263 100644 --- a/apps/openmw/mwscript/globalscripts.hpp +++ b/apps/openmw/mwscript/globalscripts.hpp @@ -52,6 +52,10 @@ namespace MWScript ///< Records for variables that do not exist are dropped silently. /// /// \return Known type? + + Locals& getLocals (const std::string& name); + ///< If the script \a name has not been added as a global script yet, it is added + /// automatically, but is not set to running state. }; } diff --git a/apps/openmw/mwscript/interpretercontext.cpp b/apps/openmw/mwscript/interpretercontext.cpp index 10e98e3988..b79808d08b 100644 --- a/apps/openmw/mwscript/interpretercontext.cpp +++ b/apps/openmw/mwscript/interpretercontext.cpp @@ -54,6 +54,47 @@ namespace MWScript } } + const Locals& InterpreterContext::getMemberLocals (std::string& id, bool global) + const + { + if (global) + { + return MWBase::Environment::get().getScriptManager()->getGlobalScripts(). + getLocals (id); + } + else + { + const MWWorld::Ptr ptr = getReference (id, false); + + id = MWWorld::Class::get (ptr).getScript (ptr); + + ptr.getRefData().setLocals ( + *MWBase::Environment::get().getWorld()->getStore().get().find (id)); + + return ptr.getRefData().getLocals(); + } + } + + Locals& InterpreterContext::getMemberLocals (std::string& id, bool global) + { + if (global) + { + return MWBase::Environment::get().getScriptManager()->getGlobalScripts(). + getLocals (id); + } + else + { + const MWWorld::Ptr ptr = getReference (id, false); + + id = MWWorld::Class::get (ptr).getScript (ptr); + + ptr.getRefData().setLocals ( + *MWBase::Environment::get().getWorld()->getStore().get().find (id)); + + return ptr.getRefData().getLocals(); + } + } + InterpreterContext::InterpreterContext ( MWScript::Locals *locals, MWWorld::Ptr reference) : mLocals (locals), mReference (reference), @@ -407,82 +448,80 @@ namespace MWScript MWBase::Environment::get().getWorld()->disable (ref); } - int InterpreterContext::getMemberShort (const std::string& id, const std::string& name) const + int InterpreterContext::getMemberShort (const std::string& id, const std::string& name, + bool global) const { - const MWWorld::Ptr ptr = getReference (id, false); + std::string scriptId (id); - std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr); + const Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 's'); + int index = MWBase::Environment::get().getScriptManager()->getLocalIndex ( + scriptId, name, 's'); - ptr.getRefData().setLocals ( - *MWBase::Environment::get().getWorld()->getStore().get().find (scriptId)); - return ptr.getRefData().getLocals().mShorts[index]; + return locals.mShorts[index]; } - int InterpreterContext::getMemberLong (const std::string& id, const std::string& name) const + int InterpreterContext::getMemberLong (const std::string& id, const std::string& name, + bool global) const { - const MWWorld::Ptr ptr = getReference (id, false); + std::string scriptId (id); - std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr); + const Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'l'); + int index = MWBase::Environment::get().getScriptManager()->getLocalIndex ( + scriptId, name, 'l'); - ptr.getRefData().setLocals ( - *MWBase::Environment::get().getWorld()->getStore().get().find (scriptId)); - return ptr.getRefData().getLocals().mLongs[index]; + return locals.mLongs[index]; } - float InterpreterContext::getMemberFloat (const std::string& id, const std::string& name) const + float InterpreterContext::getMemberFloat (const std::string& id, const std::string& name, + bool global) const { - const MWWorld::Ptr ptr = getReference (id, false); + std::string scriptId (id); - std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr); + const Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'f'); + int index = MWBase::Environment::get().getScriptManager()->getLocalIndex ( + scriptId, name, 'f'); - ptr.getRefData().setLocals ( - *MWBase::Environment::get().getWorld()->getStore().get().find (scriptId)); - return ptr.getRefData().getLocals().mFloats[index]; + return locals.mFloats[index]; } - void InterpreterContext::setMemberShort (const std::string& id, const std::string& name, int value) + void InterpreterContext::setMemberShort (const std::string& id, const std::string& name, + int value, bool global) { - const MWWorld::Ptr ptr = getReference (id, false); + std::string scriptId (id); - std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr); + Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 's'); + int index = + MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 's'); - ptr.getRefData().setLocals ( - *MWBase::Environment::get().getWorld()->getStore().get().find (scriptId)); - ptr.getRefData().getLocals().mShorts[index] = value; + locals.mShorts[index] = value; } - void InterpreterContext::setMemberLong (const std::string& id, const std::string& name, int value) + void InterpreterContext::setMemberLong (const std::string& id, const std::string& name, int value, bool global) { - const MWWorld::Ptr ptr = getReference (id, false); + std::string scriptId (id); - std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr); + Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'l'); + int index = + MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'l'); - ptr.getRefData().setLocals ( - *MWBase::Environment::get().getWorld()->getStore().get().find (scriptId)); - ptr.getRefData().getLocals().mLongs[index] = value; + locals.mLongs[index] = value; } - void InterpreterContext::setMemberFloat (const std::string& id, const std::string& name, float value) + void InterpreterContext::setMemberFloat (const std::string& id, const std::string& name, float value, bool global) { - const MWWorld::Ptr ptr = getReference (id, false); + std::string scriptId (id); - std::string scriptId = MWWorld::Class::get (ptr).getScript (ptr); + Locals& locals = getMemberLocals (scriptId, global); - int index = MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'f'); + int index = + MWBase::Environment::get().getScriptManager()->getLocalIndex (scriptId, name, 'f'); - ptr.getRefData().setLocals ( - *MWBase::Environment::get().getWorld()->getStore().get().find (scriptId)); - ptr.getRefData().getLocals().mFloats[index] = value; + locals.mFloats[index] = value; } MWWorld::Ptr InterpreterContext::getReference(bool required) diff --git a/apps/openmw/mwscript/interpretercontext.hpp b/apps/openmw/mwscript/interpretercontext.hpp index 04546faed7..9fb7fa2bf8 100644 --- a/apps/openmw/mwscript/interpretercontext.hpp +++ b/apps/openmw/mwscript/interpretercontext.hpp @@ -37,6 +37,12 @@ namespace MWScript const MWWorld::Ptr getReference (const std::string& id, bool activeOnly, bool doThrow=true) const; + const Locals& getMemberLocals (std::string& id, bool global) const; + ///< \a id is changed to the respective script ID, if \a id wasn't a script ID before + + Locals& getMemberLocals (std::string& id, bool global); + ///< \a id is changed to the respective script ID, if \a id wasn't a script ID before + public: InterpreterContext (MWScript::Locals *locals, MWWorld::Ptr reference); @@ -75,35 +81,35 @@ namespace MWScript virtual void setGlobalLong (const std::string& name, int value); virtual void setGlobalFloat (const std::string& name, float value); - + virtual std::vector getGlobals () const; virtual char getGlobalType (const std::string& name) const; - + virtual std::string getActionBinding(const std::string& action) const; - + virtual std::string getNPCName() const; - + virtual std::string getNPCRace() const; - + virtual std::string getNPCClass() const; - + virtual std::string getNPCFaction() const; virtual std::string getNPCRank() const; - + virtual std::string getPCName() const; - + virtual std::string getPCRace() const; - + virtual std::string getPCClass() const; - + virtual std::string getPCRank() const; - + virtual std::string getPCNextRank() const; - + virtual int getPCBounty() const; - + virtual std::string getCurrentCellName() const; virtual bool isScriptRunning (const std::string& name) const; @@ -138,17 +144,17 @@ namespace MWScript virtual void disable (const std::string& id = ""); - virtual int getMemberShort (const std::string& id, const std::string& name) const; + virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const; - virtual int getMemberLong (const std::string& id, const std::string& name) const; + virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const; - virtual float getMemberFloat (const std::string& id, const std::string& name) const; + virtual float getMemberFloat (const std::string& id, const std::string& name, bool global) const; - virtual void setMemberShort (const std::string& id, const std::string& name, int value); + virtual void setMemberShort (const std::string& id, const std::string& name, int value, bool global); - virtual void setMemberLong (const std::string& id, const std::string& name, int value); + virtual void setMemberLong (const std::string& id, const std::string& name, int value, bool global); - virtual void setMemberFloat (const std::string& id, const std::string& name, float value); + virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global); MWWorld::Ptr getReference(bool required=true); ///< Reference, that the script is running from (can be empty) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index d117c8dede..0c60e1c940 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -365,17 +365,21 @@ namespace MWScript }; template - class OpRemoveSoulGem : public Interpreter::Opcode0 + class OpRemoveSoulGem : public Interpreter::Opcode1 { public: - virtual void execute (Interpreter::Runtime& runtime) + virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0) { MWWorld::Ptr ptr = R()(runtime); std::string soul = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); + // throw away additional arguments + for (unsigned int i=0; i); interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGem, new OpAddSoulGem); interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGemExplicit, new OpAddSoulGem); - interpreter.installSegment5 (Compiler::Misc::opcodeRemoveSoulGem, new OpRemoveSoulGem); - interpreter.installSegment5 (Compiler::Misc::opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem); + interpreter.installSegment3 (Compiler::Misc::opcodeRemoveSoulGem, new OpRemoveSoulGem); + interpreter.installSegment3 (Compiler::Misc::opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem); interpreter.installSegment5 (Compiler::Misc::opcodeDrop, new OpDrop); interpreter.installSegment5 (Compiler::Misc::opcodeDropExplicit, new OpDrop); interpreter.installSegment5 (Compiler::Misc::opcodeDropSoulGem, new OpDropSoulGem); @@ -888,6 +914,8 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeCastExplicit, new OpCast); interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpell, new OpExplodeSpell); interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpellExplicit, new OpExplodeSpell); + interpreter.installSegment5 (Compiler::Misc::opcodeGetPcInJail, new OpGetPcInJail); + interpreter.installSegment5 (Compiler::Misc::opcodeGetPcTraveling, new OpGetPcTraveling); } } } diff --git a/apps/openmw/mwscript/scriptmanagerimp.cpp b/apps/openmw/mwscript/scriptmanagerimp.cpp index a3e0615467..74c85dbbf7 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.cpp +++ b/apps/openmw/mwscript/scriptmanagerimp.cpp @@ -7,22 +7,28 @@ #include #include -#include "../mwworld/esmstore.hpp" + +#include #include #include #include +#include + +#include "../mwworld/esmstore.hpp" #include "extensions.hpp" namespace MWScript { ScriptManager::ScriptManager (const MWWorld::ESMStore& store, bool verbose, - Compiler::Context& compilerContext) + Compiler::Context& compilerContext, int warningsMode) : mErrorHandler (std::cerr), mStore (store), mVerbose (verbose), mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext), mOpcodesInstalled (false), mGlobalScripts (store) - {} + { + mErrorHandler.setWarningsMode (warningsMode); + } bool ScriptManager::compile (const std::string& name) { @@ -138,37 +144,33 @@ namespace MWScript Compiler::Locals& ScriptManager::getLocals (const std::string& name) { + std::string name2 = Misc::StringUtils::lowerCase (name); + { - ScriptCollection::iterator iter = mScripts.find (name); + ScriptCollection::iterator iter = mScripts.find (name2); if (iter!=mScripts.end()) return iter->second.second; } { - std::map::iterator iter = mOtherLocals.find (name); + std::map::iterator iter = mOtherLocals.find (name2); if (iter!=mOtherLocals.end()) return iter->second; } - Compiler::Locals locals; - - if (const ESM::Script *script = mStore.get().find (name)) + if (const ESM::Script *script = mStore.get().find (name2)) { - int index = 0; + Compiler::Locals locals; - for (int i=0; imData.mNumShorts; ++i) - locals.declare ('s', script->mVarNames[index++]); - - for (int i=0; imData.mNumLongs; ++i) - locals.declare ('l', script->mVarNames[index++]); - - for (int i=0; imData.mNumFloats; ++i) - locals.declare ('f', script->mVarNames[index++]); + std::istringstream stream (script->mScriptText); + Compiler::QuickFileParser parser (mErrorHandler, mCompilerContext, locals); + Compiler::Scanner scanner (mErrorHandler, stream, mCompilerContext.getExtensions()); + scanner.scan (parser); std::map::iterator iter = - mOtherLocals.insert (std::make_pair (name, locals)).first; + mOtherLocals.insert (std::make_pair (name2, locals)).first; return iter->second; } @@ -214,8 +216,10 @@ namespace MWScript throw std::runtime_error ("invalid variable type"); } + std::string variable2 = Misc::StringUtils::lowerCase (variable); + for (int i=0; imVarNames.at (i+offset)==variable) + if (Misc::StringUtils::lowerCase (script->mVarNames.at (i+offset))==variable2) return i; throw std::runtime_error ("unable to access local variable " + variable + " of " + scriptId); diff --git a/apps/openmw/mwscript/scriptmanagerimp.hpp b/apps/openmw/mwscript/scriptmanagerimp.hpp index 1a856e0c58..da3abc60b5 100644 --- a/apps/openmw/mwscript/scriptmanagerimp.hpp +++ b/apps/openmw/mwscript/scriptmanagerimp.hpp @@ -52,7 +52,7 @@ namespace MWScript public: ScriptManager (const MWWorld::ESMStore& store, bool verbose, - Compiler::Context& compilerContext); + Compiler::Context& compilerContext, int warningsMode); virtual void run (const std::string& name, Interpreter::Context& interpreterContext); ///< Run the script with the given name (compile first, if not compiled yet) diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 9dc0b8c5db..7563ad0150 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -172,6 +172,7 @@ class OpenAL_SoundStream : public Sound DecoderPtr mDecoder; volatile bool mIsFinished; + volatile bool mIsInitialBatchEnqueued; void updateAll(bool local); @@ -264,7 +265,7 @@ private: OpenAL_SoundStream::OpenAL_SoundStream(OpenAL_Output &output, ALuint src, DecoderPtr decoder, float basevol, float pitch, int flags) : Sound(Ogre::Vector3(0.0f), 1.0f, basevol, pitch, 1.0f, 1000.0f, flags) - , mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true) + , mOutput(output), mSource(src), mSamplesQueued(0), mDecoder(decoder), mIsFinished(true), mIsInitialBatchEnqueued(false) { throwALerror(); @@ -315,16 +316,8 @@ void OpenAL_SoundStream::play() alSourcei(mSource, AL_BUFFER, 0); throwALerror(); mSamplesQueued = 0; - - for(ALuint i = 0;i < sNumBuffers;i++) - alBufferData(mBuffers[i], mFormat, this, 0, mSampleRate); - throwALerror(); - - alSourceQueueBuffers(mSource, sNumBuffers, mBuffers); - alSourcePlay(mSource); - throwALerror(); - mIsFinished = false; + mIsInitialBatchEnqueued = false; mOutput.mStreamThread->add(this); } @@ -332,6 +325,7 @@ void OpenAL_SoundStream::stop() { mOutput.mStreamThread->remove(this); mIsFinished = true; + mIsInitialBatchEnqueued = false; alSourceStop(mSource); alSourcei(mSource, AL_BUFFER, 0); @@ -444,6 +438,24 @@ bool OpenAL_SoundStream::process() } while(processed > 0); throwALerror(); } + else if (!mIsInitialBatchEnqueued) { // nothing enqueued yet + std::vector data(mBufferSize); + + for(ALuint i = 0;i < sNumBuffers && !finished;i++) + { + size_t got = mDecoder->read(&data[0], data.size()); + finished = (got < data.size()); + if(got > 0) + { + ALuint bufid = mBuffers[i]; + alBufferData(bufid, mFormat, &data[0], got, mSampleRate); + alSourceQueueBuffers(mSource, 1, &bufid); + throwALerror(); + mSamplesQueued += getBufferSampleCount(bufid); + } + } + mIsInitialBatchEnqueued = true; + } if(state != AL_PLAYING && state != AL_PAUSED) { @@ -461,6 +473,7 @@ bool OpenAL_SoundStream::process() std::cout<< "Error updating stream \""<getName()<<"\"" <updateSlot (slot, profile); - std::ofstream stream (slot->mPath.string().c_str()); + std::ofstream stream (slot->mPath.string().c_str(), std::ios::binary); ESM::ESMWriter writer; @@ -193,7 +193,8 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot +MWBase::Environment::get().getJournal()->countSavedGameRecords() +MWBase::Environment::get().getWorld()->countSavedGameRecords() +MWBase::Environment::get().getScriptManager()->getGlobalScripts().countSavedGameRecords() - + 1 // global map + +MWBase::Environment::get().getDialogueManager()->countSavedGameRecords() + +1 // global map ); writer.save (stream); @@ -203,6 +204,7 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot writer.endRecord (ESM::REC_SAVE); MWBase::Environment::get().getJournal()->write (writer); + MWBase::Environment::get().getDialogueManager()->write (writer); MWBase::Environment::get().getWorld()->write (writer); MWBase::Environment::get().getScriptManager()->getGlobalScripts().write (writer); MWBase::Environment::get().getWindowManager()->write(writer); @@ -245,6 +247,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl MWBase::Environment::get().getJournal()->readRecord (reader, n.val); break; + case ESM::REC_DIAS: + + MWBase::Environment::get().getDialogueManager()->readRecord (reader, n.val); + break; + case ESM::REC_ALCH: case ESM::REC_ARMO: case ESM::REC_BOOK: diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4e240195ad..be6c0b338f 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -529,12 +529,6 @@ namespace MWWorld return mPlayer->getPlayer(); } - Ptr ptr = Class::get (mPlayer->getPlayer()). - getContainerStore (mPlayer->getPlayer()).search (name); - - if (!ptr.isEmpty()) - return ptr; - std::string lowerCaseName = Misc::StringUtils::lowerCase(name); // active cells @@ -548,6 +542,12 @@ namespace MWWorld return ptr; } + Ptr ptr = Class::get (mPlayer->getPlayer()). + getContainerStore (mPlayer->getPlayer()).search (lowerCaseName); + + if (!ptr.isEmpty()) + return ptr; + if (!activeOnly) { ret = mCells.getPtr (lowerCaseName); @@ -610,6 +610,10 @@ namespace MWWorld void World::enable (const Ptr& reference) { + // enable is a no-op for items in containers + if (!reference.isInCell()) + return; + if (!reference.getRefData().isEnabled()) { reference.getRefData().enable(); @@ -640,6 +644,10 @@ namespace MWWorld void World::disable (const Ptr& reference) { + // disable is a no-op for items in containers + if (!reference.isInCell()) + return; + if (reference.getRefData().isEnabled()) { reference.getRefData().disable(); diff --git a/cmake/FindSDL.cmake b/cmake/FindSDL.cmake deleted file mode 100644 index 0dc02f5b60..0000000000 --- a/cmake/FindSDL.cmake +++ /dev/null @@ -1,177 +0,0 @@ -# Locate SDL library -# This module defines -# SDL_LIBRARY, the name of the library to link against -# SDL_FOUND, if false, do not try to link to SDL -# SDL_INCLUDE_DIR, where to find SDL.h -# -# This module responds to the the flag: -# SDL_BUILDING_LIBRARY -# If this is defined, then no SDL_main will be linked in because -# only applications need main(). -# Otherwise, it is assumed you are building an application and this -# module will attempt to locate and set the the proper link flags -# as part of the returned SDL_LIBRARY variable. -# -# Don't forget to include SDLmain.h and SDLmain.m your project for the -# OS X framework based version. (Other versions link to -lSDLmain which -# this module will try to find on your behalf.) Also for OS X, this -# module will automatically add the -framework Cocoa on your behalf. -# -# -# Additional Note: If you see an empty SDL_LIBRARY_TEMP in your configuration -# and no SDL_LIBRARY, it means CMake did not find your SDL library -# (SDL.dll, libsdl.so, SDL.framework, etc). -# Set SDL_LIBRARY_TEMP to point to your SDL library, and configure again. -# Similarly, if you see an empty SDLMAIN_LIBRARY, you should set this value -# as appropriate. These values are used to generate the final SDL_LIBRARY -# variable, but when these values are unset, SDL_LIBRARY does not get created. -# -# -# $SDLDIR is an environment variable that would -# correspond to the ./configure --prefix=$SDLDIR -# used in building SDL. -# l.e.galup 9-20-02 -# -# Modified by Eric Wing. -# Added code to assist with automated building by using environmental variables -# and providing a more controlled/consistent search behavior. -# Added new modifications to recognize OS X frameworks and -# additional Unix paths (FreeBSD, etc). -# Also corrected the header search path to follow "proper" SDL guidelines. -# Added a search for SDLmain which is needed by some platforms. -# Added a search for threads which is needed by some platforms. -# Added needed compile switches for MinGW. -# -# On OSX, this will prefer the Framework version (if found) over others. -# People will have to manually change the cache values of -# SDL_LIBRARY to override this selection or set the CMake environment -# CMAKE_INCLUDE_PATH to modify the search paths. -# -# Note that the header path has changed from SDL/SDL.h to just SDL.h -# This needed to change because "proper" SDL convention -# is #include "SDL.h", not . This is done for portability -# reasons because not all systems place things in SDL/ (see FreeBSD). - -#============================================================================= -# Copyright 2003-2009 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -FIND_PATH(SDL_INCLUDE_DIR SDL.h - HINTS - $ENV{SDLDIR} - PATH_SUFFIXES include/SDL include - PATHS - ~/Library/Frameworks - /Library/Frameworks - /usr/local/include/SDL12 - /usr/local/include/SDL11 # FreeBSD ports - /usr/include/SDL12 - /usr/include/SDL11 - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt -) -#MESSAGE("SDL_INCLUDE_DIR is ${SDL_INCLUDE_DIR}") - -# SDL-1.1 is the name used by FreeBSD ports... -# don't confuse it for the version number. -FIND_LIBRARY(SDL_LIBRARY_TEMP - NAMES SDL SDL-1.1 - HINTS - $ENV{SDLDIR} - PATH_SUFFIXES lib64 lib - PATHS - /sw - /opt/local - /opt/csw - /opt -) - -#MESSAGE("SDL_LIBRARY_TEMP is ${SDL_LIBRARY_TEMP}") - -IF(NOT SDL_BUILDING_LIBRARY) - IF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework") - # Non-OS X framework versions expect you to also dynamically link to - # SDLmain. This is mainly for Windows and OS X. Other (Unix) platforms - # seem to provide SDLmain for compatibility even though they don't - # necessarily need it. - FIND_LIBRARY(SDLMAIN_LIBRARY - NAMES SDLmain SDLmain-1.1 - HINTS - $ENV{SDLDIR} - PATH_SUFFIXES lib64 lib - PATHS - /sw - /opt/local - /opt/csw - /opt - ) - ENDIF(NOT ${SDL_INCLUDE_DIR} MATCHES ".framework") -ENDIF(NOT SDL_BUILDING_LIBRARY) - -# SDL may require threads on your system. -# The Apple build may not need an explicit flag because one of the -# frameworks may already provide it. -# But for non-OSX systems, I will use the CMake Threads package. -IF(NOT APPLE) - FIND_PACKAGE(Threads) -ENDIF(NOT APPLE) - -# MinGW needs an additional library, mwindows -# It's total link flags should look like -lmingw32 -lSDLmain -lSDL -lmwindows -# (Actually on second look, I think it only needs one of the m* libraries.) -IF(MINGW) - SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") -ENDIF(MINGW) - -SET(SDL_FOUND "NO") -IF(SDL_LIBRARY_TEMP) - # For SDLmain - IF(NOT SDL_BUILDING_LIBRARY) - IF(SDLMAIN_LIBRARY) - SET(SDL_LIBRARY_TEMP ${SDLMAIN_LIBRARY} ${SDL_LIBRARY_TEMP}) - ENDIF(SDLMAIN_LIBRARY) - ENDIF(NOT SDL_BUILDING_LIBRARY) - - # For OS X, SDL uses Cocoa as a backend so it must link to Cocoa. - # CMake doesn't display the -framework Cocoa string in the UI even - # though it actually is there if I modify a pre-used variable. - # I think it has something to do with the CACHE STRING. - # So I use a temporary variable until the end so I can set the - # "real" variable in one-shot. - IF(APPLE) - SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} "-framework Cocoa") - ENDIF(APPLE) - - # For threads, as mentioned Apple doesn't need this. - # In fact, there seems to be a problem if I used the Threads package - # and try using this line, so I'm just skipping it entirely for OS X. - IF(NOT APPLE) - SET(SDL_LIBRARY_TEMP ${SDL_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) - ENDIF(NOT APPLE) - - # For MinGW library - IF(MINGW) - SET(SDL_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL_LIBRARY_TEMP}) - ENDIF(MINGW) - - # Set the final string here so the GUI reflects the final state. - SET(SDL_LIBRARY ${SDL_LIBRARY_TEMP} CACHE STRING "Where the SDL Library can be found") - # Set the temp variable to INTERNAL so it is not seen in the CMake GUI - SET(SDL_LIBRARY_TEMP "${SDL_LIBRARY_TEMP}" CACHE INTERNAL "") - - SET(SDL_FOUND "YES") -ENDIF(SDL_LIBRARY_TEMP) - -#MESSAGE("SDL_LIBRARY is ${SDL_LIBRARY}") - diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake index fecd1654db..56ff1d5459 100644 --- a/cmake/GetGitRevisionDescription.cmake +++ b/cmake/GetGitRevisionDescription.cmake @@ -85,10 +85,6 @@ function(get_git_head_revision _refspecvar _hashvar) endfunction() function(git_describe _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - #get_git_head_revision(refspec hash) if(NOT GIT_FOUND) @@ -133,7 +129,8 @@ endfunction() function(get_git_tag_revision _var) if(NOT GIT_FOUND) - find_package(Git QUIET) + set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) + return() endif() execute_process(COMMAND diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index e279556848..831b140575 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -44,7 +44,8 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate + npcstats creaturestats ) add_component_dir (misc @@ -59,7 +60,8 @@ add_component_dir (files add_component_dir (compiler context controlparser errorhandler exception exprparser extensions fileparser generator lineparser literals locals output parser scanner scriptparser skipparser streamerrorhandler - stringparser tokenloc nullerrorhandler opcodes extensions0 + stringparser tokenloc nullerrorhandler opcodes extensions0 declarationparser + quickfileparser ) add_component_dir (interpreter diff --git a/components/compiler/context.hpp b/components/compiler/context.hpp index 1b02613c59..84bb89bdc4 100644 --- a/components/compiler/context.hpp +++ b/components/compiler/context.hpp @@ -33,11 +33,18 @@ namespace Compiler virtual char getGlobalType (const std::string& name) const = 0; ///< 'l: long, 's': short, 'f': float, ' ': does not exist. - virtual char getMemberType (const std::string& name, const std::string& id) const = 0; - ///< 'l: long, 's': short, 'f': float, ' ': does not exist. + virtual std::pair getMemberType (const std::string& name, + const std::string& id) const = 0; + ///< Return type of member variable \a name in script \a id or in script of reference of + /// \a id + /// \return first: 'l: long, 's': short, 'f': float, ' ': does not exist. + /// second: true: script of reference virtual bool isId (const std::string& name) const = 0; ///< Does \a name match an ID, that can be referenced? + + virtual bool isJournalId (const std::string& name) const = 0; + ///< Does \a name match a journal ID? }; } diff --git a/components/compiler/controlparser.cpp b/components/compiler/controlparser.cpp index 5d74ee9d42..aefe6d16da 100644 --- a/components/compiler/controlparser.cpp +++ b/components/compiler/controlparser.cpp @@ -7,6 +7,8 @@ #include "scanner.hpp" #include "generator.hpp" +#include "errorhandler.hpp" +#include "skipparser.hpp" namespace Compiler { @@ -70,7 +72,7 @@ namespace Compiler } else if (keyword==Scanner::K_else) { - mState = IfElseEndState; + mState = IfElseJunkState; /// \todo should be IfElseEndState; add an option for that } return true; @@ -106,7 +108,7 @@ namespace Compiler Codes expr; mExprParser.append (expr); - Generator::jump (loop, -static_cast (mCodeBlock.size()-expr.size())); + Generator::jump (loop, -static_cast (mCodeBlock.size()+expr.size())); std::copy (expr.begin(), expr.end(), std::back_inserter (mCode)); @@ -120,7 +122,7 @@ namespace Compiler Codes loop2; - Generator::jump (loop2, -static_cast (mCodeBlock.size()-expr.size()-skip.size())); + Generator::jump (loop2, -static_cast (mCodeBlock.size()+expr.size()+skip.size())); if (loop.size()!=loop2.size()) throw std::logic_error ( @@ -153,7 +155,7 @@ namespace Compiler } } - ControlParser::ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + ControlParser::ControlParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals) : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mLineParser (errorHandler, context, locals, literals, mCodeBlock), @@ -186,8 +188,11 @@ namespace Compiler { if (mState==StartState) { - if (keyword==Scanner::K_if) + if (keyword==Scanner::K_if || keyword==Scanner::K_elseif) { + if (keyword==Scanner::K_elseif) + getErrorHandler().warning ("elseif without matching if", loc); + mExprParser.reset(); scanner.scan (mExprParser); @@ -203,7 +208,8 @@ namespace Compiler return true; } } - else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState) + else if (mState==IfBodyState || mState==IfElseifBodyState || mState==IfElseBodyState || + mState==IfElseJunkState) { if (parseIfBody (keyword, loc, scanner)) return true; @@ -226,6 +232,7 @@ namespace Compiler case IfEndState: mState = IfBodyState; return true; case IfElseifEndState: mState = IfElseifBodyState; return true; case IfElseEndState: mState = IfElseBodyState; return true; + case IfElseJunkState: mState = IfElseBodyState; return true; case WhileEndState: mState = WhileBodyState; return true; @@ -243,7 +250,13 @@ namespace Compiler default: ; } - + } + else if (code==Scanner::S_open && mState==IfElseJunkState) + { + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + mState = IfElseBodyState; + return true; } return Parser::parseSpecial (code, loc, scanner); diff --git a/components/compiler/controlparser.hpp b/components/compiler/controlparser.hpp index 50fd2d1f9e..1175a0ed58 100644 --- a/components/compiler/controlparser.hpp +++ b/components/compiler/controlparser.hpp @@ -26,7 +26,8 @@ namespace Compiler IfElseEndState, IfElseBodyState, IfEndifState, WhileEndState, WhileBodyState, - WhileEndwhileState + WhileEndwhileState, + IfElseJunkState }; typedef std::vector Codes; @@ -47,7 +48,7 @@ namespace Compiler public: - ControlParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + ControlParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals); void appendCode (std::vector& code) const; diff --git a/components/compiler/declarationparser.cpp b/components/compiler/declarationparser.cpp new file mode 100644 index 0000000000..d17f49caf0 --- /dev/null +++ b/components/compiler/declarationparser.cpp @@ -0,0 +1,83 @@ + +#include "declarationparser.hpp" + +#include + +#include "scanner.hpp" +#include "errorhandler.hpp" +#include "skipparser.hpp" +#include "locals.hpp" + +Compiler::DeclarationParser::DeclarationParser (ErrorHandler& errorHandler, const Context& context, + Locals& locals) +: Parser (errorHandler, context), mLocals (locals), mState (State_Begin), mType (0) +{} + +bool Compiler::DeclarationParser::parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner) +{ + if (mState==State_Name) + { + std::string name2 = Misc::StringUtils::lowerCase (name); + + char type = mLocals.getType (name2); + + if (type!=' ') + { + /// \todo add option to make re-declared local variables an error + getErrorHandler().warning ("can't re-declare local variable (ignoring declaration)", + loc); + + mState = State_End; + return true; + } + + mLocals.declare (mType, name2); + + mState = State_End; + return true; + } + + return Parser::parseName (name, loc, scanner); +} + +bool Compiler::DeclarationParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) +{ + if (mState==State_Begin) + { + switch (keyword) + { + case Scanner::K_short: mType = 's'; break; + case Scanner::K_long: mType = 'l'; break; + case Scanner::K_float: mType = 'f'; break; + default: mType = 0; + } + + if (mType) + { + mState = State_Name; + return true; + } + } + else if (mState==State_Name) + { + // allow keywords to be used as local variable names. MW script compiler, you suck! + /// \todo option to disable this atrocity. + return parseName (loc.mLiteral, loc, scanner); + } + + return Parser::parseKeyword (keyword, loc, scanner); +} + +bool Compiler::DeclarationParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) +{ + if (code==Scanner::S_newline && mState==State_End) + return false; + + return Parser::parseSpecial (code, loc, scanner); +} + +void Compiler::DeclarationParser::reset() +{ + mState = State_Begin; +} \ No newline at end of file diff --git a/components/compiler/declarationparser.hpp b/components/compiler/declarationparser.hpp new file mode 100644 index 0000000000..43dd835705 --- /dev/null +++ b/components/compiler/declarationparser.hpp @@ -0,0 +1,43 @@ +#ifndef COMPILER_DECLARATIONPARSER_H_INCLUDED +#define COMPILER_DECLARATIONPARSER_H_INCLUDED + +#include "parser.hpp" + +namespace Compiler +{ + class Locals; + + class DeclarationParser : public Parser + { + enum State + { + State_Begin, State_Name, State_End + }; + + Locals& mLocals; + State mState; + char mType; + + public: + + DeclarationParser (ErrorHandler& errorHandler, const Context& context, Locals& locals); + + virtual bool parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner); + ///< Handle a name token. + /// \return fetch another token? + + virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); + ///< Handle a keyword token. + /// \return fetch another token? + + virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); + ///< Handle a special character token. + /// \return fetch another token? + + void reset(); + + }; +} + +#endif diff --git a/components/compiler/errorhandler.cpp b/components/compiler/errorhandler.cpp index ee13c837d1..fe58836cca 100644 --- a/components/compiler/errorhandler.cpp +++ b/components/compiler/errorhandler.cpp @@ -5,7 +5,7 @@ namespace Compiler { // constructor - ErrorHandler::ErrorHandler() : mWarnings (0), mErrors (0) {} + ErrorHandler::ErrorHandler() : mWarnings (0), mErrors (0), mWarningsMode (1) {} // destructor @@ -36,8 +36,13 @@ namespace Compiler void ErrorHandler::warning (const std::string& message, const TokenLoc& loc) { - ++mWarnings; - report (message, loc, WarningMessage); + if (mWarningsMode==1) + { + ++mWarnings; + report (message, loc, WarningMessage); + } + else if (mWarningsMode==2) + error (message, loc); } // Generate an error message. @@ -62,4 +67,9 @@ namespace Compiler { mErrors = mWarnings = 0; } + + void ErrorHandler::setWarningsMode (int mode) + { + mWarningsMode = mode; + } } diff --git a/components/compiler/errorhandler.hpp b/components/compiler/errorhandler.hpp index 256065854a..e5922a6be5 100644 --- a/components/compiler/errorhandler.hpp +++ b/components/compiler/errorhandler.hpp @@ -16,6 +16,7 @@ namespace Compiler { int mWarnings; int mErrors; + int mWarningsMode; protected: @@ -60,8 +61,11 @@ namespace Compiler void endOfFile(); ///< Generate an error message for an unexpected EOF. - virtual void reset(); + virtual void reset(); ///< Remove all previous error/warning events + + void setWarningsMode (int mode); + ///< // 0 ignore, 1 rate as warning, 2 rate as error }; } diff --git a/components/compiler/exprparser.cpp b/components/compiler/exprparser.cpp index 94240c5eb9..0c013b18f1 100644 --- a/components/compiler/exprparser.cpp +++ b/components/compiler/exprparser.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include "generator.hpp" #include "scanner.hpp" #include "errorhandler.hpp" @@ -14,7 +16,6 @@ #include "stringparser.hpp" #include "extensions.hpp" #include "context.hpp" -#include namespace Compiler { @@ -203,21 +204,22 @@ namespace Compiler std::string name2 = Misc::StringUtils::lowerCase (name); std::string id = Misc::StringUtils::lowerCase (mExplicit); - char type = getContext().getMemberType (name2, id); + std::pair type = getContext().getMemberType (name2, id); - if (type!=' ') + if (type.first!=' ') { - Generator::fetchMember (mCode, mLiterals, type, name2, id); + Generator::fetchMember (mCode, mLiterals, type.first, name2, id, !type.second); + mNextOperand = false; mExplicit.clear(); - mOperands.push_back (type=='f' ? 'f' : 'l'); + mOperands.push_back (type.first=='f' ? 'f' : 'l'); return true; } return false; } - ExprParser::ExprParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + ExprParser::ExprParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, bool argument) : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mNextOperand (true), mFirst (true), mArgument (argument), mRefOp (false), mMemberOp (false) @@ -308,6 +310,22 @@ namespace Compiler return true; } + // die in a fire, Morrowind script compiler! + if (const Extensions *extensions = getContext().getExtensions()) + { + if (getContext().isJournalId (name2)) + { + // JournalID used as an argument. Use the index of that JournalID + Generator::pushString (mCode, mLiterals, name2); + int keyword = extensions->searchKeyword ("getjournalindex"); + extensions->generateFunctionCode (keyword, mCode, mLiterals, mExplicit, 0); + mNextOperand = false; + mOperands.push_back ('l'); + + return 2; + } + } + if (mExplicit.empty() && getContext().isId (name2)) { mExplicit = name2; @@ -326,6 +344,31 @@ namespace Compiler bool ExprParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { + if (const Extensions *extensions = getContext().getExtensions()) + { + std::string argumentType; // ignored + bool hasExplicit = false; // ignored + if (extensions->isInstruction (keyword, argumentType, hasExplicit)) + { + // pretend this is not a keyword + return parseName (loc.mLiteral, loc, scanner); + } + } + + if (keyword==Scanner::K_end || keyword==Scanner::K_begin || + keyword==Scanner::K_short || keyword==Scanner::K_long || + keyword==Scanner::K_float || keyword==Scanner::K_if || + keyword==Scanner::K_endif || keyword==Scanner::K_else || + keyword==Scanner::K_elseif || keyword==Scanner::K_while || + keyword==Scanner::K_endwhile || keyword==Scanner::K_return || + keyword==Scanner::K_messagebox || keyword==Scanner::K_set || + keyword==Scanner::K_to || keyword==Scanner::K_startscript || + keyword==Scanner::K_stopscript || keyword==Scanner::K_enable || + keyword==Scanner::K_disable) + { + return parseName (loc.mLiteral, loc, scanner); + } + mFirst = false; if (!mExplicit.empty()) @@ -368,8 +411,15 @@ namespace Compiler char returnType; std::string argumentType; - if (extensions->isFunction (keyword, returnType, argumentType, true)) + bool hasExplicit = true; + if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit)) { + if (!hasExplicit) + { + getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + mExplicit.clear(); + } + start(); mTokenLoc = loc; @@ -490,7 +540,9 @@ namespace Compiler char returnType; std::string argumentType; - if (extensions->isFunction (keyword, returnType, argumentType, false)) + bool hasExplicit = false; + + if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit)) { mTokenLoc = loc; int optionals = parseArguments (argumentType, scanner); @@ -518,6 +570,14 @@ namespace Compiler { if (!mExplicit.empty()) { + if (mRefOp && code==Scanner::S_open) + { + /// \todo add option to disable this workaround + mOperators.push_back ('('); + mTokenLoc = loc; + return true; + } + if (!mRefOp && code==Scanner::S_ref) { mRefOp = true; @@ -687,11 +747,11 @@ namespace Compiler { optional = true; } - else if (*iter=='S' || *iter=='c') + else if (*iter=='S' || *iter=='c' || *iter=='x') { stringParser.reset(); - if (optional) + if (optional || *iter=='x') stringParser.setOptional (true); if (*iter=='c') stringParser.smashCase(); @@ -700,18 +760,21 @@ namespace Compiler if (optional && stringParser.isEmpty()) break; - if (invert) + if (*iter!='x') { - std::vector tmp; - stringParser.append (tmp); + if (invert) + { + std::vector tmp; + stringParser.append (tmp); - stack.push (tmp); + stack.push (tmp); + } + else + stringParser.append (code); + + if (optional) + ++optionalCount; } - else - stringParser.append (code); - - if (optional) - ++optionalCount; } else { diff --git a/components/compiler/exprparser.hpp b/components/compiler/exprparser.hpp index 8ce5409d23..6a4e1be2ff 100644 --- a/components/compiler/exprparser.hpp +++ b/components/compiler/exprparser.hpp @@ -58,7 +58,7 @@ namespace Compiler public: - ExprParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + ExprParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, bool argument = false); ///< constructor /// \param argument Parser is used to parse function- or instruction- @@ -101,6 +101,7 @@ namespace Compiler /// \param arguments Each character represents one arguments ('l': integer, /// 'f': float, 'S': string, 'c': string (case smashed), '/': following arguments are /// optional) + /// 'x': optional string that will be ignored (die in a fire, MW script compiler!) /// \param invert Store arguments in reverted order. /// \return number of optional arguments }; diff --git a/components/compiler/extensions.cpp b/components/compiler/extensions.cpp index c6a74b234b..c09abcbafd 100644 --- a/components/compiler/extensions.cpp +++ b/components/compiler/extensions.cpp @@ -22,7 +22,7 @@ namespace Compiler } bool Extensions::isFunction (int keyword, char& returnType, std::string& argumentType, - bool explicitReference) const + bool& explicitReference) const { std::map::const_iterator iter = mFunctions.find (keyword); @@ -30,7 +30,7 @@ namespace Compiler return false; if (explicitReference && iter->second.mCodeExplicit==-1) - return false; + explicitReference = false; returnType = iter->second.mReturn; argumentType = iter->second.mArguments; @@ -38,7 +38,7 @@ namespace Compiler } bool Extensions::isInstruction (int keyword, std::string& argumentType, - bool explicitReference) const + bool& explicitReference) const { std::map::const_iterator iter = mInstructions.find (keyword); @@ -46,7 +46,7 @@ namespace Compiler return false; if (explicitReference && iter->second.mCodeExplicit==-1) - return false; + explicitReference = false; argumentType = iter->second.mArguments; return true; diff --git a/components/compiler/extensions.hpp b/components/compiler/extensions.hpp index 34551374a0..18bb24ed09 100644 --- a/components/compiler/extensions.hpp +++ b/components/compiler/extensions.hpp @@ -47,13 +47,17 @@ namespace Compiler /// - keyword must be all lower case. bool isFunction (int keyword, char& returnType, std::string& argumentType, - bool explicitReference) const; + bool& explicitReference) const; ///< Is this keyword registered with a function? If yes, return return and argument /// types. + /// \param explicitReference In: has explicit reference; Out: set to false, if + /// explicit reference is not available for this instruction. bool isInstruction (int keyword, std::string& argumentType, - bool explicitReference) const; + bool& explicitReference) const; ///< Is this keyword registered with a function? If yes, return argument types. + /// \param explicitReference In: has explicit reference; Out: set to false, if + /// explicit reference is not available for this instruction. void registerFunction (const std::string& keyword, char returnType, const std::string& argumentType, int code, int codeExplicit = -1); diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index baec9987fa..78b6409f22 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -41,7 +41,7 @@ namespace Compiler opcodeAiEscortCellExplicit); extensions.registerInstruction ("aiwander", "fff/llllllllll", opcodeAiWander, opcodeAiWanderExplicit); - extensions.registerInstruction ("aifollow", "cffff/l", opcodeAiFollow, + extensions.registerInstruction ("aifollow", "cffff/llllllll", opcodeAiFollow, opcodeAiFollowExplicit); extensions.registerInstruction ("aifollowcell", "ccffff/l", opcodeAiFollowCell, opcodeAiFollowCellExplicit); @@ -62,7 +62,7 @@ namespace Compiler extensions.registerInstruction ("toggleai", "", opcodeToggleAI, opcodeToggleAI); extensions.registerInstruction ("tai", "", opcodeToggleAI, opcodeToggleAI); extensions.registerInstruction("startcombat", "c", opcodeStartCombat, opcodeStartCombatExplicit); - extensions.registerInstruction("stopcombat", "", opcodeStopCombat, opcodeStopCombatExplicit); + extensions.registerInstruction("stopcombat", "x", opcodeStopCombat, opcodeStopCombatExplicit); extensions.registerFunction ("gethello", 'l', "", opcodeGetHello, opcodeGetHelloExplicit); extensions.registerFunction ("getfight", 'l', "", opcodeGetFight, opcodeGetFightExplicit); extensions.registerFunction ("getflee", 'l', "", opcodeGetFlee, opcodeGetFleeExplicit); @@ -190,7 +190,7 @@ namespace Compiler extensions.registerInstruction ("enableclassmenu", "", opcodeEnableClassMenu); extensions.registerInstruction ("enablenamemenu", "", opcodeEnableNameMenu); extensions.registerInstruction ("enableracemenu", "", opcodeEnableRaceMenu); - extensions.registerInstruction ("enablestatreviewmenu", "", + extensions.registerInstruction ("enablestatreviewmenu", "", opcodeEnableStatsReviewMenu); extensions.registerInstruction ("enableinventorymenu", "", opcodeEnableInventoryMenu); @@ -253,7 +253,7 @@ namespace Compiler extensions.registerFunction ("getlocked", 'l', "", opcodeGetLocked, opcodeGetLockedExplicit); extensions.registerFunction ("geteffect", 'l', "S", opcodeGetEffect, opcodeGetEffectExplicit); extensions.registerInstruction ("addsoulgem", "cc", opcodeAddSoulGem, opcodeAddSoulGemExplicit); - extensions.registerInstruction ("removesoulgem", "c", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit); + extensions.registerInstruction ("removesoulgem", "c/l", opcodeRemoveSoulGem, opcodeRemoveSoulGemExplicit); extensions.registerInstruction ("drop", "cl", opcodeDrop, opcodeDropExplicit); extensions.registerInstruction ("dropsoulgem", "c", opcodeDropSoulGem, opcodeDropSoulGemExplicit); extensions.registerFunction ("getattacked", 'l', "", opcodeGetAttacked, opcodeGetAttackedExplicit); @@ -276,6 +276,8 @@ namespace Compiler extensions.registerInstruction("togglegodmode", "", opcodeToggleGodMode); extensions.registerInstruction ("disablelevitation", "", opcodeDisableLevitation); extensions.registerInstruction ("enablelevitation", "", opcodeEnableLevitation); + extensions.registerFunction ("getpcinjail", 'l', "", opcodeGetPcInJail); + extensions.registerFunction ("getpctraveling", 'l', "", opcodeGetPcTraveling); } } @@ -396,7 +398,7 @@ namespace Compiler extensions.registerInstruction ("setpccrimelevel", "f", opcodeSetPCCrimeLevel); extensions.registerInstruction ("modpccrimelevel", "f", opcodeModPCCrimeLevel); - extensions.registerInstruction ("addspell", "c", opcodeAddSpell, opcodeAddSpellExplicit); + extensions.registerInstruction ("addspell", "cx", opcodeAddSpell, opcodeAddSpellExplicit); extensions.registerInstruction ("removespell", "c", opcodeRemoveSpell, opcodeRemoveSpellExplicit); extensions.registerInstruction ("removespelleffects", "c", opcodeRemoveSpellEffects, diff --git a/components/compiler/generator.cpp b/components/compiler/generator.cpp index 9b02e4273f..c67e51e572 100644 --- a/components/compiler/generator.cpp +++ b/components/compiler/generator.cpp @@ -260,34 +260,34 @@ namespace code.push_back (Compiler::Generator::segment5 (44)); } - void opStoreMemberShort (Compiler::Generator::CodeContainer& code) + void opStoreMemberShort (Compiler::Generator::CodeContainer& code, bool global) { - code.push_back (Compiler::Generator::segment5 (59)); + code.push_back (Compiler::Generator::segment5 (global ? 65 : 59)); } - void opStoreMemberLong (Compiler::Generator::CodeContainer& code) + void opStoreMemberLong (Compiler::Generator::CodeContainer& code, bool global) { - code.push_back (Compiler::Generator::segment5 (60)); + code.push_back (Compiler::Generator::segment5 (global ? 66 : 60)); } - void opStoreMemberFloat (Compiler::Generator::CodeContainer& code) + void opStoreMemberFloat (Compiler::Generator::CodeContainer& code, bool global) { - code.push_back (Compiler::Generator::segment5 (61)); + code.push_back (Compiler::Generator::segment5 (global ? 67 : 61)); } - void opFetchMemberShort (Compiler::Generator::CodeContainer& code) + void opFetchMemberShort (Compiler::Generator::CodeContainer& code, bool global) { - code.push_back (Compiler::Generator::segment5 (62)); + code.push_back (Compiler::Generator::segment5 (global ? 68 : 62)); } - void opFetchMemberLong (Compiler::Generator::CodeContainer& code) + void opFetchMemberLong (Compiler::Generator::CodeContainer& code, bool global) { - code.push_back (Compiler::Generator::segment5 (63)); + code.push_back (Compiler::Generator::segment5 (global ? 69 : 63)); } - void opFetchMemberFloat (Compiler::Generator::CodeContainer& code) + void opFetchMemberFloat (Compiler::Generator::CodeContainer& code, bool global) { - code.push_back (Compiler::Generator::segment5 (64)); + code.push_back (Compiler::Generator::segment5 (global ? 70 : 64)); } void opRandom (Compiler::Generator::CodeContainer& code) @@ -593,7 +593,7 @@ namespace Compiler else if (offset<0) opJumpBackward (code, -offset); else - throw std::logic_error ("inifite loop"); + throw std::logic_error ("infinite loop"); } void jumpOnZero (CodeContainer& code, int offset) @@ -738,7 +738,8 @@ namespace Compiler } void assignToMember (CodeContainer& code, Literals& literals, char localType, - const std::string& name, const std::string& id, const CodeContainer& value, char valueType) + const std::string& name, const std::string& id, const CodeContainer& value, + char valueType, bool global) { int index = literals.addString (name); @@ -766,17 +767,17 @@ namespace Compiler { case 'f': - opStoreMemberFloat (code); + opStoreMemberFloat (code, global); break; case 's': - opStoreMemberShort (code); + opStoreMemberShort (code, global); break; case 'l': - opStoreMemberLong (code); + opStoreMemberLong (code, global); break; default: @@ -786,7 +787,7 @@ namespace Compiler } void fetchMember (CodeContainer& code, Literals& literals, char localType, - const std::string& name, const std::string& id) + const std::string& name, const std::string& id, bool global) { int index = literals.addString (name); @@ -800,17 +801,17 @@ namespace Compiler { case 'f': - opFetchMemberFloat (code); + opFetchMemberFloat (code, global); break; case 's': - opFetchMemberShort (code); + opFetchMemberShort (code, global); break; case 'l': - opFetchMemberLong (code); + opFetchMemberLong (code, global); break; default: diff --git a/components/compiler/generator.hpp b/components/compiler/generator.hpp index feab26c93d..b511161225 100644 --- a/components/compiler/generator.hpp +++ b/components/compiler/generator.hpp @@ -102,10 +102,12 @@ namespace Compiler const std::string& name); void assignToMember (CodeContainer& code, Literals& literals, char memberType, - const std::string& name, const std::string& id, const CodeContainer& value, char valueType); + const std::string& name, const std::string& id, const CodeContainer& value, char valueType, bool global); + ///< \param global Member of a global script instead of a script of a reference. void fetchMember (CodeContainer& code, Literals& literals, char memberType, - const std::string& name, const std::string& id); + const std::string& name, const std::string& id, bool global); + ///< \param global Member of a global script instead of a script of a reference. void random (CodeContainer& code); diff --git a/components/compiler/lineparser.cpp b/components/compiler/lineparser.cpp index 3d9ac0a93a..5457d76255 100644 --- a/components/compiler/lineparser.cpp +++ b/components/compiler/lineparser.cpp @@ -1,6 +1,8 @@ #include "lineparser.hpp" +#include + #include "scanner.hpp" #include "context.hpp" #include "errorhandler.hpp" @@ -8,7 +10,7 @@ #include "locals.hpp" #include "generator.hpp" #include "extensions.hpp" -#include +#include "declarationparser.hpp" namespace Compiler { @@ -48,7 +50,7 @@ namespace Compiler } } - LineParser::LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + LineParser::LineParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, std::vector& code, bool allowExpression) : Parser (errorHandler, context), mLocals (locals), mLiterals (literals), mCode (code), mState (BeginState), mExprParser (errorHandler, context, locals, literals), @@ -82,33 +84,9 @@ namespace Compiler bool LineParser::parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner) { - if (mState==ShortState || mState==LongState || mState==FloatState) + if (mState==PotentialEndState) { - if (!getContext().canDeclareLocals()) - { - getErrorHandler().error ("local variables can't be declared in this context", loc); - SkipParser skip (getErrorHandler(), getContext()); - scanner.scan (skip); - return false; - } - - std::string name2 = Misc::StringUtils::lowerCase (name); - - char type = mLocals.getType (name2); - - if (type!=' ') - { - /// \todo add option to make re-declared local variables an error - getErrorHandler().warning ("can't re-declare local variable", loc); - SkipParser skip (getErrorHandler(), getContext()); - scanner.scan (skip); - mState = EndState; - return true; - } - - mLocals.declare (mState==ShortState ? 's' : (mState==LongState ? 'l' : 'f'), - name2); - + getErrorHandler().warning ("stay string argument (ignoring it)", loc); mState = EndState; return true; } @@ -142,12 +120,13 @@ namespace Compiler if (mState==SetMemberVarState) { mMemberName = name; - char type = getContext().getMemberType (mMemberName, mName); + std::pair type = getContext().getMemberType (mMemberName, mName); - if (type!=' ') + if (type.first!=' ') { mState = SetMemberVarState2; - mType = type; + mType = type.first; + mReferenceMember = type.second; return true; } @@ -240,6 +219,34 @@ namespace Compiler bool LineParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { + if (mState==SetMemberVarState) + { + mMemberName = loc.mLiteral; + std::pair type = getContext().getMemberType (mMemberName, mName); + + if (type.first!=' ') + { + mState = SetMemberVarState2; + mType = type.first; + mReferenceMember = type.second; + return true; + } + } + + if (mState==SetPotentialMemberVarState && keyword==Scanner::K_to) + { + getErrorHandler().warning ("unknown variable (ignoring set instruction)", loc); + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return false; + } + + if (mState==SetState) + { + // allow keywords to be used as variable names when assigning a value to a variable. + return parseName (loc.mLiteral, loc, scanner); + } + if (mState==BeginState || mState==ExplicitState) { switch (keyword) @@ -247,13 +254,13 @@ namespace Compiler case Scanner::K_enable: Generator::enable (mCode, mLiterals, mExplicit); - mState = EndState; + mState = PotentialEndState; return true; case Scanner::K_disable: Generator::disable (mCode, mLiterals, mExplicit); - mState = EndState; + mState = PotentialEndState; return true; } @@ -262,8 +269,15 @@ namespace Compiler { std::string argumentType; - if (extensions->isInstruction (keyword, argumentType, mState==ExplicitState)) + bool hasExplicit = mState==ExplicitState; + if (extensions->isInstruction (keyword, argumentType, hasExplicit)) { + if (!hasExplicit && mState==ExplicitState) + { + getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + mExplicit.clear(); + } + int optionals = mExprParser.parseArguments (argumentType, scanner, mCode, true); extensions->generateInstructionCode (keyword, mCode, mLiterals, mExplicit, optionals); @@ -287,9 +301,16 @@ namespace Compiler char returnType; std::string argumentType; - if (extensions->isFunction (keyword, returnType, argumentType, - !mExplicit.empty())) + bool hasExplicit = !mExplicit.empty(); + + if (extensions->isFunction (keyword, returnType, argumentType, hasExplicit)) { + if (!hasExplicit && !mExplicit.empty()) + { + getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + mExplicit.clear(); + } + scanner.putbackKeyword (keyword, loc); parseExpression (scanner, loc); mState = EndState; @@ -299,13 +320,38 @@ namespace Compiler } } + if (mState==ExplicitState) + { + // drop stray explicit reference + getErrorHandler().warning ("stray explicit reference (ignoring it)", loc); + mState = BeginState; + mExplicit.clear(); + } + if (mState==BeginState) { switch (keyword) { - case Scanner::K_short: mState = ShortState; return true; - case Scanner::K_long: mState = LongState; return true; - case Scanner::K_float: mState = FloatState; return true; + case Scanner::K_short: + case Scanner::K_long: + case Scanner::K_float: + { + if (!getContext().canDeclareLocals()) + { + getErrorHandler().error ( + "local variables can't be declared in this context", loc); + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return true; + } + + DeclarationParser declaration (getErrorHandler(), getContext(), mLocals); + if (declaration.parseKeyword (keyword, loc, scanner)) + scanner.scan (declaration); + + return true; + } + case Scanner::K_set: mState = SetState; return true; case Scanner::K_messagebox: mState = MessageState; return true; @@ -328,6 +374,24 @@ namespace Compiler Generator::stopScript (mCode); mState = EndState; return true; + + case Scanner::K_else: + + getErrorHandler().warning ("stay else (ignoring it)", loc); + mState = EndState; + return true; + + case Scanner::K_endif: + + getErrorHandler().warning ("stay endif (ignoring it)", loc); + mState = EndState; + return true; + + case Scanner::K_begin: + + getErrorHandler().warning ("stay begin (ignoring it)", loc); + mState = EndState; + return true; } } else if (mState==SetLocalVarState && keyword==Scanner::K_to) @@ -365,7 +429,8 @@ namespace Compiler std::vector code; char type = mExprParser.append (code); - Generator::assignToMember (mCode, mLiterals, mType, mMemberName, mName, code, type); + Generator::assignToMember (mCode, mLiterals, mType, mMemberName, mName, code, type, + !mReferenceMember); mState = EndState; return true; @@ -389,7 +454,8 @@ namespace Compiler bool LineParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) { - if (code==Scanner::S_newline && (mState==EndState || mState==BeginState)) + if (code==Scanner::S_newline && + (mState==EndState || mState==BeginState || mState==PotentialEndState)) return false; if (code==Scanner::S_comma && mState==MessageState) diff --git a/components/compiler/lineparser.hpp b/components/compiler/lineparser.hpp index aa74cd232f..4f2d33bde5 100644 --- a/components/compiler/lineparser.hpp +++ b/components/compiler/lineparser.hpp @@ -20,11 +20,10 @@ namespace Compiler enum State { BeginState, - ShortState, LongState, FloatState, SetState, SetLocalVarState, SetGlobalVarState, SetPotentialMemberVarState, SetMemberVarState, SetMemberVarState2, MessageState, MessageCommaState, MessageButtonState, MessageButtonCommaState, - EndState, + EndState, PotentialEndState /* may have a stray string argument */, PotentialExplicitState, ExplicitState, MemberState }; @@ -34,6 +33,7 @@ namespace Compiler State mState; std::string mName; std::string mMemberName; + bool mReferenceMember; int mButtons; std::string mExplicit; char mType; @@ -44,7 +44,7 @@ namespace Compiler public: - LineParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + LineParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, Literals& literals, std::vector& code, bool allowExpression = false); ///< \param allowExpression Allow lines consisting of a naked expression diff --git a/components/compiler/locals.cpp b/components/compiler/locals.cpp index e2b1c5c966..60a5704bf3 100644 --- a/components/compiler/locals.cpp +++ b/components/compiler/locals.cpp @@ -7,6 +7,8 @@ #include #include +#include + namespace Compiler { const std::vector& Locals::get (char type) const @@ -97,7 +99,7 @@ namespace Compiler void Locals::declare (char type, const std::string& name) { - get (type).push_back (name); + get (type).push_back (Misc::StringUtils::lowerCase (name)); } void Locals::clear() diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index 57d86e62bf..1dbdbf7e72 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -203,8 +203,8 @@ namespace Compiler const int opcodeGetEffectExplicit = 0x20001d0; const int opcodeAddSoulGem = 0x20001f3; const int opcodeAddSoulGemExplicit = 0x20001f4; - const int opcodeRemoveSoulGem = 0x20001f5; - const int opcodeRemoveSoulGemExplicit = 0x20001f6; + const int opcodeRemoveSoulGem = 0x20027; + const int opcodeRemoveSoulGemExplicit = 0x20028; const int opcodeDrop = 0x20001f8; const int opcodeDropExplicit = 0x20001f9; const int opcodeDropSoulGem = 0x20001fa; @@ -245,6 +245,8 @@ namespace Compiler const int opcodeCastExplicit = 0x2000228; const int opcodeExplodeSpell = 0x2000229; const int opcodeExplodeSpellExplicit = 0x200022a; + const int opcodeGetPcInJail = 0x200023e; + const int opcodeGetPcTraveling = 0x200023f; } namespace Sky diff --git a/components/compiler/parser.cpp b/components/compiler/parser.cpp index 8d11c4086d..781fbad8cb 100644 --- a/components/compiler/parser.cpp +++ b/components/compiler/parser.cpp @@ -52,7 +52,7 @@ namespace Compiler // Return context - Context& Parser::getContext() + const Context& Parser::getContext() const { return mContext; } @@ -64,7 +64,7 @@ namespace Compiler return lowerCase; } - Parser::Parser (ErrorHandler& errorHandler, Context& context) + Parser::Parser (ErrorHandler& errorHandler, const Context& context) : mErrorHandler (errorHandler), mContext (context), mOptional (false), mEmpty (true) {} diff --git a/components/compiler/parser.hpp b/components/compiler/parser.hpp index 4fec570e9f..54e913b202 100644 --- a/components/compiler/parser.hpp +++ b/components/compiler/parser.hpp @@ -17,7 +17,7 @@ namespace Compiler class Parser { ErrorHandler& mErrorHandler; - Context& mContext; + const Context& mContext; bool mOptional; bool mEmpty; @@ -38,14 +38,14 @@ namespace Compiler ErrorHandler& getErrorHandler(); ///< Return error handler - Context& getContext(); + const Context& getContext() const; ///< Return context static std::string toLower (const std::string& name); public: - Parser (ErrorHandler& errorHandler, Context& context); + Parser (ErrorHandler& errorHandler, const Context& context); ///< constructor virtual ~Parser(); diff --git a/components/compiler/quickfileparser.cpp b/components/compiler/quickfileparser.cpp new file mode 100644 index 0000000000..f3d8063b2f --- /dev/null +++ b/components/compiler/quickfileparser.cpp @@ -0,0 +1,52 @@ + +#include "quickfileparser.hpp" + +#include "skipparser.hpp" +#include "scanner.hpp" + +Compiler::QuickFileParser::QuickFileParser (ErrorHandler& errorHandler, const Context& context, + Locals& locals) +: Parser (errorHandler, context), mDeclarationParser (errorHandler, context, locals) +{} + +bool Compiler::QuickFileParser::parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner) +{ + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return true; +} + +bool Compiler::QuickFileParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) +{ + if (keyword==Scanner::K_end) + return false; + + if (keyword==Scanner::K_short || keyword==Scanner::K_long || keyword==Scanner::K_float) + { + mDeclarationParser.reset(); + scanner.putbackKeyword (keyword, loc); + scanner.scan (mDeclarationParser); + return true; + } + + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + return true; +} + +bool Compiler::QuickFileParser::parseSpecial (int code, const TokenLoc& loc, Scanner& scanner) +{ + if (code!=Scanner::S_newline) + { + SkipParser skip (getErrorHandler(), getContext()); + scanner.scan (skip); + } + + return true; +} + +void Compiler::QuickFileParser::parseEOF (Scanner& scanner) +{ + +} \ No newline at end of file diff --git a/components/compiler/quickfileparser.hpp b/components/compiler/quickfileparser.hpp new file mode 100644 index 0000000000..440d910387 --- /dev/null +++ b/components/compiler/quickfileparser.hpp @@ -0,0 +1,39 @@ +#ifndef COMPILER_QUICKFILEPARSER_H_INCLUDED +#define COMPILER_QUICKFILEPARSER_H_INCLUDED + +#include "parser.hpp" +#include "declarationparser.hpp" + +namespace Compiler +{ + class Locals; + + /// \brief File parser variant that ignores everything but variable declarations + class QuickFileParser : public Parser + { + DeclarationParser mDeclarationParser; + + public: + + QuickFileParser (ErrorHandler& errorHandler, const Context& context, Locals& locals); + + virtual bool parseName (const std::string& name, const TokenLoc& loc, + Scanner& scanner); + ///< Handle a name token. + /// \return fetch another token? + + virtual bool parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner); + ///< Handle a keyword token. + /// \return fetch another token? + + virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); + ///< Handle a special character token. + /// \return fetch another token? + + virtual void parseEOF (Scanner& scanner); + ///< Handle EOF token. + }; +} + +#endif + diff --git a/components/compiler/scanner.cpp b/components/compiler/scanner.cpp index 816443c447..46e50a2e9b 100644 --- a/components/compiler/scanner.cpp +++ b/components/compiler/scanner.cpp @@ -370,9 +370,9 @@ namespace Compiler if (c=='\n') special = S_newline; - else if (c=='(') + else if (c=='(' || c=='[') /// \todo option to disable the use of [ as alias for ( special = S_open; - else if (c==')') + else if (c==')' || c==']') /// \todo option to disable the use of ] as alias for ) special = S_close; else if (c=='.') { diff --git a/components/compiler/scriptparser.cpp b/components/compiler/scriptparser.cpp index 5b3d244b0d..ea11be5f03 100644 --- a/components/compiler/scriptparser.cpp +++ b/components/compiler/scriptparser.cpp @@ -7,7 +7,7 @@ namespace Compiler { - ScriptParser::ScriptParser (ErrorHandler& errorHandler, Context& context, + ScriptParser::ScriptParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, bool end) : Parser (errorHandler, context), mOutput (locals), mLineParser (errorHandler, context, locals, mOutput.getLiterals(), mOutput.getCode()), @@ -32,7 +32,7 @@ namespace Compiler bool ScriptParser::parseKeyword (int keyword, const TokenLoc& loc, Scanner& scanner) { - if (keyword==Scanner::K_while || keyword==Scanner::K_if) + if (keyword==Scanner::K_while || keyword==Scanner::K_if || keyword==Scanner::K_elseif) { mControlParser.reset(); if (mControlParser.parseKeyword (keyword, loc, scanner)) @@ -71,6 +71,12 @@ namespace Compiler if (code==Scanner::S_newline) // empty line return true; + if (code==Scanner::S_open) /// \todo Option to switch this off + { + scanner.putbackSpecial (code, loc); + return parseKeyword (Scanner::K_if, loc, scanner); + } + mLineParser.reset(); if (mLineParser.parseSpecial (code, loc, scanner)) scanner.scan (mLineParser); diff --git a/components/compiler/scriptparser.hpp b/components/compiler/scriptparser.hpp index bb4809dabd..000244c793 100644 --- a/components/compiler/scriptparser.hpp +++ b/components/compiler/scriptparser.hpp @@ -12,7 +12,7 @@ namespace Compiler class Locals; // Script parser, to be used in dialogue scripts and as part of FileParser - + class ScriptParser : public Parser { Output mOutput; @@ -21,14 +21,14 @@ namespace Compiler bool mEnd; public: - + /// \param end of script is marked by end keyword. - ScriptParser (ErrorHandler& errorHandler, Context& context, Locals& locals, + ScriptParser (ErrorHandler& errorHandler, const Context& context, Locals& locals, bool end = false); - + void getCode (std::vector& code) const; ///< store generated code in \æ code. - + virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); ///< Handle a name token. @@ -43,8 +43,8 @@ namespace Compiler /// \return fetch another token? virtual void parseEOF (Scanner& scanner); - ///< Handle EOF token. - + ///< Handle EOF token. + void reset(); ///< Reset parser to clean state. }; diff --git a/components/compiler/skipparser.cpp b/components/compiler/skipparser.cpp index 2d09b63ba0..c7cb31f58e 100644 --- a/components/compiler/skipparser.cpp +++ b/components/compiler/skipparser.cpp @@ -5,7 +5,7 @@ namespace Compiler { - SkipParser::SkipParser (ErrorHandler& errorHandler, Context& context) + SkipParser::SkipParser (ErrorHandler& errorHandler, const Context& context) : Parser (errorHandler, context) {} @@ -34,7 +34,7 @@ namespace Compiler { if (code==Scanner::S_newline) return false; - + return true; } } diff --git a/components/compiler/skipparser.hpp b/components/compiler/skipparser.hpp index 17f1c8d988..239c8bb02c 100644 --- a/components/compiler/skipparser.hpp +++ b/components/compiler/skipparser.hpp @@ -8,13 +8,13 @@ namespace Compiler // \brief Skip parser for skipping a line // // This parser is mainly intended for skipping the rest of a faulty line. - + class SkipParser : public Parser { public: - - SkipParser (ErrorHandler& errorHandler, Context& context); - + + SkipParser (ErrorHandler& errorHandler, const Context& context); + virtual bool parseInt (int value, const TokenLoc& loc, Scanner& scanner); ///< Handle an int token. /// \return fetch another token? diff --git a/components/compiler/stringparser.cpp b/components/compiler/stringparser.cpp index 09c902131a..a86c15794f 100644 --- a/components/compiler/stringparser.cpp +++ b/components/compiler/stringparser.cpp @@ -10,7 +10,7 @@ namespace Compiler { - StringParser::StringParser (ErrorHandler& errorHandler, Context& context, Literals& literals) + StringParser::StringParser (ErrorHandler& errorHandler, const Context& context, Literals& literals) : Parser (errorHandler, context), mLiterals (literals), mState (StartState), mSmashCase (false) { diff --git a/components/compiler/stringparser.hpp b/components/compiler/stringparser.hpp index f692c650b8..3859a24965 100644 --- a/components/compiler/stringparser.hpp +++ b/components/compiler/stringparser.hpp @@ -10,22 +10,22 @@ namespace Compiler { class Literals; - + class StringParser : public Parser { enum State { StartState, CommaState }; - + Literals& mLiterals; State mState; std::vector mCode; bool mSmashCase; - + public: - - StringParser (ErrorHandler& errorHandler, Context& context, Literals& literals); + + StringParser (ErrorHandler& errorHandler, const Context& context, Literals& literals); virtual bool parseName (const std::string& name, const TokenLoc& loc, Scanner& scanner); @@ -35,15 +35,15 @@ namespace Compiler virtual bool parseSpecial (int code, const TokenLoc& loc, Scanner& scanner); ///< Handle a special character token. /// \return fetch another token? - + void append (std::vector& code); ///< Append code for parsed string. - + void smashCase(); ///< Transform all scanned strings to lower case - + void reset(); - ///< Reset parser to clean state (this includes the smashCase function). + ///< Reset parser to clean state (this includes the smashCase function). }; } diff --git a/components/esm/creaturestate.cpp b/components/esm/creaturestate.cpp index 43cde30251..9e9b561026 100644 --- a/components/esm/creaturestate.cpp +++ b/components/esm/creaturestate.cpp @@ -6,6 +6,8 @@ void ESM::CreatureState::load (ESMReader &esm) ObjectState::load (esm); mInventory.load (esm); + + mCreatureStats.load (esm); } void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const @@ -13,4 +15,6 @@ void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const ObjectState::save (esm, inInventory); mInventory.save (esm); + + mCreatureStats.save (esm); } \ No newline at end of file diff --git a/components/esm/creaturestate.hpp b/components/esm/creaturestate.hpp index f7f9b80380..604c2f3a70 100644 --- a/components/esm/creaturestate.hpp +++ b/components/esm/creaturestate.hpp @@ -3,6 +3,7 @@ #include "objectstate.hpp" #include "inventorystate.hpp" +#include "creaturestats.hpp" namespace ESM { @@ -11,6 +12,7 @@ namespace ESM struct CreatureState : public ObjectState { InventoryState mInventory; + CreatureStats mCreatureStats; virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp new file mode 100644 index 0000000000..fe250089aa --- /dev/null +++ b/components/esm/creaturestats.cpp @@ -0,0 +1,20 @@ + +#include "creaturestats.hpp" + +void ESM::CreatureStats::load (ESMReader &esm) +{ + for (int i=0; i<8; ++i) + mAttributes[i].load (esm); + + for (int i=0; i<3; ++i) + mDynamic[i].load (esm); +} + +void ESM::CreatureStats::save (ESMWriter &esm) const +{ + for (int i=0; i<8; ++i) + mAttributes[i].save (esm); + + for (int i=0; i<3; ++i) + mDynamic[i].save (esm); +} \ No newline at end of file diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp new file mode 100644 index 0000000000..540044f389 --- /dev/null +++ b/components/esm/creaturestats.hpp @@ -0,0 +1,27 @@ +#ifndef OPENMW_ESM_CREATURESTATS_H +#define OPENMW_ESM_CREATURESTATS_H + +#include +#include +#include + +#include "statstate.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct CreatureStats + { + StatState mAttributes[8]; + StatState mDynamic[3]; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 1b0125e78b..c1f1679924 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -91,6 +91,7 @@ enum RecNameInts REC_PLAY = 0x59414c50, REC_CSTA = 0x41545343, REC_GMAP = 0x50414d47, + REC_DIAS = 0x53414944, // format 1 REC_FILT = 0x544C4946 diff --git a/components/esm/dialoguestate.cpp b/components/esm/dialoguestate.cpp new file mode 100644 index 0000000000..b3544c85ce --- /dev/null +++ b/components/esm/dialoguestate.cpp @@ -0,0 +1,21 @@ + +#include "dialoguestate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +void ESM::DialogueState::load (ESMReader &esm) +{ + while (esm.isNextSub ("TOPI")) + mKnownTopics.push_back (esm.getHString()); +} + +void ESM::DialogueState::save (ESMWriter &esm) const +{ + for (std::vector::const_iterator iter (mKnownTopics.begin()); + iter!=mKnownTopics.end(); ++iter) + { + esm.writeHNString ("TOPI", *iter); + + } +} \ No newline at end of file diff --git a/components/esm/dialoguestate.hpp b/components/esm/dialoguestate.hpp new file mode 100644 index 0000000000..9aa9eaefd3 --- /dev/null +++ b/components/esm/dialoguestate.hpp @@ -0,0 +1,23 @@ +#ifndef OPENMW_ESM_DIALOGUESTATE_H +#define OPENMW_ESM_DIALOGUESTATE_H + +#include +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct DialogueState + { + std::vector mKnownTopics; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index f38591b7bf..91f123eb71 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -80,8 +80,8 @@ namespace ESM rec.name = name; rec.position = mStream->tellp(); rec.size = 0; - writeT(0); // Size goes here - writeT(0); // Unused header? + writeT(0); // Size goes here + writeT(0); // Unused header? writeT(flags); mRecords.push_back(rec); @@ -105,7 +105,7 @@ namespace ESM rec.name = name; rec.position = mStream->tellp(); rec.size = 0; - writeT(0); // Size goes here + writeT(0); // Size goes here mRecords.push_back(rec); assert(mRecords.back().size == 0); @@ -120,7 +120,7 @@ namespace ESM mStream->seekp(rec.position); mCounting = false; - write (reinterpret_cast (&rec.size), sizeof(int)); + write (reinterpret_cast (&rec.size), sizeof(uint32_t)); mCounting = true; mStream->seekp(0, std::ios::end); diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 94f0a10048..33650e6784 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -17,7 +17,7 @@ class ESMWriter { std::string name; std::streampos position; - size_t size; + uint32_t size; }; public: diff --git a/components/esm/npcstate.cpp b/components/esm/npcstate.cpp index c452611a0a..e59ec3e268 100644 --- a/components/esm/npcstate.cpp +++ b/components/esm/npcstate.cpp @@ -6,6 +6,10 @@ void ESM::NpcState::load (ESMReader &esm) ObjectState::load (esm); mInventory.load (esm); + + mNpcStats.load (esm); + + mCreatureStats.load (esm); } void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const @@ -13,4 +17,8 @@ void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const ObjectState::save (esm, inInventory); mInventory.save (esm); + + mNpcStats.save (esm); + + mCreatureStats.save (esm); } \ No newline at end of file diff --git a/components/esm/npcstate.hpp b/components/esm/npcstate.hpp index ceb18b88b6..39858d5533 100644 --- a/components/esm/npcstate.hpp +++ b/components/esm/npcstate.hpp @@ -3,6 +3,8 @@ #include "objectstate.hpp" #include "inventorystate.hpp" +#include "npcstats.hpp" +#include "creaturestats.hpp" namespace ESM { @@ -11,6 +13,8 @@ namespace ESM struct NpcState : public ObjectState { InventoryState mInventory; + NpcStats mNpcStats; + CreatureStats mCreatureStats; virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp new file mode 100644 index 0000000000..531424ab23 --- /dev/null +++ b/components/esm/npcstats.cpp @@ -0,0 +1,133 @@ + +#include "npcstats.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +ESM::NpcStats::Faction::Faction() : mExpelled (false), mRank (0), mReputation (0) {} + +void ESM::NpcStats::load (ESMReader &esm) +{ + while (esm.isNextSub ("FACT")) + { + std::string id = esm.getHString(); + + Faction faction; + + int expelled = 0; + esm.getHNOT (expelled, "FAEX"); + + if (expelled) + faction.mExpelled = true; + + esm.getHNOT (faction.mRank, "FARA"); + + esm.getHNOT (faction.mReputation, "FARE"); + + mFactions.insert (std::make_pair (id, faction)); + } + + mDisposition = 0; + esm.getHNOT (mDisposition, "DISP"); + + for (int i=0; i<27; ++i) + { + mSkills[i].mRegular.load (esm); + mSkills[i].mWerewolf.load (esm); + } + + mBounty = 0; + esm.getHNOT (mBounty, "BOUN"); + + mReputation = 0; + esm.getHNOT (mReputation, "REPU"); + + mWerewolfKills = 0; + esm.getHNOT (mWerewolfKills, "WKIL"); + + mProfit = 0; + esm.getHNOT (mProfit, "PROF"); + + mAttackStrength = 0; + esm.getHNOT (mAttackStrength, "ASTR"); + + mLevelProgress = 0; + esm.getHNOT (mLevelProgress, "LPRO"); + + esm.getHNT (mSkillIncrease, "INCR"); + + while (esm.isNextSub ("USED")) + mUsedIds.push_back (esm.getHString()); + + mTimeToStartDrowning = 0; + esm.getHNOT (mTimeToStartDrowning, "DRTI"); + + mLastDrowningHit = 0; + esm.getHNOT (mLastDrowningHit, "DRLH"); + + mLevelHealthBonus = 0; + esm.getHNOT (mLevelHealthBonus, "LVLH"); +} + +void ESM::NpcStats::save (ESMWriter &esm) const +{ + for (std::map::const_iterator iter (mFactions.begin()); + iter!=mFactions.end(); ++iter) + { + esm.writeHNString ("FACT", iter->first); + + if (iter->second.mExpelled) + { + int expelled = 1; + esm.writeHNT ("FAEX", expelled); + } + + if (iter->second.mRank) + esm.writeHNT ("FARA", iter->second.mRank); + + if (iter->second.mReputation) + esm.writeHNT ("FARE", iter->second.mReputation); + } + + if (mDisposition) + esm.writeHNT ("DISP", mDisposition); + + for (int i=0; i<27; ++i) + { + mSkills[i].mRegular.save (esm); + mSkills[i].mWerewolf.save (esm); + } + + if (mBounty) + esm.writeHNT ("BOUN", mBounty); + + if (mReputation) + esm.writeHNT ("REPU", mReputation); + + if (mWerewolfKills) + esm.writeHNT ("WKIL", mWerewolfKills); + + if (mProfit) + esm.writeHNT ("PROF", mProfit); + + if (mAttackStrength) + esm.writeHNT ("ASTR", mAttackStrength); + + if (mLevelProgress) + esm.writeHNT ("LPRO", mLevelProgress); + + esm.writeHNT ("INCR", mSkillIncrease); + + for (std::vector::const_iterator iter (mUsedIds.begin()); iter!=mUsedIds.end(); + ++iter) + esm.writeHNT ("USED", *iter); + + if (mTimeToStartDrowning) + esm.writeHNT ("DRTI", mTimeToStartDrowning); + + if (mLastDrowningHit) + esm.writeHNT ("DRLH", mLastDrowningHit); + + if (mLevelHealthBonus) + esm.writeHNT ("LVLH", mLevelHealthBonus); +} \ No newline at end of file diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp new file mode 100644 index 0000000000..b3f70db252 --- /dev/null +++ b/components/esm/npcstats.hpp @@ -0,0 +1,54 @@ +#ifndef OPENMW_ESM_NPCSTATS_H +#define OPENMW_ESM_NPCSTATS_H + +#include +#include +#include + +#include "statstate.hpp" + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // format 0, saved games only + + struct NpcStats + { + struct Skill + { + StatState mRegular; + StatState mWerewolf; + }; + + struct Faction + { + bool mExpelled; + int mRank; + int mReputation; + + Faction(); + }; + + std::map mFactions; + int mDisposition; + Skill mSkills[27]; + int mBounty; + int mReputation; + int mWerewolfKills; + int mProfit; + float mAttackStrength; + int mLevelProgress; + int mSkillIncrease[8]; + std::vector mUsedIds; + float mTimeToStartDrowning; + float mLastDrowningHit; + float mLevelHealthBonus; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif \ No newline at end of file diff --git a/components/esm/statstate.hpp b/components/esm/statstate.hpp new file mode 100644 index 0000000000..4b4023bc22 --- /dev/null +++ b/components/esm/statstate.hpp @@ -0,0 +1,59 @@ +#ifndef OPENMW_ESM_STATSTATE_H +#define OPENMW_ESM_STATSTATE_H + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace ESM +{ + // format 0, saved games only + + template + struct StatState + { + T mBase; + T mMod; + T mCurrent; + T mDamage; + float mProgress; + + StatState(); + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; + + template + StatState::StatState() : mBase (0), mMod (0), mCurrent (0), mDamage (0), mProgress (0) {} + + template + void StatState::load (ESMReader &esm) + { + esm.getHNT (mBase, "STBA"); + esm.getHNT (mMod, "STMO"); + mCurrent = 0; + esm.getHNOT (mCurrent, "STCU"); + mDamage = 0; + esm.getHNOT (mDamage, "STDA"); + mProgress = 0; + esm.getHNOT (mProgress, "STPR"); + } + + template + void StatState::save (ESMWriter &esm) const + { + esm.writeHNT ("STBA", mBase); + esm.writeHNT ("STMO", mMod); + + if (mCurrent) + esm.writeHNT ("STCU", mCurrent); + + if (mDamage) + esm.writeHNT ("STDA", mDamage); + + if (mProgress) + esm.writeHNT ("STPR", mProgress); + } +} + +#endif \ No newline at end of file diff --git a/components/interpreter/context.hpp b/components/interpreter/context.hpp index bdba7b6af9..97e4fad4fc 100644 --- a/components/interpreter/context.hpp +++ b/components/interpreter/context.hpp @@ -50,33 +50,33 @@ namespace Interpreter virtual void setGlobalFloat (const std::string& name, float value) = 0; virtual std::vector getGlobals () const = 0; - + virtual char getGlobalType (const std::string& name) const = 0; virtual std::string getActionBinding(const std::string& action) const = 0; - + virtual std::string getNPCName() const = 0; - + virtual std::string getNPCRace() const = 0; - + virtual std::string getNPCClass() const = 0; - + virtual std::string getNPCFaction() const = 0; - + virtual std::string getNPCRank() const = 0; virtual std::string getPCName() const = 0; - + virtual std::string getPCRace() const = 0; - + virtual std::string getPCClass() const = 0; - + virtual std::string getPCRank() const = 0; - + virtual std::string getPCNextRank() const = 0; - + virtual int getPCBounty() const = 0; - + virtual std::string getCurrentCellName() const = 0; virtual bool isScriptRunning (const std::string& name) const = 0; @@ -96,17 +96,17 @@ namespace Interpreter virtual void disable (const std::string& id = "") = 0; - virtual int getMemberShort (const std::string& id, const std::string& name) const = 0; + virtual int getMemberShort (const std::string& id, const std::string& name, bool global) const = 0; - virtual int getMemberLong (const std::string& id, const std::string& name) const = 0; + virtual int getMemberLong (const std::string& id, const std::string& name, bool global) const = 0; - virtual float getMemberFloat (const std::string& id, const std::string& name) const = 0; + virtual float getMemberFloat (const std::string& id, const std::string& name, bool global) const = 0; - virtual void setMemberShort (const std::string& id, const std::string& name, int value) = 0; + virtual void setMemberShort (const std::string& id, const std::string& name, int value, bool global) = 0; - virtual void setMemberLong (const std::string& id, const std::string& name, int value) = 0; + virtual void setMemberLong (const std::string& id, const std::string& name, int value, bool global) = 0; - virtual void setMemberFloat (const std::string& id, const std::string& name, float value) + virtual void setMemberFloat (const std::string& id, const std::string& name, float value, bool global) = 0; }; } diff --git a/components/interpreter/docs/vmformat.txt b/components/interpreter/docs/vmformat.txt index 91e0c060e7..990762268f 100644 --- a/components/interpreter/docs/vmformat.txt +++ b/components/interpreter/docs/vmformat.txt @@ -127,5 +127,11 @@ op 61: store stack[0] in member float stack[2] of object with ID stack[1] op 62: replace stack[0] with member short stack[1] of object with ID stack[0] op 63: replace stack[0] with member short stack[1] of object with ID stack[0] op 64: replace stack[0] with member short stack[1] of object with ID stack[0] -opcodes 65-33554431 unused +op 65: store stack[0] in member short stack[2] of global script with ID stack[1] +op 66: store stack[0] in member long stack[2] of global script with ID stack[1] +op 67: store stack[0] in member float stack[2] of global script with ID stack[1] +op 68: replace stack[0] with member short stack[1] of global script with ID stack[0] +op 69: replace stack[0] with member short stack[1] of global script with ID stack[0] +op 70: replace stack[0] with member short stack[1] of global script with ID stack[0] +opcodes 71-33554431 unused opcodes 33554432-67108863 reserved for extensions diff --git a/components/interpreter/installopcodes.cpp b/components/interpreter/installopcodes.cpp index 05f71f1cca..721cde3d8d 100644 --- a/components/interpreter/installopcodes.cpp +++ b/components/interpreter/installopcodes.cpp @@ -40,12 +40,18 @@ namespace Interpreter interpreter.installSegment5 (42, new OpFetchGlobalShort); interpreter.installSegment5 (43, new OpFetchGlobalLong); interpreter.installSegment5 (44, new OpFetchGlobalFloat); - interpreter.installSegment5 (59, new OpStoreMemberShort); - interpreter.installSegment5 (60, new OpStoreMemberLong); - interpreter.installSegment5 (61, new OpStoreMemberFloat); - interpreter.installSegment5 (62, new OpFetchMemberShort); - interpreter.installSegment5 (63, new OpFetchMemberLong); - interpreter.installSegment5 (64, new OpFetchMemberFloat); + interpreter.installSegment5 (59, new OpStoreMemberShort (false)); + interpreter.installSegment5 (60, new OpStoreMemberLong (false)); + interpreter.installSegment5 (61, new OpStoreMemberFloat (false)); + interpreter.installSegment5 (62, new OpFetchMemberShort (false)); + interpreter.installSegment5 (63, new OpFetchMemberLong (false)); + interpreter.installSegment5 (64, new OpFetchMemberFloat (false)); + interpreter.installSegment5 (65, new OpStoreMemberShort (true)); + interpreter.installSegment5 (66, new OpStoreMemberLong (true)); + interpreter.installSegment5 (67, new OpStoreMemberFloat (true)); + interpreter.installSegment5 (68, new OpFetchMemberShort (true)); + interpreter.installSegment5 (69, new OpFetchMemberLong (true)); + interpreter.installSegment5 (70, new OpFetchMemberFloat (true)); // math interpreter.installSegment5 (9, new OpAddInt); diff --git a/components/interpreter/localopcodes.hpp b/components/interpreter/localopcodes.hpp index 731c16276d..7844a9ea73 100644 --- a/components/interpreter/localopcodes.hpp +++ b/components/interpreter/localopcodes.hpp @@ -208,8 +208,12 @@ namespace Interpreter class OpStoreMemberShort : public Opcode0 { + bool mGlobal; + public: + OpStoreMemberShort (bool global) : mGlobal (global) {} + virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; @@ -218,7 +222,7 @@ namespace Interpreter index = runtime[2].mInteger; std::string variable = runtime.getStringLiteral (index); - runtime.getContext().setMemberShort (id, variable, data); + runtime.getContext().setMemberShort (id, variable, data, mGlobal); runtime.pop(); runtime.pop(); @@ -228,8 +232,12 @@ namespace Interpreter class OpStoreMemberLong : public Opcode0 { + bool mGlobal; + public: + OpStoreMemberLong (bool global) : mGlobal (global) {} + virtual void execute (Runtime& runtime) { Type_Integer data = runtime[0].mInteger; @@ -238,7 +246,7 @@ namespace Interpreter index = runtime[2].mInteger; std::string variable = runtime.getStringLiteral (index); - runtime.getContext().setMemberLong (id, variable, data); + runtime.getContext().setMemberLong (id, variable, data, mGlobal); runtime.pop(); runtime.pop(); @@ -248,8 +256,12 @@ namespace Interpreter class OpStoreMemberFloat : public Opcode0 { + bool mGlobal; + public: + OpStoreMemberFloat (bool global) : mGlobal (global) {} + virtual void execute (Runtime& runtime) { Type_Float data = runtime[0].mFloat; @@ -258,7 +270,7 @@ namespace Interpreter index = runtime[2].mInteger; std::string variable = runtime.getStringLiteral (index); - runtime.getContext().setMemberFloat (id, variable, data); + runtime.getContext().setMemberFloat (id, variable, data, mGlobal); runtime.pop(); runtime.pop(); @@ -268,8 +280,12 @@ namespace Interpreter class OpFetchMemberShort : public Opcode0 { + bool mGlobal; + public: + OpFetchMemberShort (bool global) : mGlobal (global) {} + virtual void execute (Runtime& runtime) { Type_Integer index = runtime[0].mInteger; @@ -278,15 +294,19 @@ namespace Interpreter std::string variable = runtime.getStringLiteral (index); runtime.pop(); - int value = runtime.getContext().getMemberShort (id, variable); + int value = runtime.getContext().getMemberShort (id, variable, mGlobal); runtime[0].mInteger = value; } }; class OpFetchMemberLong : public Opcode0 { + bool mGlobal; + public: + OpFetchMemberLong (bool global) : mGlobal (global) {} + virtual void execute (Runtime& runtime) { Type_Integer index = runtime[0].mInteger; @@ -295,15 +315,19 @@ namespace Interpreter std::string variable = runtime.getStringLiteral (index); runtime.pop(); - int value = runtime.getContext().getMemberLong (id, variable); + int value = runtime.getContext().getMemberLong (id, variable, mGlobal); runtime[0].mInteger = value; } }; class OpFetchMemberFloat : public Opcode0 { + bool mGlobal; + public: + OpFetchMemberFloat (bool global) : mGlobal (global) {} + virtual void execute (Runtime& runtime) { Type_Integer index = runtime[0].mInteger; @@ -312,7 +336,7 @@ namespace Interpreter std::string variable = runtime.getStringLiteral (index); runtime.pop(); - float value = runtime.getContext().getMemberFloat (id, variable); + float value = runtime.getContext().getMemberFloat (id, variable, mGlobal); runtime[0].mFloat = value; } }; diff --git a/components/ogreinit/ogreinit.cpp b/components/ogreinit/ogreinit.cpp index c8fc621c78..840cf4bb05 100644 --- a/components/ogreinit/ogreinit.cpp +++ b/components/ogreinit/ogreinit.cpp @@ -6,6 +6,10 @@ #include #include +#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE +#include +#endif + #include #include "ogreplugin.hpp" @@ -127,6 +131,9 @@ namespace OgreInit #endif #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE pluginDir = OGRE_PLUGIN_DIR; + // if path is not specified try to find plugins inside the app bundle + if (pluginDir.empty()) + pluginDir = Ogre::macFrameworksPath(); #endif #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX pluginDir = OGRE_PLUGIN_DIR_REL; diff --git a/components/ogreinit/ogreplugin.cpp b/components/ogreinit/ogreplugin.cpp index c319f77589..6070c43a87 100644 --- a/components/ogreinit/ogreplugin.cpp +++ b/components/ogreinit/ogreplugin.cpp @@ -6,18 +6,12 @@ namespace Files { bool loadOgrePlugin(const std::string &pluginDir, std::string pluginName, Ogre::Root &ogreRoot) { -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - std::ostringstream verStream; - verStream << "." << OGRE_VERSION_MAJOR << "." << OGRE_VERSION_MINOR << "." << OGRE_VERSION_PATCH; - pluginName = pluginName + verStream.str(); -#endif - std::string pluginExt; #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 pluginExt = ".dll"; #endif #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - pluginExt = ".dylib"; + pluginExt = ".framework"; #endif #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX pluginExt = ".so"; diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index a4fdd5e13f..82ccc7c89a 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -168,7 +168,8 @@ QuadTreeNode::QuadTreeNode(World* terrain, ChildDirection dir, float size, const if (mParent) pos = mParent->getCenter(); pos = mCenter - pos; - mSceneNode->setPosition(Ogre::Vector3(pos.x*8192, pos.y*8192, 0)); + float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); + mSceneNode->setPosition(Ogre::Vector3(pos.x*cellWorldSize, pos.y*cellWorldSize, 0)); mMaterialGenerator = new MaterialGenerator(mTerrain->getShadersEnabled()); } @@ -203,6 +204,7 @@ void QuadTreeNode::initNeighbours() void QuadTreeNode::initAabb() { + float cellWorldSize = mTerrain->getStorage()->getCellWorldSize(); if (hasChildren()) { for (int i=0; i<4; ++i) @@ -210,11 +212,11 @@ void QuadTreeNode::initAabb() mChildren[i]->initAabb(); mBounds.merge(mChildren[i]->getBoundingBox()); } - mBounds = Ogre::AxisAlignedBox (Ogre::Vector3(-mSize/2*8192, -mSize/2*8192, mBounds.getMinimum().z), - Ogre::Vector3(mSize/2*8192, mSize/2*8192, mBounds.getMaximum().z)); + mBounds = Ogre::AxisAlignedBox (Ogre::Vector3(-mSize/2*cellWorldSize, -mSize/2*cellWorldSize, mBounds.getMinimum().z), + Ogre::Vector3(mSize/2*cellWorldSize, mSize/2*cellWorldSize, mBounds.getMaximum().z)); } - mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0), - mBounds.getMaximum() + Ogre::Vector3(mCenter.x*8192, mCenter.y*8192, 0)); + mWorldBounds = Ogre::AxisAlignedBox(mBounds.getMinimum() + Ogre::Vector3(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0), + mBounds.getMaximum() + Ogre::Vector3(mCenter.x*cellWorldSize, mCenter.y*cellWorldSize, 0)); } void QuadTreeNode::setBoundingBox(const Ogre::AxisAlignedBox &box) diff --git a/components/terrain/storage.hpp b/components/terrain/storage.hpp index 021e01c7e0..d8cdab9ec1 100644 --- a/components/terrain/storage.hpp +++ b/components/terrain/storage.hpp @@ -1,9 +1,6 @@ #ifndef COMPONENTS_TERRAIN_STORAGE_H #define COMPONENTS_TERRAIN_STORAGE_H -#include -#include - #include #include @@ -27,7 +24,7 @@ namespace Terrain public: /// Get bounds of the whole terrain in cell units - virtual Ogre::AxisAlignedBox getBounds() = 0; + virtual void getBounds(float& minX, float& maxX, float& minY, float& maxY) = 0; /// Get the minimum and maximum heights of a terrain chunk. /// @note Should only be called for chunks <= 1 cell, i.e. leafs of the quad tree. diff --git a/components/terrain/world.cpp b/components/terrain/world.cpp index dac960fbb6..4273f227d6 100644 --- a/components/terrain/world.cpp +++ b/components/terrain/world.cpp @@ -62,6 +62,10 @@ namespace Terrain , mShaders(shaders) , mVisible(true) , mLoadingListener(loadingListener) + , mMaxX(0) + , mMinX(0) + , mMaxY(0) + , mMinY(0) { loadingListener->setLabel("Creating terrain"); loadingListener->indicateProgress(); @@ -76,20 +80,21 @@ namespace Terrain mCompositeMapRenderTarget->setAutoUpdated(false); mCompositeMapRenderTarget->addViewport(compositeMapCam); - mBounds = storage->getBounds(); + storage->getBounds(mMinX, mMaxX, mMinY, mMaxY); - int origSizeX = mBounds.getSize().x; - int origSizeY = mBounds.getSize().y; + int origSizeX = mMaxX-mMinX; + int origSizeY = mMaxY-mMinY; // Dividing a quad tree only works well for powers of two, so round up to the nearest one int size = nextPowerOfTwo(std::max(origSizeX, origSizeY)); // Adjust the center according to the new size - Ogre::Vector3 center = mBounds.getCenter() + Ogre::Vector3((size-origSizeX)/2.f, (size-origSizeY)/2.f, 0); + float centerX = (mMinX+mMaxX)/2.f + (size-origSizeX)/2.f; + float centerY = (mMinY+mMaxY)/2.f + (size-origSizeY)/2.f; mRootSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); - mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(center.x, center.y), NULL); + mRootNode = new QuadTreeNode(this, Root, size, Ogre::Vector2(centerX, centerY), NULL); buildQuadTree(mRootNode); loadingListener->indicateProgress(); mRootNode->initAabb(); @@ -113,18 +118,19 @@ namespace Terrain // We arrived at a leaf float minZ,maxZ; Ogre::Vector2 center = node->getCenter(); + float cellWorldSize = getStorage()->getCellWorldSize(); if (mStorage->getMinMaxHeights(node->getSize(), center, minZ, maxZ)) - node->setBoundingBox(Ogre::AxisAlignedBox(Ogre::Vector3(-halfSize*8192, -halfSize*8192, minZ), - Ogre::Vector3(halfSize*8192, halfSize*8192, maxZ))); + node->setBoundingBox(Ogre::AxisAlignedBox(Ogre::Vector3(-halfSize*cellWorldSize, -halfSize*cellWorldSize, minZ), + Ogre::Vector3(halfSize*cellWorldSize, halfSize*cellWorldSize, maxZ))); else node->markAsDummy(); // no data available for this node, skip it return; } - if (node->getCenter().x - halfSize > mBounds.getMaximum().x - || node->getCenter().x + halfSize < mBounds.getMinimum().x - || node->getCenter().y - halfSize > mBounds.getMaximum().y - || node->getCenter().y + halfSize < mBounds.getMinimum().y ) + if (node->getCenter().x - halfSize > mMaxX + || node->getCenter().x + halfSize < mMinX + || node->getCenter().y - halfSize > mMaxY + || node->getCenter().y + halfSize < mMinY ) // Out of bounds of the actual terrain - this will happen because // we rounded the size up to the next power of two { @@ -161,15 +167,16 @@ namespace Terrain Ogre::AxisAlignedBox World::getWorldBoundingBox (const Ogre::Vector2& center) { - if (center.x > mBounds.getMaximum().x - || center.x < mBounds.getMinimum().x - || center.y > mBounds.getMaximum().y - || center.y < mBounds.getMinimum().y) + if (center.x > mMaxX + || center.x < mMinX + || center.y > mMaxY + || center.y < mMinY) return Ogre::AxisAlignedBox::BOX_NULL; QuadTreeNode* node = findNode(center, mRootNode); Ogre::AxisAlignedBox box = node->getBoundingBox(); - box.setExtents(box.getMinimum() + Ogre::Vector3(center.x, center.y, 0) * 8192, - box.getMaximum() + Ogre::Vector3(center.x, center.y, 0) * 8192); + float cellWorldSize = getStorage()->getCellWorldSize(); + box.setExtents(box.getMinimum() + Ogre::Vector3(center.x, center.y, 0) * cellWorldSize, + box.getMaximum() + Ogre::Vector3(center.x, center.y, 0) * cellWorldSize); return box; } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index b8c1b0a7d6..bf733b889a 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -105,7 +105,7 @@ namespace Terrain Ogre::SceneManager* mCompositeMapSceneMgr; /// Bounds in cell units - Ogre::AxisAlignedBox mBounds; + float mMinX, mMaxX, mMinY, mMaxY; /// Minimum size of a terrain batch along one side (in cell units) float mMinBatchSize; diff --git a/credits.txt b/credits.txt index 561931cde9..6012557638 100644 --- a/credits.txt +++ b/credits.txt @@ -20,6 +20,7 @@ Artem Kotsynyak (greye) athile Britt Mathis (galdor557) BrotherBrick +cc9cii Chris Robinson (KittyCat) Cory F. Cohen (cfcohen) Cris Mihalache (Mirceam) diff --git a/extern/oics/ICSInputControlSystem.h b/extern/oics/ICSInputControlSystem.h index 907cba5fc0..a83ae539ef 100644 --- a/extern/oics/ICSInputControlSystem.h +++ b/extern/oics/ICSInputControlSystem.h @@ -102,19 +102,19 @@ namespace ICS JoystickIDList& getJoystickIdList(){ return mJoystickIDList; }; // MouseListener - bool mouseMoved(const SFO::MouseMotionEvent &evt); - bool mousePressed(const SDL_MouseButtonEvent &evt, Uint8); - bool mouseReleased(const SDL_MouseButtonEvent &evt, Uint8); + void mouseMoved(const SFO::MouseMotionEvent &evt); + void mousePressed(const SDL_MouseButtonEvent &evt, Uint8); + void mouseReleased(const SDL_MouseButtonEvent &evt, Uint8); // KeyListener - bool keyPressed(const SDL_KeyboardEvent &evt); - bool keyReleased(const SDL_KeyboardEvent &evt); + void keyPressed(const SDL_KeyboardEvent &evt); + void keyReleased(const SDL_KeyboardEvent &evt); // JoyStickListener - bool buttonPressed(const SDL_JoyButtonEvent &evt, int button); - bool buttonReleased(const SDL_JoyButtonEvent &evt, int button); - bool axisMoved(const SDL_JoyAxisEvent &evt, int axis); - bool povMoved(const SDL_JoyHatEvent &evt, int index); + void buttonPressed(const SDL_JoyButtonEvent &evt, int button); + void buttonReleased(const SDL_JoyButtonEvent &evt, int button); + void axisMoved(const SDL_JoyAxisEvent &evt, int axis); + void povMoved(const SDL_JoyHatEvent &evt, int index); //TODO: does this have an SDL equivalent? //bool sliderMoved(const OIS::JoyStickEvent &evt, int index); diff --git a/extern/oics/ICSInputControlSystem_joystick.cpp b/extern/oics/ICSInputControlSystem_joystick.cpp index 8e501d5018..21adc9f74a 100644 --- a/extern/oics/ICSInputControlSystem_joystick.cpp +++ b/extern/oics/ICSInputControlSystem_joystick.cpp @@ -318,7 +318,7 @@ namespace ICS } // joyStick listeners - bool InputControlSystem::buttonPressed(const SDL_JoyButtonEvent &evt, int button) + void InputControlSystem::buttonPressed(const SDL_JoyButtonEvent &evt, int button) { if(mActive) { @@ -354,11 +354,9 @@ namespace ICS mDetectingBindingControl, evt.which, button, mDetectingBindingDirection); } } - - return true; } - bool InputControlSystem::buttonReleased(const SDL_JoyButtonEvent &evt, int button) + void InputControlSystem::buttonReleased(const SDL_JoyButtonEvent &evt, int button) { if(mActive) { @@ -371,10 +369,9 @@ namespace ICS } } } - return true; } - bool InputControlSystem::axisMoved(const SDL_JoyAxisEvent &evt, int axis) + void InputControlSystem::axisMoved(const SDL_JoyAxisEvent &evt, int axis) { if(mActive) { @@ -417,12 +414,10 @@ namespace ICS } } } - - return true; } //Here be dragons, apparently - bool InputControlSystem::povMoved(const SDL_JoyHatEvent &evt, int index) + void InputControlSystem::povMoved(const SDL_JoyHatEvent &evt, int index) { if(mActive) { @@ -542,13 +537,11 @@ namespace ICS } } } - - return true; } //TODO: does this have an SDL equivalent? /* - bool InputControlSystem::sliderMoved(const OIS::JoyStickEvent &evt, int index) + void InputControlSystem::sliderMoved(const OIS::JoyStickEvent &evt, int index) { if(mActive) { @@ -590,8 +583,6 @@ namespace ICS } } } - - return true; } */ diff --git a/extern/oics/ICSInputControlSystem_keyboard.cpp b/extern/oics/ICSInputControlSystem_keyboard.cpp index 01d68f7843..0a9a34d63c 100644 --- a/extern/oics/ICSInputControlSystem_keyboard.cpp +++ b/extern/oics/ICSInputControlSystem_keyboard.cpp @@ -85,7 +85,7 @@ namespace ICS return SDLK_UNKNOWN; } - bool InputControlSystem::keyPressed(const SDL_KeyboardEvent &evt) + void InputControlSystem::keyPressed(const SDL_KeyboardEvent &evt) { if(mActive) { @@ -118,11 +118,9 @@ namespace ICS mDetectingBindingControl, evt.keysym.sym, mDetectingBindingDirection); } } - - return true; - } + } - bool InputControlSystem::keyReleased(const SDL_KeyboardEvent &evt) + void InputControlSystem::keyReleased(const SDL_KeyboardEvent &evt) { if(mActive) { @@ -132,8 +130,6 @@ namespace ICS it->second.control->setChangingDirection(Control::STOP); } } - - return true; } void DetectingBindingListener::keyBindingDetected(InputControlSystem* ICS, Control* control diff --git a/extern/oics/ICSInputControlSystem_mouse.cpp b/extern/oics/ICSInputControlSystem_mouse.cpp index 52eb894ed5..be18ebbc0e 100644 --- a/extern/oics/ICSInputControlSystem_mouse.cpp +++ b/extern/oics/ICSInputControlSystem_mouse.cpp @@ -219,7 +219,7 @@ namespace ICS } // mouse Listeners - bool InputControlSystem::mouseMoved(const SFO::MouseMotionEvent& evt) + void InputControlSystem::mouseMoved(const SFO::MouseMotionEvent& evt) { if(mActive) { @@ -304,11 +304,9 @@ namespace ICS } } } - - return true; } - bool InputControlSystem::mousePressed(const SDL_MouseButtonEvent &evt, Uint8 btn) + void InputControlSystem::mousePressed(const SDL_MouseButtonEvent &evt, Uint8 btn) { if(mActive) { @@ -341,11 +339,9 @@ namespace ICS mDetectingBindingControl, btn, mDetectingBindingDirection); } } - - return true; } - bool InputControlSystem::mouseReleased(const SDL_MouseButtonEvent &evt, Uint8 btn) + void InputControlSystem::mouseReleased(const SDL_MouseButtonEvent &evt, Uint8 btn) { if(mActive) { @@ -355,8 +351,6 @@ namespace ICS it->second.control->setChangingDirection(Control::STOP); } } - - return true; } // mouse auto bindings diff --git a/extern/sdl4ogre/events.h b/extern/sdl4ogre/events.h index 48adb45456..0fb4d6f060 100644 --- a/extern/sdl4ogre/events.h +++ b/extern/sdl4ogre/events.h @@ -26,9 +26,9 @@ class MouseListener { public: virtual ~MouseListener() {} - virtual bool mouseMoved( const MouseMotionEvent &arg ) = 0; - virtual bool mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; - virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; + virtual void mouseMoved( const MouseMotionEvent &arg ) = 0; + virtual void mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; + virtual void mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) = 0; }; class KeyListener @@ -36,8 +36,8 @@ class KeyListener public: virtual ~KeyListener() {} virtual void textInput (const SDL_TextInputEvent& arg) {} - virtual bool keyPressed(const SDL_KeyboardEvent &arg) = 0; - virtual bool keyReleased(const SDL_KeyboardEvent &arg) = 0; + virtual void keyPressed(const SDL_KeyboardEvent &arg) = 0; + virtual void keyReleased(const SDL_KeyboardEvent &arg) = 0; }; class JoyListener @@ -45,18 +45,18 @@ class JoyListener public: virtual ~JoyListener() {} /** @remarks Joystick button down event */ - virtual bool buttonPressed( const SDL_JoyButtonEvent &evt, int button ) = 0; + virtual void buttonPressed( const SDL_JoyButtonEvent &evt, int button ) = 0; /** @remarks Joystick button up event */ - virtual bool buttonReleased( const SDL_JoyButtonEvent &evt, int button ) = 0; + virtual void buttonReleased( const SDL_JoyButtonEvent &evt, int button ) = 0; /** @remarks Joystick axis moved event */ - virtual bool axisMoved( const SDL_JoyAxisEvent &arg, int axis ) = 0; + virtual void axisMoved( const SDL_JoyAxisEvent &arg, int axis ) = 0; //-- Not so common control events, so are not required --// //! Joystick Event, and povID - virtual bool povMoved( const SDL_JoyHatEvent &arg, int index) {return true;} + virtual void povMoved( const SDL_JoyHatEvent &arg, int index) {} }; class WindowListener diff --git a/files/opencs/multitype.png b/files/opencs/multitype.png new file mode 100644 index 0000000000..05676e2de0 Binary files /dev/null and b/files/opencs/multitype.png differ diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc index eadcf96977..2b1e65ff03 100644 --- a/files/opencs/resources.qrc +++ b/files/opencs/resources.qrc @@ -56,6 +56,7 @@ spell.png static.png weapon.png + multitype.png raster/startup/big/create-addon.png raster/startup/big/new-game.png raster/startup/big/edit-content.png diff --git a/readme.txt b/readme.txt index 6b388dc724..a23cd1077e 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.27.0 +Version: 0.28.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org @@ -48,42 +48,48 @@ Allowed options: --version print version information and quit --data arg (=data) set data directories (later directories have higher priority) - --data-local arg set local data directory (highest + --data-local arg set local data directory (highest priority) --fallback-archive arg (=fallback-archive) - set fallback BSA archives (later + set fallback BSA archives (later archives have higher priority) --resources arg (=resources) set resources directory --start arg (=Beshara) set initial cell - --content arg content file(s): esm/esp, or + --content arg content file(s): esm/esp, or omwgame/omwaddon --anim-verbose [=arg(=1)] (=0) output animation indices files --no-sound [=arg(=1)] (=0) disable all sounds --script-verbose [=arg(=1)] (=0) verbose script output --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scripts) at startup - --script-console [=arg(=1)] (=0) enable console-only script + --script-console [=arg(=1)] (=0) enable console-only script functionality - --script-run arg select a file containing a list of - console commands that is executed on + --script-run arg select a file containing a list of + console commands that is executed on startup - --new-game [=arg(=1)] (=0) activate char gen/new game mechanics - --fs-strict [=arg(=1)] (=0) strict file system handling (no case + --script-warn [=arg(=1)] (=1) handling of warnings when compiling + scripts + 0 - ignore warning + 1 - show warning but consider script as + correctly compiled anyway + 2 - treat warnings as errors + --skip-menu [=arg(=1)] (=0) skip main menu on game startup + --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding) - --encoding arg (=win1252) Character encoding used in OpenMW game + --encoding arg (=win1252) Character encoding used in OpenMW game messages: - - win1250 - Central and Eastern European - such as Polish, Czech, Slovak, - Hungarian, Slovene, Bosnian, Croatian, - Serbian (Latin script), Romanian and + + win1250 - Central and Eastern European + such as Polish, Czech, Slovak, + Hungarian, Slovene, Bosnian, Croatian, + Serbian (Latin script), Romanian and Albanian languages - - win1251 - Cyrillic alphabet such as - Russian, Bulgarian, Serbian Cyrillic + + win1251 - Cyrillic alphabet such as + Russian, Bulgarian, Serbian Cyrillic and other languages - - win1252 - Western European (Latin) + + win1252 - Western European (Latin) alphabet, used by default --fallback arg fallback values --no-grab Don't grab mouse cursor @@ -91,6 +97,160 @@ Allowed options: CHANGELOG +0.29.0 + +Bug #556: Video soundtrack not played when music volume is set to zero +Bug #829: OpenMW uses up all available vram, when playing for extended time +Bug #848: Wrong amount of footsteps playing in 1st person +Bug #888: Ascended Sleepers have movement issues +Bug #892: Explicit references are allowed on all script functions +Bug #999: Graphic Herbalism (mod): sometimes doesn't activate properly +Bug #1009: Lake Fjalding AI related slowdown. +Bug #1041: Music playback issues on OS X >= 10.9 +Bug #1043: No message box when advancing skill "Speechcraft" while in dialog window +Bug #1060: Some message boxes are cut off at the bottom +Bug #1062: Bittercup script does not work ('end' variable) +Bug #1074: Inventory paperdoll obscures armour rating +Bug #1077: Message after killing an essential NPC disappears too fast +Bug #1078: "Clutterbane" shows empty charge bar +Bug #1083: UndoWerewolf fails +Bug #1088: Better Clothes Bloodmoon Plus 1.5 by Spirited Treasure pants are not rendered +Bug #1090: Start scripts fail when going to a non-predefined cell +Bug #1091: Crash: Assertion `!q.isNaN() && "Invalid orientation supplied as parameter"' failed. +Bug #1093: Weapons of aggressive NPCs are invisible after you exit and re-enter interior +Bug #1105: Magicka is depleted when using uncastable spells +Bug #1106: Creatures should be able to run +Bug #1107: TR cliffs have way too huge collision boxes in OpenMW +Bug #1109: Cleaning True Light and Darkness with Tes3cmd makes Addamasartus , Zenarbael and Yasamsi flooded. +Bug #1114: Bad output for desktop-file-validate on openmw.desktop (and opencs.desktop) +Bug #1115: Memory leak when spying on Fargoth +Bug #1137: Script execution fails (drenSlaveOwners script) +Bug #1143: Mehra Milo quest (vivec informants) is broken +Bug #1145: Issues with moving gold between inventory and containers +Bug #1146: Issues with picking up stacks of gold +Bug #1147: Dwemer Crossbows are held incorrectly +Bug #1158: Armor rating should always stay below inventory mannequin +Bug #1159: Quick keys can be set during character generation +Bug #1160: Crash on equip lockpick when +Bug #1167: Editor: Referenceables are not correctly loaded when dealing with more than one content file +Feature #30: Loading/Saving (still missing a few parts) +Feature #101: AI Package: Activate +Feature #103: AI Package: Follow, FollowCell +Feature #138: Editor: Drag & Drop +Feature #428: Player death +Feature #505: Editor: Record Cloning +Feature #701: Levelled creatures +Feature #708: Improved Local Variable handling +Feature #709: Editor: Script verifier +Feature #764: Missing journal backend features +Feature #777: Creature weapons/shields +Feature #789: Editor: Referenceable record verifier +Feature #924: Load/Save GUI (still missing loading screen and progress bars) +Feature #946: Knockdown +Feature #947: Decrease fatigue when running, swimming and attacking +Feature #956: Melee Combat: Blocking +Feature #957: Area magic +Feature #960: Combat/AI combat for creatures +Feature #962: Combat-Related AI instructions +Feature #1075: Damage/Restore skill/attribute magic effects +Feature #1076: Soultrap magic effect +Feature #1081: Disease contraction +Feature #1086: Blood particles +Feature #1092: Interrupt resting +Feature #1101: Inventory equip scripts +Feature #1116: Version/Build number in Launcher window +Feature #1119: Resistance/weakness to normal weapons magic effect +Feature #1123: Slow Fall magic effect +Feature #1130: Auto-calculate spells +Feature #1164: Editor: Case-insensitive sorting in tables + +0.28.0 + +Bug #399: Inventory changes are not visible immediately +Bug #417: Apply weather instantly when teleporting +Bug #566: Global Map position marker not updated for interior cells +Bug #712: Looting corpse delay +Bug #716: Problem with the "Vurt's Ascadian Isles Mod" mod +Bug #805: Two TR meshes appear black (v0.24RC) +Bug #841: Third-person activation distance taken from camera rather than head +Bug #845: NPCs hold torches during the day +Bug #855: Vvardenfell Visages Volume I some hairs don´t appear since 0,24 +Bug #856: Maormer race by Mac Kom - The heads are way up +Bug #864: Walk locks during loading in 3rd person +Bug #871: active weapon/magic item icon is not immediately made blank if item is removed during dialog +Bug #882: Hircine's Ring doesn't always work +Bug #909: [Tamriel Rebuilt] crashes in Akamora +Bug #922: Launcher writing merged openmw.cfg files +Bug #943: Random magnitude should be calculated per effect +Bug #948: Negative fatigue level should be allowed +Bug #949: Particles in world space +Bug #950: Hard crash on x64 Linux running --new-game (on startup) +Bug #951: setMagicka and setFatigue have no effect +Bug #954: Problem with equipping inventory items when using a keyboard shortcut +Bug #955: Issues with equipping torches +Bug #966: Shield is visible when casting spell +Bug #967: Game crashes when equipping silver candlestick +Bug #970: Segmentation fault when starting at Bal Isra +Bug #977: Pressing down key in console doesn't go forward in history +Bug #979: Tooltip disappears when changing inventory +Bug #980: Barter: item category is remembered, but not shown +Bug #981: Mod: replacing model has wrong position/orientation +Bug #982: Launcher: Addon unchecking is not saved +Bug #983: Fix controllers to affect objects attached to the base node +Bug #985: Player can talk to NPCs who are in combat +Bug #989: OpenMW crashes when trying to include mod with capital .ESP +Bug #991: Merchants equip items with harmful constant effect enchantments +Bug #994: Don't cap skills/attributes when set via console +Bug #998: Setting the max health should also set the current health +Bug #1005: Torches are visible when casting spells and during hand to hand combat. +Bug #1006: Many NPCs have 0 skill +Bug #1007: Console fills up with text +Bug #1013: Player randomly loses health or dies +Bug #1014: Persuasion window is not centered in maximized window +Bug #1015: Player status window scroll state resets on status change +Bug #1016: Notification window not big enough for all skill level ups +Bug #1020: Saved window positions are not rescaled appropriately on resolution change +Bug #1022: Messages stuck permanently on screen when they pile up +Bug #1023: Journals doesn't open +Bug #1026: Game loses track of torch usage. +Bug #1028: Crash on pickup of jug in Unexplored Shipwreck, Upper level +Bug #1029: Quick keys menu: Select compatible replacement when tool used up +Bug #1042: TES3 header data wrong encoding +Bug #1045: OS X: deployed OpenCS won't launch +Bug #1046: All damaged weaponry is worth 1 gold +Bug #1048: Links in "locked" dialogue are still clickable +Bug #1052: Using color codes when naming your character actually changes the name's color +Bug #1054: Spell effects not visible in front of water +Bug #1055: Power-Spell animation starts even though you already casted it that day +Bug #1059: Cure disease potion removes all effects from player, even your race bonus and race ability +Bug #1063: Crash upon checking out game start ship area in Seyda Neen +Bug #1064: openmw binaries link to unnecessary libraries +Bug #1065: Landing from a high place in water still causes fall damage +Bug #1072: Drawing weapon increases torch brightness +Bug #1073: Merchants sell stacks of gold +Feature #43: Visuals for Magic Effects +Feature #51: Ranged Magic +Feature #52: Touch Range Magic +Feature #53: Self Range Magic +Feature #54: Spell Casting +Feature #70: Vampirism +Feature #100: Combat AI +Feature #171: Implement NIF record NiFlipController +Feature #410: Window to restore enchanted item charge +Feature #647: Enchanted item glow +Feature #723: Invisibility/Chameleon magic effects +Feature #737: Resist Magicka magic effect +Feature #758: GetLOS +Feature #926: Editor: Info-Record tables +Feature #958: Material controllers +Feature #959: Terrain bump, specular, & parallax mapping +Feature #990: Request: unlock mouse when in any menu +Feature #1018: Do not allow view mode switching while performing an action +Feature #1027: Vertex morph animation (NiGeomMorpherController) +Feature #1031: Handle NiBillboardNode +Feature #1051: Implement NIF texture slot DarkTexture +Task #873: Unify OGRE initialisation + 0.27.0 Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp