1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-01 01:45:36 +00:00

Merge branch 'master' of https://github.com/zinnschlag/openmw into DragDrop

This commit is contained in:
Marek Kochanowicz 2014-02-19 11:23:53 +01:00
commit 569533eeae
109 changed files with 2786 additions and 1017 deletions

View file

@ -4,17 +4,17 @@ compiler:
branches:
only:
- master
- next
- /openmw-.*$/
before_install:
- pwd
- git submodule update --init --recursive
- git fetch --tags
- 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 +37,9 @@ notifications:
email:
on_success: change
on_failure: always
irc:
channels:
- "chat.freenode.net#openmw"
on_success: change
on_failure: always

View file

@ -4,10 +4,6 @@ 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
@ -20,7 +16,7 @@ include(OpenMWMacros)
include(GetGitRevisionDescription)
get_git_tag_revision(TAGHASH --tags --max-count=1)
get_git_tag_revision(TAGHASH --tags --max-count=1 "HEAD...")
get_git_head_revision(REFSPEC COMMITHASH)
git_describe(VERSION --tags ${TAGHASH})
@ -206,10 +202,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 +252,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 +275,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 +296,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 +653,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 +668,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 +710,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 +742,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)

View file

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

View file

@ -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, &params);
hiddenWindow->setActive(false);
#endif
mStartup.show();

View file

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

View file

@ -0,0 +1,103 @@
#include "scriptcheck.hpp"
#include <components/compiler/tokenloc.hpp>
#include <components/compiler/scanner.hpp>
#include <components/compiler/fileparser.hpp>
#include <components/compiler/exception.hpp>
#include <components/compiler/extensions0.hpp>
#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<std::string>& 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;
}

View file

@ -0,0 +1,41 @@
#ifndef CSM_TOOLS_SCRIPTCHECK_H
#define CSM_TOOLS_SCRIPTCHECK_H
#include <components/compiler/errorhandler.hpp>
#include <components/compiler/extensions.hpp>
#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<std::string> *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<std::string>& messages);
///< Messages resulting from this tage will be appended to \a messages.
};
}
#endif

View file

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

View file

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

View file

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

View file

@ -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<UniversalId::Type, RefIdDataContainerBase *>::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)));

View file

@ -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<RecordT>& newRecord = dynamic_cast<Record<RecordT>& >(record);
mContainer.push_back(newRecord);
}
template<typename RecordT>
int RefIdDataContainer<RecordT>::getSize() const
{
@ -108,12 +108,15 @@ namespace CSMWorld
}
template<typename RecordT>
void RefIdDataContainer<RecordT>::appendRecord (const std::string& id)
void RefIdDataContainer<RecordT>::appendRecord (const std::string& id, bool base)
{
Record<RecordT> 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;

View file

@ -5,23 +5,92 @@
#include <components/misc/stringops.hpp>
#include <components/compiler/quickfileparser.hpp>
#include <components/compiler/nullerrorhandler.hpp>
#include <components/compiler/scanner.hpp>
#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<char, bool> 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<std::string, Compiler::Locals>::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();
}

View file

@ -3,8 +3,10 @@
#include <string>
#include <vector>
#include <map>
#include <components/compiler/context.hpp>
#include <components/compiler/locals.hpp>
namespace CSMWorld
{
@ -15,6 +17,7 @@ namespace CSMWorld
const Data& mData;
mutable std::vector<std::string> mIds;
mutable bool mIdsUpdated;
mutable std::map<std::string, Compiler::Locals> 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<char, bool> 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.
};
}

View file

@ -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, &params);
mWindow->addViewport(mCamera)->setBackgroundColour(Ogre::ColourValue(0.3,0.3,0.3,1));

View file

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

View file

@ -7,7 +7,7 @@
#include <QMenu>
#include <QContextMenuEvent>
#include <QString>
#include <qt4/QtCore/qnamespace.h>
#include <QtCore/qnamespace.h>
#include "../../model/world/data.hpp"
#include "../../model/world/commands.hpp"

View file

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

View file

@ -74,6 +74,7 @@ namespace OMW
bool mSkipMenu;
bool mUseSound;
bool mCompileAll;
int mWarningsMode;
std::string mFocusName;
std::map<std::string,std::string> 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;
};

View file

@ -136,6 +136,13 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("script-run", bpo::value<std::string>()->default_value(""),
"select a file containing a list of console commands that is executed on startup")
("script-warn", bpo::value<int>()->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<bool>()->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<bool>());
engine.setStartupScript (variables["script-run"].as<std::string>());
engine.setActivationDistanceOverride (variables["activate-dist"].as<int>());
engine.setWarningsMode (variables["script-warn"].as<int>());
return true;
}

View file

@ -3,6 +3,14 @@
#include <string>
#include <libs/platform/stdint.h>
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;
};
}

View file

@ -766,8 +766,11 @@ namespace MWClass
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore->
readState (state2.mInventory);
CustomData& customData = dynamic_cast<CustomData&> (*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<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore->
writeState (state2.mInventory);
CustomData& customData = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData());
customData.mContainerStore->writeState (state2.mInventory);
customData.mCreatureStats.writeState (state2.mCreatureStats);
}
const ESM::GameSetting* Creature::fMinWalkSpeedCreature;

View file

@ -115,21 +115,11 @@ namespace MWClass
if (ref->mRef.mTeleport)
{
// teleport door
/// \todo remove this if clause once ActionTeleport can also support other actors
if (MWBase::Environment::get().getWorld()->getPlayerPtr()==actor)
{
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionTeleport (ref->mRef.mDestCell, ref->mRef.mDoorDest));
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionTeleport (ref->mRef.mDestCell, ref->mRef.mDoorDest));
action->setSound(openSound);
action->setSound(openSound);
return action;
}
else
{
// another NPC or a creature is using the door
return boost::shared_ptr<MWWorld::Action> (new MWWorld::FailedAction);
}
return action;
}
else
{

View file

@ -1278,8 +1278,11 @@ namespace MWClass
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore.
readState (state2.mInventory);
CustomData& customData = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData());
customData.mInventoryStore.readState (state2.mInventory);
customData.mNpcStats.readState (state2.mNpcStats);
static_cast<MWMechanics::CreatureStats&> (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<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore.
writeState (state2.mInventory);
CustomData& customData = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData());
customData.mInventoryStore.writeState (state2.mInventory);
customData.mNpcStats.writeState (state2.mNpcStats);
static_cast<const MWMechanics::CreatureStats&> (customData.mNpcStats).writeState (state2.mCreatureStats);
}
const ESM::GameSetting *Npc::fMinWalkSpeed;

View file

@ -8,6 +8,7 @@
#include <components/esm/loaddial.hpp>
#include <components/esm/loadinfo.hpp>
#include <components/esm/dialoguestate.hpp>
#include <components/compiler/exception.hpp>
#include <components/compiler/errorhandler.hpp>
@ -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<std::string, bool>::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<std::string>::const_iterator iter (state.mKnownTopics.begin());
iter!=state.mKnownTopics.end(); ++iter)
if (store.get<ESM::Dialogue>().search (*iter))
mKnownTopics.insert (std::make_pair (*iter, true));
}
}
std::vector<HyperTextToken> ParseHyperText(const std::string& text)
{

View file

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

View file

@ -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"
@ -647,7 +648,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();

View file

@ -1,8 +1,27 @@
#include "aiactivate.hpp"
#include <iostream>
#include "movement.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/action.hpp"
#include "steering.hpp"
namespace
{
float sgn(float a)
{
if(a > 0)
return 1.0;
return -1.0;
}
}
MWMechanics::AiActivate::AiActivate(const std::string &objectId)
: mObjectId(objectId)
: mObjectId(objectId)
{
}
MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const
@ -11,8 +30,81 @@ MWMechanics::AiActivate *MWMechanics::AiActivate::clone() const
}
bool MWMechanics::AiActivate::execute (const MWWorld::Ptr& actor,float duration)
{
std::cout << "AiActivate completed.\n";
return true;
MWBase::World *world = MWBase::Environment::get().getWorld();
ESM::Position pos = actor.getRefData().getPosition();
Movement &movement = actor.getClass().getMovementSettings(actor);
const ESM::Cell *cell = actor.getCell()->mCell;
MWWorld::Ptr player = world->getPlayerPtr();
if(cell->mData.mX != player.getCell()->mCell->mData.mX)
{
int sideX = sgn(cell->mData.mX - player.getCell()->mCell->mData.mX);
//check if actor is near the border of an inactive cell. If so, stop walking.
if(sideX * (pos.pos[0] - cell->mData.mX*ESM::Land::REAL_SIZE) >
sideX * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
{
movement.mPosition[1] = 0;
return false;
}
}
if(cell->mData.mY != player.getCell()->mCell->mData.mY)
{
int sideY = sgn(cell->mData.mY - player.getCell()->mCell->mData.mY);
//check if actor is near the border of an inactive cell. If so, stop walking.
if(sideY * (pos.pos[1] - cell->mData.mY*ESM::Land::REAL_SIZE) >
sideY * (ESM::Land::REAL_SIZE/2.0f - 200.0f))
{
movement.mPosition[1] = 0;
return false;
}
}
MWWorld::Ptr target = world->getPtr(mObjectId,false);
ESM::Position targetPos = target.getRefData().getPosition();
bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY;
if(!mPathFinder.isPathConstructed() || cellChange)
{
mCellX = cell->mData.mX;
mCellY = cell->mData.mY;
ESM::Pathgrid::Point dest;
dest.mX = targetPos.pos[0];
dest.mY = targetPos.pos[1];
dest.mZ = targetPos.pos[2];
ESM::Pathgrid::Point start;
start.mX = pos.pos[0];
start.mY = pos.pos[1];
start.mZ = pos.pos[2];
mPathFinder.buildPath(start, dest, actor.getCell(), true);
}
if((pos.pos[0]-targetPos.pos[0])*(pos.pos[0]-targetPos.pos[0])+
(pos.pos[1]-targetPos.pos[1])*(pos.pos[1]-targetPos.pos[1])+
(pos.pos[2]-targetPos.pos[2])*(pos.pos[2]-targetPos.pos[2]) < 200*200)
{
movement.mPosition[1] = 0;
MWWorld::Ptr target = world->getPtr(mObjectId,false);
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor);
return true;
}
if(mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
{
movement.mPosition[1] = 0;
MWWorld::Ptr target = world->getPtr(mObjectId,false);
MWWorld::Class::get(target).activate(target,actor).get()->execute(actor);
return true;
}
float zAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
zTurn(actor, Ogre::Degree(zAngle));
MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 1;
movement.mPosition[1] = 1;
return false;
}
int MWMechanics::AiActivate::getTypeId() const

View file

@ -4,6 +4,8 @@
#include "aipackage.hpp"
#include <string>
#include "pathfinding.hpp"
namespace MWMechanics
{
@ -18,6 +20,10 @@ namespace MWMechanics
private:
std::string mObjectId;
PathFinder mPathFinder;
int mCellX;
int mCellY;
};
}
#endif // GAME_MWMECHANICS_AIACTIVATE_H

View file

@ -21,8 +21,8 @@ namespace MWMechanics
{
AiTravel::AiTravel(float x, float y, float z)
: mX(x),mY(y),mZ(z),mPathFinder()
, cellX(std::numeric_limits<int>::max())
, cellY(std::numeric_limits<int>::max())
, mCellX(std::numeric_limits<int>::max())
, mCellY(std::numeric_limits<int>::max())
{
}
@ -62,11 +62,11 @@ namespace MWMechanics
}
}
bool cellChange = cell->mData.mX != cellX || cell->mData.mY != cellY;
bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY;
if(!mPathFinder.isPathConstructed() || cellChange)
{
cellX = cell->mData.mX;
cellY = cell->mData.mY;
mCellX = cell->mData.mX;
mCellY = cell->mData.mY;
ESM::Pathgrid::Point dest;
dest.mX = mX;

View file

@ -23,8 +23,8 @@ namespace MWMechanics
float mY;
float mZ;
int cellX;
int cellY;
int mCellX;
int mCellY;
PathFinder mPathFinder;
};

View file

@ -2,6 +2,8 @@
#include <algorithm>
#include <components/esm/creaturestats.hpp>
#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]);
}
}

View file

@ -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<int> mBoundItems;
// Same as above
std::map<int, std::string> mSummonedCreatures;
void writeState (ESM::CreatureStats& state) const;
void readState (const ESM::CreatureStats& state);
};
}

View file

@ -12,6 +12,7 @@
#include <components/esm/loadclas.hpp>
#include <components/esm/loadgmst.hpp>
#include <components/esm/loadfact.hpp>
#include <components/esm/npcstats.hpp>
#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<std::string, int>::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<std::string>::const_iterator iter (mExpelled.begin());
iter!=mExpelled.end(); ++iter)
state.mFactions[*iter].mExpelled = true;
for (std::map<std::string, int>::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<std::string, ESM::NpcStats::Faction>::const_iterator iter (state.mFactions.begin());
iter!=state.mFactions.end(); ++iter)
if (store.get<ESM::Faction>().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<std::string>::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;
}

View file

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

View file

@ -0,0 +1,29 @@
#include "stat.hpp"
void MWMechanics::AttributeValue::writeState (ESM::StatState<int>& state) const
{
state.mBase = mBase;
state.mMod = mModifier;
state.mDamage = mDamage;
}
void MWMechanics::AttributeValue::readState (const ESM::StatState<int>& state)
{
mBase = state.mBase;
mModifier = state.mMod;
mDamage = state.mDamage;
}
void MWMechanics::SkillValue::writeState (ESM::StatState<int>& state) const
{
AttributeValue::writeState (state);
state.mProgress = mProgress;
}
void MWMechanics::SkillValue::readState (const ESM::StatState<int>& state)
{
AttributeValue::readState (state);
mProgress = state.mProgress;
}

View file

@ -6,6 +6,8 @@
#include <limits>
#include <components/esm/statstate.hpp>
namespace MWMechanics
{
template<typename T>
@ -86,6 +88,18 @@ namespace MWMechanics
{
mModified = mBase + modifier;
}
void writeState (ESM::StatState<T>& state) const
{
state.mBase = mBase;
state.mMod = mModified;
}
void readState (const ESM::StatState<T>& state)
{
mBase = state.mBase;
mModified = state.mMod;
}
};
template<typename T>
@ -190,6 +204,18 @@ namespace MWMechanics
mStatic.setModifier (modifier);
setCurrent (getCurrent()+diff);
}
void writeState (ESM::StatState<T>& state) const
{
mStatic.writeState (state);
state.mCurrent = mCurrent;
}
void readState (const ESM::StatState<T>& state)
{
mStatic.readState (state);
mCurrent = state.mCurrent;
}
};
template<typename T>
@ -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<int>& state) const;
void readState (const ESM::StatState<int>& 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<int>& state) const;
void readState (const ESM::StatState<int>& state);
};
inline bool operator== (const AttributeValue& left, const AttributeValue& right)

View file

@ -697,7 +697,8 @@ void NpcAnimation::showWeapons(bool showWeapon)
addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1,
mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor);
if (weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
if (weapon->getTypeName() == typeid(ESM::Weapon).name() &&
weapon->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow)
{
MWWorld::ContainerStoreIterator ammo = inv.getSlot(MWWorld::InventoryStore::Slot_Ammunition);
if (ammo != inv.end() && ammo->get<ESM::Weapon>()->mBase->mData.mType == ESM::Weapon::Bolt)

View file

@ -1,5 +1,14 @@
#include "terrainstorage.hpp"
#include <OgreVector2.h>
#include <OgreTextureManager.h>
#include <OgreStringConverter.h>
#include <OgreRenderSystem.h>
#include <OgreResourceGroupManager.h>
#include <OgreRoot.h>
#include <boost/algorithm/string.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwworld/esmstore.hpp"
@ -53,4 +62,509 @@ namespace MWRender
return esmStore.get<ESM::LandTexture>().find(index, plugin);
}
bool TerrainStorage::getMinMaxHeights(float size, const Ogre::Vector2 &center, float &min, float &max)
{
assert (size <= 1 && "TerrainStorage::getMinMaxHeights, chunk size should be <= 1 cell");
/// \todo investigate if min/max heights should be stored at load time in ESM::Land instead
Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f);
assert(origin.x == (int) origin.x);
assert(origin.y == (int) origin.y);
int cellX = origin.x;
int cellY = origin.y;
const ESM::Land* land = getLand(cellX, cellY);
if (!land)
return false;
min = std::numeric_limits<float>().max();
max = -std::numeric_limits<float>().max();
for (int row=0; row<ESM::Land::LAND_SIZE; ++row)
{
for (int col=0; col<ESM::Land::LAND_SIZE; ++col)
{
float h = land->mLandData->mHeights[col*ESM::Land::LAND_SIZE+row];
if (h > max)
max = h;
if (h < min)
min = h;
}
}
return true;
}
void TerrainStorage::fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row)
{
while (col >= ESM::Land::LAND_SIZE-1)
{
++cellY;
col -= ESM::Land::LAND_SIZE-1;
}
while (row >= ESM::Land::LAND_SIZE-1)
{
++cellX;
row -= ESM::Land::LAND_SIZE-1;
}
while (col < 0)
{
--cellY;
col += ESM::Land::LAND_SIZE-1;
}
while (row < 0)
{
--cellX;
row += ESM::Land::LAND_SIZE-1;
}
ESM::Land* land = getLand(cellX, cellY);
if (land && land->mHasData)
{
normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2];
normal.normalise();
}
else
normal = Ogre::Vector3(0,0,1);
}
void TerrainStorage::averageNormal(Ogre::Vector3 &normal, int cellX, int cellY, int col, int row)
{
Ogre::Vector3 n1,n2,n3,n4;
fixNormal(n1, cellX, cellY, col+1, row);
fixNormal(n2, cellX, cellY, col-1, row);
fixNormal(n3, cellX, cellY, col, row+1);
fixNormal(n4, cellX, cellY, col, row-1);
normal = (n1+n2+n3+n4);
normal.normalise();
}
void TerrainStorage::fixColour (Ogre::ColourValue& color, int cellX, int cellY, int col, int row)
{
if (col == ESM::Land::LAND_SIZE-1)
{
++cellY;
col = 0;
}
if (row == ESM::Land::LAND_SIZE-1)
{
++cellX;
row = 0;
}
ESM::Land* land = getLand(cellX, cellY);
if (land && land->mLandData->mUsingColours)
{
color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f;
}
else
{
color.r = 1;
color.g = 1;
color.b = 1;
}
}
void TerrainStorage::fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center,
Ogre::HardwareVertexBufferSharedPtr vertexBuffer,
Ogre::HardwareVertexBufferSharedPtr normalBuffer,
Ogre::HardwareVertexBufferSharedPtr colourBuffer)
{
// LOD level n means every 2^n-th vertex is kept
size_t increment = 1 << lodLevel;
Ogre::Vector2 origin = center - Ogre::Vector2(size/2.f, size/2.f);
assert(origin.x == (int) origin.x);
assert(origin.y == (int) origin.y);
int startX = origin.x;
int startY = origin.y;
size_t numVerts = size*(ESM::Land::LAND_SIZE-1)/increment + 1;
std::vector<uint8_t> colors;
colors.resize(numVerts*numVerts*4);
std::vector<float> positions;
positions.resize(numVerts*numVerts*3);
std::vector<float> normals;
normals.resize(numVerts*numVerts*3);
Ogre::Vector3 normal;
Ogre::ColourValue color;
float vertY;
float vertX;
float vertY_ = 0; // of current cell corner
for (int cellY = startY; cellY < startY + std::ceil(size); ++cellY)
{
float vertX_ = 0; // of current cell corner
for (int cellX = startX; cellX < startX + std::ceil(size); ++cellX)
{
ESM::Land* land = getLand(cellX, cellY);
if (land && !land->mHasData)
land = NULL;
bool hasColors = land && land->mLandData->mUsingColours;
int rowStart = 0;
int colStart = 0;
// Skip the first row / column unless we're at a chunk edge,
// since this row / column is already contained in a previous cell
if (colStart == 0 && vertY_ != 0)
colStart += increment;
if (rowStart == 0 && vertX_ != 0)
rowStart += increment;
vertY = vertY_;
for (int col=colStart; col<ESM::Land::LAND_SIZE; col += increment)
{
vertX = vertX_;
for (int row=rowStart; row<ESM::Land::LAND_SIZE; row += increment)
{
positions[vertX*numVerts*3 + vertY*3] = ((vertX/float(numVerts-1)-0.5) * size * 8192);
positions[vertX*numVerts*3 + vertY*3 + 1] = ((vertY/float(numVerts-1)-0.5) * size * 8192);
if (land)
positions[vertX*numVerts*3 + vertY*3 + 2] = land->mLandData->mHeights[col*ESM::Land::LAND_SIZE+row];
else
positions[vertX*numVerts*3 + vertY*3 + 2] = -2048;
if (land)
{
normal.x = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3];
normal.y = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+1];
normal.z = land->mLandData->mNormals[col*ESM::Land::LAND_SIZE*3+row*3+2];
normal.normalise();
}
else
normal = Ogre::Vector3(0,0,1);
// Normals apparently don't connect seamlessly between cells
if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)
fixNormal(normal, cellX, cellY, col, row);
// some corner normals appear to be complete garbage (z < 0)
if ((row == 0 || row == ESM::Land::LAND_SIZE-1) && (col == 0 || col == ESM::Land::LAND_SIZE-1))
averageNormal(normal, cellX, cellY, col, row);
assert(normal.z > 0);
normals[vertX*numVerts*3 + vertY*3] = normal.x;
normals[vertX*numVerts*3 + vertY*3 + 1] = normal.y;
normals[vertX*numVerts*3 + vertY*3 + 2] = normal.z;
if (hasColors)
{
color.r = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3] / 255.f;
color.g = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+1] / 255.f;
color.b = land->mLandData->mColours[col*ESM::Land::LAND_SIZE*3+row*3+2] / 255.f;
}
else
{
color.r = 1;
color.g = 1;
color.b = 1;
}
// Unlike normals, colors mostly connect seamlessly between cells, but not always...
if (col == ESM::Land::LAND_SIZE-1 || row == ESM::Land::LAND_SIZE-1)
fixColour(color, cellX, cellY, col, row);
color.a = 1;
Ogre::uint32 rsColor;
Ogre::Root::getSingleton().getRenderSystem()->convertColourValue(color, &rsColor);
memcpy(&colors[vertX*numVerts*4 + vertY*4], &rsColor, sizeof(Ogre::uint32));
++vertX;
}
++vertY;
}
vertX_ = vertX;
}
vertY_ = vertY;
assert(vertX_ == numVerts); // Ensure we covered whole area
}
assert(vertY_ == numVerts); // Ensure we covered whole area
vertexBuffer->writeData(0, vertexBuffer->getSizeInBytes(), &positions[0], true);
normalBuffer->writeData(0, normalBuffer->getSizeInBytes(), &normals[0], true);
colourBuffer->writeData(0, colourBuffer->getSizeInBytes(), &colors[0], true);
}
TerrainStorage::UniqueTextureId TerrainStorage::getVtexIndexAt(int cellX, int cellY,
int x, int y)
{
// For the first/last row/column, we need to get the texture from the neighbour cell
// to get consistent blending at the borders
--x;
if (x < 0)
{
--cellX;
x += ESM::Land::LAND_TEXTURE_SIZE;
}
if (y >= ESM::Land::LAND_TEXTURE_SIZE) // Y appears to be wrapped from the other side because why the hell not?
{
++cellY;
y -= ESM::Land::LAND_TEXTURE_SIZE;
}
assert(x<ESM::Land::LAND_TEXTURE_SIZE);
assert(y<ESM::Land::LAND_TEXTURE_SIZE);
ESM::Land* land = getLand(cellX, cellY);
if (land)
{
if (!land->isDataLoaded(ESM::Land::DATA_VTEX))
land->loadData(ESM::Land::DATA_VTEX);
int tex = land->mLandData->mTextures[y * ESM::Land::LAND_TEXTURE_SIZE + x];
if (tex == 0)
return std::make_pair(0,0); // vtex 0 is always the base texture, regardless of plugin
return std::make_pair(tex, land->mPlugin);
}
else
return std::make_pair(0,0);
}
std::string TerrainStorage::getTextureName(UniqueTextureId id)
{
if (id.first == 0)
return "_land_default.dds"; // Not sure if the default texture floatly is hardcoded?
// NB: All vtex ids are +1 compared to the ltex ids
const ESM::LandTexture* ltex = getLandTexture(id.first-1, id.second);
std::string texture = ltex->mTexture;
//TODO this is needed due to MWs messed up texture handling
texture = texture.substr(0, texture.rfind(".")) + ".dds";
return texture;
}
void TerrainStorage::getBlendmaps(float chunkSize, const Ogre::Vector2 &chunkCenter,
bool pack, std::vector<Ogre::TexturePtr> &blendmaps, std::vector<Terrain::LayerInfo> &layerList)
{
// TODO - blending isn't completely right yet; the blending radius appears to be
// different at a cell transition (2 vertices, not 4), so we may need to create a larger blendmap
// and interpolate the rest of the cell by hand? :/
Ogre::Vector2 origin = chunkCenter - Ogre::Vector2(chunkSize/2.f, chunkSize/2.f);
int cellX = origin.x;
int cellY = origin.y;
// Save the used texture indices so we know the total number of textures
// and number of required blend maps
std::set<UniqueTextureId> textureIndices;
// Due to the way the blending works, the base layer will always shine through in between
// blend transitions (eg halfway between two texels, both blend values will be 0.5, so 25% of base layer visible).
// To get a consistent look, we need to make sure to use the same base layer in all cells.
// So we're always adding _land_default.dds as the base layer here, even if it's not referenced in this cell.
textureIndices.insert(std::make_pair(0,0));
for (int y=0; y<ESM::Land::LAND_TEXTURE_SIZE+1; ++y)
for (int x=0; x<ESM::Land::LAND_TEXTURE_SIZE+1; ++x)
{
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x, y);
textureIndices.insert(id);
}
// Makes sure the indices are sorted, or rather,
// retrieved as sorted. This is important to keep the splatting order
// consistent across cells.
std::map<UniqueTextureId, int> textureIndicesMap;
for (std::set<UniqueTextureId>::iterator it = textureIndices.begin(); it != textureIndices.end(); ++it)
{
int size = textureIndicesMap.size();
textureIndicesMap[*it] = size;
layerList.push_back(getLayerInfo(getTextureName(*it)));
}
int numTextures = textureIndices.size();
// numTextures-1 since the base layer doesn't need blending
int numBlendmaps = pack ? std::ceil((numTextures-1) / 4.f) : (numTextures-1);
int channels = pack ? 4 : 1;
// Second iteration - create and fill in the blend maps
const int blendmapSize = ESM::Land::LAND_TEXTURE_SIZE+1;
std::vector<Ogre::uchar> data;
data.resize(blendmapSize * blendmapSize * channels, 0);
for (int i=0; i<numBlendmaps; ++i)
{
Ogre::PixelFormat format = pack ? Ogre::PF_A8B8G8R8 : Ogre::PF_A8;
static int count=0;
Ogre::TexturePtr map = Ogre::TextureManager::getSingleton().createManual("terrain/blend/"
+ Ogre::StringConverter::toString(count++), Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D, blendmapSize, blendmapSize, 0, format);
for (int y=0; y<blendmapSize; ++y)
{
for (int x=0; x<blendmapSize; ++x)
{
UniqueTextureId id = getVtexIndexAt(cellX, cellY, x, y);
int layerIndex = textureIndicesMap.find(id)->second;
int blendIndex = (pack ? std::floor((layerIndex-1)/4.f) : layerIndex-1);
int channel = pack ? std::max(0, (layerIndex-1) % 4) : 0;
if (blendIndex == i)
data[y*blendmapSize*channels + x*channels + channel] = 255;
else
data[y*blendmapSize*channels + x*channels + channel] = 0;
}
}
// All done, upload to GPU
Ogre::DataStreamPtr stream(new Ogre::MemoryDataStream(&data[0], data.size()));
map->loadRawData(stream, blendmapSize, blendmapSize, format);
blendmaps.push_back(map);
}
}
float TerrainStorage::getHeightAt(const Ogre::Vector3 &worldPos)
{
int cellX = std::floor(worldPos.x / 8192.f);
int cellY = std::floor(worldPos.y / 8192.f);
ESM::Land* land = getLand(cellX, cellY);
if (!land)
return -2048;
// Mostly lifted from Ogre::Terrain::getHeightAtTerrainPosition
// Normalized position in the cell
float nX = (worldPos.x - (cellX * 8192))/8192.f;
float nY = (worldPos.y - (cellY * 8192))/8192.f;
// get left / bottom points (rounded down)
float factor = ESM::Land::LAND_SIZE - 1.0f;
float invFactor = 1.0f / factor;
int startX = static_cast<int>(nX * factor);
int startY = static_cast<int>(nY * factor);
int endX = startX + 1;
int endY = startY + 1;
assert(endX < ESM::Land::LAND_SIZE);
assert(endY < ESM::Land::LAND_SIZE);
// now get points in terrain space (effectively rounding them to boundaries)
float startXTS = startX * invFactor;
float startYTS = startY * invFactor;
float endXTS = endX * invFactor;
float endYTS = endY * invFactor;
// get parametric from start coord to next point
float xParam = (nX - startXTS) * factor;
float yParam = (nY - startYTS) * factor;
/* For even / odd tri strip rows, triangles are this shape:
even odd
3---2 3---2
| / | | \ |
0---1 0---1
*/
// Build all 4 positions in normalized cell space, using point-sampled height
Ogre::Vector3 v0 (startXTS, startYTS, getVertexHeight(land, startX, startY) / 8192.f);
Ogre::Vector3 v1 (endXTS, startYTS, getVertexHeight(land, endX, startY) / 8192.f);
Ogre::Vector3 v2 (endXTS, endYTS, getVertexHeight(land, endX, endY) / 8192.f);
Ogre::Vector3 v3 (startXTS, endYTS, getVertexHeight(land, startX, endY) / 8192.f);
// define this plane in terrain space
Ogre::Plane plane;
// (At the moment, all rows have the same triangle alignment)
if (true)
{
// odd row
bool secondTri = ((1.0 - yParam) > xParam);
if (secondTri)
plane.redefine(v0, v1, v3);
else
plane.redefine(v1, v2, v3);
}
else
{
// even row
bool secondTri = (yParam > xParam);
if (secondTri)
plane.redefine(v0, v2, v3);
else
plane.redefine(v0, v1, v2);
}
// Solve plane equation for z
return (-plane.normal.x * nX
-plane.normal.y * nY
- plane.d) / plane.normal.z * 8192;
}
float TerrainStorage::getVertexHeight(const ESM::Land *land, int x, int y)
{
assert(x < ESM::Land::LAND_SIZE);
assert(y < ESM::Land::LAND_SIZE);
return land->mLandData->mHeights[y * ESM::Land::LAND_SIZE + x];
}
Terrain::LayerInfo TerrainStorage::getLayerInfo(const std::string& texture)
{
// Already have this cached?
if (mLayerInfoMap.find(texture) != mLayerInfoMap.end())
return mLayerInfoMap[texture];
Terrain::LayerInfo info;
info.mParallax = false;
info.mSpecular = false;
info.mDiffuseMap = "textures\\" + texture;
std::string texture_ = texture;
boost::replace_last(texture_, ".", "_nh.");
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_))
{
info.mNormalMap = "textures\\" + texture_;
info.mParallax = true;
}
else
{
texture_ = texture;
boost::replace_last(texture_, ".", "_n.");
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_))
info.mNormalMap = "textures\\" + texture_;
}
texture_ = texture;
boost::replace_last(texture_, ".", "_diffusespec.");
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("textures\\" + texture_))
{
info.mDiffuseMap = "textures\\" + texture_;
info.mSpecular = true;
}
mLayerInfoMap[texture] = info;
return info;
}
Terrain::LayerInfo TerrainStorage::getDefaultLayer()
{
Terrain::LayerInfo info;
info.mDiffuseMap = "textures\\_land_default.dds";
info.mParallax = false;
info.mSpecular = false;
return info;
}
float TerrainStorage::getCellWorldSize()
{
return ESM::Land::REAL_SIZE;
}
int TerrainStorage::getCellVertices()
{
return ESM::Land::LAND_SIZE;
}
}

View file

@ -12,8 +12,75 @@ namespace MWRender
virtual ESM::Land* getLand (int cellX, int cellY);
virtual const ESM::LandTexture* getLandTexture(int index, short plugin);
public:
/// Get bounds of the whole terrain in cell units
virtual Ogre::AxisAlignedBox getBounds();
///< Get bounds in cell units
/// 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.
/// Larger chunks can simply merge AABB of children.
/// @param size size of the chunk in cell units
/// @param center center of the chunk in cell units
/// @param min min height will be stored here
/// @param max max height will be stored here
/// @return true if there was data available for this terrain chunk
virtual bool getMinMaxHeights (float size, const Ogre::Vector2& center, float& min, float& max);
/// Fill vertex buffers for a terrain chunk.
/// @param lodLevel LOD level, 0 = most detailed
/// @param size size of the terrain chunk in cell units
/// @param center center of the chunk in cell units
/// @param vertexBuffer buffer to write vertices
/// @param normalBuffer buffer to write vertex normals
/// @param colourBuffer buffer to write vertex colours
virtual void fillVertexBuffers (int lodLevel, float size, const Ogre::Vector2& center,
Ogre::HardwareVertexBufferSharedPtr vertexBuffer,
Ogre::HardwareVertexBufferSharedPtr normalBuffer,
Ogre::HardwareVertexBufferSharedPtr colourBuffer);
/// Create textures holding layer blend values for a terrain chunk.
/// @note The terrain chunk shouldn't be larger than one cell since otherwise we might
/// have to do a ridiculous amount of different layers. For larger chunks, composite maps should be used.
/// @param chunkSize size of the terrain chunk in cell units
/// @param chunkCenter center of the chunk in cell units
/// @param pack Whether to pack blend values for up to 4 layers into one texture (one in each channel) -
/// otherwise, each texture contains blend values for one layer only. Shader-based rendering
/// can utilize packing, FFP can't.
/// @param blendmaps created blendmaps will be written here
/// @param layerList names of the layer textures used will be written here
virtual void getBlendmaps (float chunkSize, const Ogre::Vector2& chunkCenter, bool pack,
std::vector<Ogre::TexturePtr>& blendmaps,
std::vector<Terrain::LayerInfo>& layerList);
virtual float getHeightAt (const Ogre::Vector3& worldPos);
virtual Terrain::LayerInfo getDefaultLayer();
/// Get the transformation factor for mapping cell units to world units.
virtual float getCellWorldSize();
/// Get the number of vertices on one side for each cell. Should be (power of two)+1
virtual int getCellVertices();
private:
void fixNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row);
void fixColour (Ogre::ColourValue& colour, int cellX, int cellY, int col, int row);
void averageNormal (Ogre::Vector3& normal, int cellX, int cellY, int col, int row);
float getVertexHeight (const ESM::Land* land, int x, int y);
// Since plugins can define new texture palettes, we need to know the plugin index too
// in order to retrieve the correct texture name.
// pair <texture id, plugin id>
typedef std::pair<short, short> UniqueTextureId;
UniqueTextureId getVtexIndexAt(int cellX, int cellY,
int x, int y);
std::string getTextureName (UniqueTextureId id);
std::map<std::string, Terrain::LayerInfo> mLayerInfoMap;
Terrain::LayerInfo getLayerInfo(const std::string& texture);
};
}

View file

@ -3,6 +3,8 @@
#include "../mwworld/esmstore.hpp"
#include <components/esm/loaddial.hpp>
#include <components/compiler/locals.hpp>
#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<char, bool> 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<ESM::Script>().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<ESM::Static>().search (name) ||
store.get<ESM::Weapon>().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<ESM::Dialogue>().search (name);
return topic && topic->mType==ESM::Dialogue::Journal;
}
}

View file

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

View file

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

View file

@ -148,4 +148,25 @@ namespace MWScript
return false;
}
Locals& GlobalScripts::getLocals (const std::string& name)
{
std::string name2 = Misc::StringUtils::lowerCase (name);
std::map<std::string, std::pair<bool, Locals> >::iterator iter =
mScripts.find (name2);
if (iter==mScripts.end())
{
if (const ESM::Script *script = mStore.get<ESM::Script>().find (name))
{
Locals locals;
locals.configure (*script);
iter = mScripts.insert (std::make_pair (name, std::make_pair (false, locals))).first;
}
}
return iter->second.second;
}
}

View file

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

View file

@ -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<ESM::Script>().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<ESM::Script>().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<ESM::Script>().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<ESM::Script>().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<ESM::Script>().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<ESM::Script>().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<ESM::Script>().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<ESM::Script>().find (scriptId));
ptr.getRefData().getLocals().mFloats[index] = value;
locals.mFloats[index] = value;
}
MWWorld::Ptr InterpreterContext::getReference(bool required)

View file

@ -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<std::string> 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)

View file

@ -365,17 +365,21 @@ namespace MWScript
};
template<class R>
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<arg0; ++i)
runtime.pop();
MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr);
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
{
@ -818,6 +822,28 @@ namespace MWScript
}
};
class OpGetPcInJail : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime &runtime)
{
/// \todo implement jail check
runtime.push (0);
}
};
class OpGetPcTraveling : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime &runtime)
{
/// \todo implement traveling check
runtime.push (0);
}
};
void installOpcodes (Interpreter::Interpreter& interpreter)
{
interpreter.installSegment5 (Compiler::Misc::opcodeXBox, new OpXBox);
@ -850,8 +876,8 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Misc::opcodeGetEffectExplicit, new OpGetEffect<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGem, new OpAddSoulGem<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeAddSoulGemExplicit, new OpAddSoulGem<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeRemoveSoulGem, new OpRemoveSoulGem<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem<ExplicitRef>);
interpreter.installSegment3 (Compiler::Misc::opcodeRemoveSoulGem, new OpRemoveSoulGem<ImplicitRef>);
interpreter.installSegment3 (Compiler::Misc::opcodeRemoveSoulGemExplicit, new OpRemoveSoulGem<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeDrop, new OpDrop<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeDropExplicit, new OpDrop<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeDropSoulGem, new OpDropSoulGem<ImplicitRef>);
@ -888,6 +914,8 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Misc::opcodeCastExplicit, new OpCast<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpell, new OpExplodeSpell<ImplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeExplodeSpellExplicit, new OpExplodeSpell<ExplicitRef>);
interpreter.installSegment5 (Compiler::Misc::opcodeGetPcInJail, new OpGetPcInJail);
interpreter.installSegment5 (Compiler::Misc::opcodeGetPcTraveling, new OpGetPcTraveling);
}
}
}

View file

@ -7,22 +7,28 @@
#include <exception>
#include <components/esm/loadscpt.hpp>
#include "../mwworld/esmstore.hpp"
#include <components/misc/stringops.hpp>
#include <components/compiler/scanner.hpp>
#include <components/compiler/context.hpp>
#include <components/compiler/exception.hpp>
#include <components/compiler/quickfileparser.hpp>
#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<std::string, Compiler::Locals>::iterator iter = mOtherLocals.find (name);
std::map<std::string, Compiler::Locals>::iterator iter = mOtherLocals.find (name2);
if (iter!=mOtherLocals.end())
return iter->second;
}
Compiler::Locals locals;
if (const ESM::Script *script = mStore.get<ESM::Script>().find (name))
if (const ESM::Script *script = mStore.get<ESM::Script>().find (name2))
{
int index = 0;
Compiler::Locals locals;
for (int i=0; i<script->mData.mNumShorts; ++i)
locals.declare ('s', script->mVarNames[index++]);
for (int i=0; i<script->mData.mNumLongs; ++i)
locals.declare ('l', script->mVarNames[index++]);
for (int i=0; i<script->mData.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<std::string, Compiler::Locals>::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; i<size; ++i)
if (script->mVarNames.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);

View file

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

View file

@ -316,15 +316,25 @@ void OpenAL_SoundStream::play()
throwALerror();
mSamplesQueued = 0;
for(ALuint i = 0;i < sNumBuffers;i++)
alBufferData(mBuffers[i], mFormat, this, 0, mSampleRate);
throwALerror();
std::vector<char> data(mBufferSize);
alSourceQueueBuffers(mSource, sNumBuffers, mBuffers);
bool finished = false;
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);
}
}
mIsFinished = finished;
alSourcePlay(mSource);
throwALerror();
mIsFinished = false;
mOutput.mStreamThread->add(this);
}

View file

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

View file

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

View file

@ -122,7 +122,6 @@ function(git_describe _var)
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
@ -147,7 +146,6 @@ function(get_git_tag_revision _var)
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")

View file

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

View file

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

View file

@ -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<int> (mCodeBlock.size()-expr.size()));
Generator::jump (loop, -static_cast<int> (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<int> (mCodeBlock.size()-expr.size()-skip.size()));
Generator::jump (loop2, -static_cast<int> (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);

View file

@ -26,7 +26,8 @@ namespace Compiler
IfElseEndState, IfElseBodyState,
IfEndifState,
WhileEndState, WhileBodyState,
WhileEndwhileState
WhileEndwhileState,
IfElseJunkState
};
typedef std::vector<Interpreter::Type_Code> 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<Interpreter::Type_Code>& code) const;

View file

@ -0,0 +1,83 @@
#include "declarationparser.hpp"
#include <components/misc/stringops.hpp>
#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;
}

View file

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

View file

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

View file

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

View file

@ -7,6 +7,8 @@
#include <stack>
#include <iterator>
#include <components/misc/stringops.hpp>
#include "generator.hpp"
#include "scanner.hpp"
#include "errorhandler.hpp"
@ -14,7 +16,6 @@
#include "stringparser.hpp"
#include "extensions.hpp"
#include "context.hpp"
#include <components/misc/stringops.hpp>
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<char, bool> 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<Interpreter::Type_Code> tmp;
stringParser.append (tmp);
if (invert)
{
std::vector<Interpreter::Type_Code> 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
{

View file

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

View file

@ -22,7 +22,7 @@ namespace Compiler
}
bool Extensions::isFunction (int keyword, char& returnType, std::string& argumentType,
bool explicitReference) const
bool& explicitReference) const
{
std::map<int, Function>::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<int, Instruction>::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;

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,8 @@
#include "lineparser.hpp"
#include <components/misc/stringops.hpp>
#include "scanner.hpp"
#include "context.hpp"
#include "errorhandler.hpp"
@ -8,7 +10,7 @@
#include "locals.hpp"
#include "generator.hpp"
#include "extensions.hpp"
#include <components/misc/stringops.hpp>
#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<Interpreter::Type_Code>& 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<char, bool> 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<char, bool> 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<Interpreter::Type_Code> 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)

View file

@ -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<Interpreter::Type_Code>& code,
bool allowExpression = false);
///< \param allowExpression Allow lines consisting of a naked expression

View file

@ -7,6 +7,8 @@
#include <ostream>
#include <iterator>
#include <components/misc/stringops.hpp>
namespace Compiler
{
const std::vector<std::string>& 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()

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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=='.')
{

View file

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

View file

@ -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<Interpreter::Type_Code>& 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.
};

View file

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

View file

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

View file

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

View file

@ -10,22 +10,22 @@
namespace Compiler
{
class Literals;
class StringParser : public Parser
{
enum State
{
StartState, CommaState
};
Literals& mLiterals;
State mState;
std::vector<Interpreter::Type_Code> 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<Interpreter::Type_Code>& 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).
};
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,27 @@
#ifndef OPENMW_ESM_CREATURESTATS_H
#define OPENMW_ESM_CREATURESTATS_H
#include <string>
#include <vector>
#include <map>
#include "statstate.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
// format 0, saved games only
struct CreatureStats
{
StatState<int> mAttributes[8];
StatState<float> mDynamic[3];
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif

View file

@ -91,6 +91,7 @@ enum RecNameInts
REC_PLAY = 0x59414c50,
REC_CSTA = 0x41545343,
REC_GMAP = 0x50414d47,
REC_DIAS = 0x53414944,
// format 1
REC_FILT = 0x544C4946

View file

@ -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<std::string>::const_iterator iter (mKnownTopics.begin());
iter!=mKnownTopics.end(); ++iter)
{
esm.writeHNString ("TOPI", *iter);
}
}

View file

@ -0,0 +1,23 @@
#ifndef OPENMW_ESM_DIALOGUESTATE_H
#define OPENMW_ESM_DIALOGUESTATE_H
#include <string>
#include <vector>
namespace ESM
{
class ESMReader;
class ESMWriter;
// format 0, saved games only
struct DialogueState
{
std::vector<std::string> mKnownTopics;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif

View file

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

View file

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

133
components/esm/npcstats.cpp Normal file
View file

@ -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<std::string, Faction>::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<std::string>::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);
}

View file

@ -0,0 +1,54 @@
#ifndef OPENMW_ESM_NPCSTATS_H
#define OPENMW_ESM_NPCSTATS_H
#include <string>
#include <vector>
#include <map>
#include "statstate.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
// format 0, saved games only
struct NpcStats
{
struct Skill
{
StatState<int> mRegular;
StatState<int> mWerewolf;
};
struct Faction
{
bool mExpelled;
int mRank;
int mReputation;
Faction();
};
std::map<std::string, Faction> mFactions;
int mDisposition;
Skill mSkills[27];
int mBounty;
int mReputation;
int mWerewolfKills;
int mProfit;
float mAttackStrength;
int mLevelProgress;
int mSkillIncrease[8];
std::vector<std::string> mUsedIds;
float mTimeToStartDrowning;
float mLastDrowningHit;
float mLevelHealthBonus;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif

View file

@ -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<typename T>
struct StatState
{
T mBase;
T mMod;
T mCurrent;
T mDamage;
float mProgress;
StatState();
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
template<typename T>
StatState<T>::StatState() : mBase (0), mMod (0), mCurrent (0), mDamage (0), mProgress (0) {}
template<typename T>
void StatState<T>::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<typename T>
void StatState<T>::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

View file

@ -50,33 +50,33 @@ namespace Interpreter
virtual void setGlobalFloat (const std::string& name, float value) = 0;
virtual std::vector<std::string> 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;
};
}

View file

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

View file

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

View file

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

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